Habrastatistics: guneko gehien eta gutxien bisitatutako atalak aztertzea

Kaixo Habr.

Π’ aurreko zatia Habr-en trafikoa parametro nagusien arabera aztertu zen: artikulu kopurua, haien ikuspuntuak eta balorazioak. Hala ere, guneko atalen ospearen arazoa aztertu gabe geratu zen. Interesgarri bihurtu zen hau xehetasun gehiago aztertzea eta gune ezagunenak eta ezezagunak aurkitzea. Azkenik, geektimes efektua zehatzago aztertuko dut, sailkapen berrietan oinarritutako artikulu onenen aukeraketa berri batekin amaitzeko.

Habrastatistics: guneko gehien eta gutxien bisitatutako atalak aztertzea

Gertatutakoan interesa dutenentzat, jarraipena mozketan dago.

Gogorarazten dizut berriro ere estatistikak eta balorazioak ez direla ofizialak, ez daukadala inongo informaziorik. Ez dago ziurtatzen nonbait akatsik edo zerbait galdu ez dudanik. Baina, hala ere, interesgarria iruditu zait. Kodearekin hasiko gara lehenik; hau interesatzen ez zaienek lehen atalak salta ditzakete.

Datu bilketa

Analizatzailearen lehen bertsioan, ikustaldi, iruzkin eta artikuluen balorazioak baino ez ziren kontuan hartu. Hau dagoeneko ona da, baina ez dizu aukera ematen kontsulta konplexuagoak egiteko. Gunearen atal tematikoak aztertzeko garaia da; honek ikerketa nahiko interesgarriak egiteko aukera emango dizu, adibidez, ikusi nola aldatu den "C++" atalaren ospea hainbat urtetan zehar.

Artikuluen analizatzailea hobetu da, orain artikuluari dagozkion hubak itzultzen ditu, baita egilearen ezizena eta bere balorazioa ere (hemen ere gauza interesgarri asko egin daitezke, baina hori geroago etorriko da). Datuak honelako itxura duen csv fitxategi batean gordetzen dira:

2018-12-18T12:43Z,https://habr.com/ru/post/433550/,"ΠœΠ΅ΡΡΠ΅Π½Π΄ΠΆΠ΅Ρ€ Slack β€” ΠΏΡ€ΠΈΡ‡ΠΈΠ½Ρ‹ Π²Ρ‹Π±ΠΎΡ€Π°, косяки ΠΏΡ€ΠΈ Π²Π½Π΅Π΄Ρ€Π΅Π½ΠΈΠΈ ΠΈ особСнности сСрвиса, ΠΎΠ±Π»Π΅Π³Ρ‡Π°ΡŽΡ‰ΠΈΠ΅ Тизнь",votes:7,votesplus:8,votesmin:1,bookmarks:32,
views:8300,comments:10,user:ReDisque,karma:5,subscribers:2,hubs:productpm+soft
...

Gunearen ardatz tematiko nagusien zerrenda jasoko dugu.

def get_as_str(link: str) -> Str:
    try:
        r = requests.get(link)
        return Str(r.text)
    except Exception as e:
        return Str("")

def get_hubs():
    hubs = []
    for p in range(1, 12):
        page_html = get_as_str("https://habr.com/ru/hubs/page%d/" % p)
        # page_html = get_as_str("https://habr.com/ru/hubs/geektimes/page%d/" % p)  # Geektimes
        # page_html = get_as_str("https://habr.com/ru/hubs/develop/page%d/" % p)  # Develop
        # page_html = get_as_str("https://habr.com/ru/hubs/admin/page%d" % p)  # Admin
        for hub in page_html.split("media-obj media-obj_hub"):
            info = Str(hub).find_between('"https://habr.com/ru/hub', 'list-snippet__tags') 
            if "*</span>" in info:
                hub_name = info.find_between('/', '/"')
                if len(hub_name) > 0 and len(hub_name) < 32:
                    hubs.append(hub_name)
    print(hubs)

find_between funtzioak eta Str klaseak bi etiketen artean kate bat hautatzen du, nik erabili ditut lehenago. Zentro tematikoak "*" batekin markatuta daude, erraz nabarmendu ahal izateko, eta dagozkien lerroak iruzkindu ditzakezu beste kategorietako atalak lortzeko.

get_hubs funtzioaren irteera nahiko zerrenda ikusgarria da, hiztegi gisa gordetzen duguna. Zehazki, zerrenda osorik aurkezten dut, haren bolumena kalkulatu dezazuen.

hubs_profile = {'infosecurity', 'programming', 'webdev', 'python', 'sys_admin', 'it-infrastructure', 'devops', 'javascript', 'open_source', 'network_technologies', 'gamedev', 'cpp', 'machine_learning', 'pm', 'hr_management', 'linux', 'analysis_design', 'ui', 'net', 'hi', 'maths', 'mobile_dev', 'productpm', 'win_dev', 'it_testing', 'dev_management', 'algorithms', 'go', 'php', 'csharp', 'nix', 'data_visualization', 'web_testing', 's_admin', 'crazydev', 'data_mining', 'bigdata', 'c', 'java', 'usability', 'instant_messaging', 'gtd', 'system_programming', 'ios_dev', 'oop', 'nginx', 'kubernetes', 'sql', '3d_graphics', 'css', 'geo', 'image_processing', 'controllers', 'game_design', 'html5', 'community_management', 'electronics', 'android_dev', 'crypto', 'netdev', 'cisconetworks', 'db_admins', 'funcprog', 'wireless', 'dwh', 'linux_dev', 'assembler', 'reactjs', 'sales', 'microservices', 'search_technologies', 'compilers', 'virtualization', 'client_side_optimization', 'distributed_systems', 'api', 'media_management', 'complete_code', 'typescript', 'postgresql', 'rust', 'agile', 'refactoring', 'parallel_programming', 'mssql', 'game_promotion', 'robo_dev', 'reverse-engineering', 'web_analytics', 'unity', 'symfony', 'build_automation', 'swift', 'raspberrypi', 'web_design', 'kotlin', 'debug', 'pay_system', 'apps_design', 'git', 'shells', 'laravel', 'mobile_testing', 'openstreetmap', 'lua', 'vs', 'yii', 'sport_programming', 'service_desk', 'itstandarts', 'nodejs', 'data_warehouse', 'ctf', 'erp', 'video', 'mobileanalytics', 'ipv6', 'virus', 'crm', 'backup', 'mesh_networking', 'cad_cam', 'patents', 'cloud_computing', 'growthhacking', 'iot_dev', 'server_side_optimization', 'latex', 'natural_language_processing', 'scala', 'unreal_engine', 'mongodb', 'delphi',  'industrial_control_system', 'r', 'fpga', 'oracle', 'arduino', 'magento', 'ruby', 'nosql', 'flutter', 'xml', 'apache', 'sveltejs', 'devmail', 'ecommerce_development', 'opendata', 'Hadoop', 'yandex_api', 'game_monetization', 'ror', 'graph_design', 'scada', 'mobile_monetization', 'sqlite', 'accessibility', 'saas', 'helpdesk', 'matlab', 'julia', 'aws', 'data_recovery', 'erlang', 'angular', 'osx_dev', 'dns', 'dart', 'vector_graphics', 'asp', 'domains', 'cvs', 'asterisk', 'iis', 'it_monetization', 'localization', 'objectivec', 'IPFS', 'jquery', 'lisp', 'arvrdev', 'powershell', 'd', 'conversion', 'animation', 'webgl', 'wordpress', 'elm', 'qt_software', 'google_api', 'groovy_grails', 'Sailfish_dev', 'Atlassian', 'desktop_environment', 'game_testing', 'mysql', 'ecm', 'cms', 'Xamarin', 'haskell', 'prototyping', 'sw', 'django', 'gradle', 'billing', 'tdd', 'openshift', 'canvas', 'map_api', 'vuejs', 'data_compression', 'tizen_dev', 'iptv', 'mono', 'labview', 'perl', 'AJAX', 'ms_access', 'gpgpu', 'infolust', 'microformats', 'facebook_api', 'vba', 'twitter_api', 'twisted', 'phalcon', 'joomla', 'action_script', 'flex', 'gtk', 'meteorjs', 'iconoskaz', 'cobol', 'cocoa', 'fortran', 'uml', 'codeigniter', 'prolog', 'mercurial', 'drupal', 'wp_dev', 'smallbasic', 'webassembly', 'cubrid', 'fido', 'bada_dev', 'cgi', 'extjs', 'zend_framework', 'typography', 'UEFI', 'geo_systems', 'vim', 'creative_commons', 'modx', 'derbyjs', 'xcode', 'greasemonkey', 'i2p', 'flash_platform', 'coffeescript', 'fsharp', 'clojure', 'puppet', 'forth', 'processing_lang', 'firebird', 'javame_dev', 'cakephp', 'google_cloud_vision_api', 'kohanaphp', 'elixirphoenix', 'eclipse', 'xslt', 'smalltalk', 'googlecloud', 'gae', 'mootools', 'emacs', 'flask', 'gwt', 'web_monetization', 'circuit-design', 'office365dev', 'haxe', 'doctrine', 'typo3', 'regex', 'solidity', 'brainfuck', 'sphinx', 'san', 'vk_api', 'ecommerce'}

Konparazio baterako, geektimes atalek apalago dirudi:

hubs_gt = {'popular_science', 'history', 'soft', 'lifehacks', 'health', 'finance', 'artificial_intelligence', 'itcompanies', 'DIY', 'energy', 'transport', 'gadgets', 'social_networks', 'space', 'futurenow', 'it_bigraphy', 'antikvariat', 'games', 'hardware', 'learning_languages', 'urban', 'brain', 'internet_of_things', 'easyelectronics', 'cellular', 'physics', 'cryptocurrency', 'interviews', 'biotech', 'network_hardware', 'autogadgets', 'lasers', 'sound', 'home_automation', 'smartphones', 'statistics', 'robot', 'cpu', 'video_tech', 'Ecology', 'presentation', 'desktops', 'wearable_electronics', 'quantum', 'notebooks', 'cyberpunk', 'Peripheral', 'demoscene', 'copyright', 'astronomy', 'arvr', 'medgadgets', '3d-printers', 'Chemistry', 'storages', 'sci-fi', 'logic_games', 'office', 'tablets', 'displays', 'video_conferencing', 'videocards', 'photo', 'multicopters', 'supercomputers', 'telemedicine', 'cybersport', 'nano', 'crowdsourcing', 'infographics'}

Gainerako ardatzak modu berean kontserbatu ziren. Orain erraza da emaitza itzultzen duen funtzio bat idaztea, artikulua geektimes edo profil hub batekoa den.

def is_geektimes(hubs: List) -> bool:
    return len(set(hubs) & hubs_gt) > 0

def is_geektimes_only(hubs: List) -> bool:
    return is_geektimes(hubs) is True and is_profile(hubs) is False

def is_profile(hubs: List) -> bool:
    return len(set(hubs) & hubs_profile) > 0

Antzeko funtzioak egin ziren beste atal batzuetarako (β€œgarapena”, β€œadministrazioa”, etab.).

Prozesatzeko

Bada garaia aztertzen hasteko. Datu multzoa kargatzen dugu eta hub datuak prozesatzen ditugu.

def to_list(s: str) -> List[str]:
    # "user:popular_science+astronomy" => [popular_science, astronomy]
    return s.split(':')[1].split('+')

def to_date(dt: datetime) -> datetime.date:
    return dt.date()

df = pd.read_csv("habr_2019.csv", sep=',', encoding='utf-8', error_bad_lines=True, quotechar='"', comment='#')
dates = pd.to_datetime(df['datetime'], format='%Y-%m-%dT%H:%MZ')
dates += datetime.timedelta(hours=3)
df['date'] = dates.map(to_date, na_action=None)
hubs = df["hubs"].map(to_list, na_action=None)
df['hubs'] = hubs
df['is_profile'] = hubs.map(is_profile, na_action=None)
df['is_geektimes'] = hubs.map(is_geektimes, na_action=None)
df['is_geektimes_only'] = hubs.map(is_geektimes_only, na_action=None)
df['is_admin'] = hubs.map(is_admin, na_action=None)
df['is_develop'] = hubs.map(is_develop, na_action=None)

Orain datuak egunaren arabera taldeka ditzakegu eta gune ezberdinetako argitalpen kopurua bistaratu.

g = df.groupby(['date'])
days_count = g.size().reset_index(name='counts')
year_days = days_count['date'].values
grouped = g.sum().reset_index()
profile_per_day_avg = grouped['is_profile'].rolling(window=20, min_periods=1).mean()
geektimes_per_day_avg = grouped['is_geektimes'].rolling(window=20, min_periods=1).mean()
geektimesonly_per_day_avg = grouped['is_geektimes_only'].rolling(window=20, min_periods=1).mean()
admin_per_day_avg = grouped['is_admin'].rolling(window=20, min_periods=1).mean()
develop_per_day_avg = grouped['is_develop'].rolling(window=20, min_periods=1).mean()

Matplotlib erabiliz argitaratutako artikulu kopurua erakusten dugu:

Habrastatistics: guneko gehien eta gutxien bisitatutako atalak aztertzea

"geektimes" eta "geektimes bakarrik" artikuluak taulan banatu ditut, zeren Artikulu bat bi ataletakoa izan daiteke aldi berean (adibidez, "DIY" + "mikrokontrolagailuak" + "C++"). "profila" izendapena erabili nuen guneko profil-artikuluak nabarmentzeko, nahiz eta beharbada ingelesezko profil terminoa guztiz zuzena ez den.

Aurreko zatian uda honetan hasiko diren geektimes artikuluen ordainketa arauen aldaketarekin lotutako "geektimes efektuari" buruz galdetu genuen. Erakuts ditzagun geektimes artikuluak bereizita:

df_gt = df[(df['is_geektimes_only'] == True)]
group_gt = df_gt.groupby(['date'])
days_count_gt = group_gt.size().reset_index(name='counts')
grouped = group_gt.sum().reset_index()
year_days_gt = days_count_gt['date'].values
view_gt_per_day_avg = grouped['views'].rolling(window=20, min_periods=1).mean()

Emaitza interesgarria da. Geektimes artikuluen ikustaldien gutxi gorabeherako proportzioa 1:5 ingurukoa da. Baina ikustaldi-kopuru osoa gorakada nabarmena izan arren, "entretenimendu" artikuluen ikustea maila berean mantendu zen gutxi gorabehera.

Habrastatistics: guneko gehien eta gutxien bisitatutako atalak aztertzea

Era berean, ohar zaitezke "geektimes" atalean artikuluen ikustaldi kopuruak oraindik ere behera egin duela arauak aldatu ondoren, baina "begiz", balio guztien % 5 baino gehiago.

Interesgarria da artikulu bakoitzeko batez besteko ikustaldi kopurua ikustea:

Habrastatistics: guneko gehien eta gutxien bisitatutako atalak aztertzea

"Entretenimendu" artikuluetarako batez bestekoaren %40 inguru dago. Hau seguruenik ez da harritzekoa. Apirilaren hasierako porrota ulertezina zait, agian hori gertatu da, edo analisi akatsen bat da, edo agian geektimes egileren bat oporretan joan zen ;).

Bide batez, grafikoak bi gailur nabarmen gehiago erakusten ditu artikuluen ikustaldi kopuruan: Urte Berria eta Maiatzeko oporrak.

Hubak

Joan gaitezen agindutako zentroen azterketara. Zerrenda ditzagun 20 zentro nagusiak ikustaldi kopuruaren arabera:

hubs_info = []
for hub_name in hubs_all:
    mask = df['hubs'].apply(lambda x: hub_name in x)
    df_hub = df[mask]

    count, views = df_hub.shape[0], df_hub['views'].sum()
    hubs_info.append((hub_name, count, views))

# Draw hubs
hubs_top = sorted(hubs_info, key=lambda v: v[2], reverse=True)[:20]
top_views = list(map(lambda x: x[2], hubs_top))
top_names = list(map(lambda x: x[0], hubs_top))

plt.rcParams["figure.figsize"] = (8, 6)
plt.bar(range(0, len(top_views)), top_views)
plt.xticks(range(0, len(top_names)), top_names, rotation=90)
plt.ticklabel_format(style='plain', axis='y')
plt.tight_layout()
plt.show()

Emaitza:

Habrastatistics: guneko gehien eta gutxien bisitatutako atalak aztertzea

Harrigarria bada ere, ikuspegiei dagokienez, gunerik ezagunena "Informazioaren segurtasuna" izan zen; 5 lider nagusien artean "Programazioa" eta "Zientzia popularra" ere sartu ziren.

Antitop Gtk eta Cocoa okupatzen ditu.

Habrastatistics: guneko gehien eta gutxien bisitatutako atalak aztertzea

Sekretu bat kontatuko dizuet, goiko hubak ere ikus daitezke Hemen, ikustaldi kopurua hor agertzen ez den arren.

Rating

Eta azkenik, agindutako balorazioa. Zentroaren analisiaren datuak erabiliz, 2019 urte honetarako gune ezagunenetako artikulu ezagunenak bistaratu ditzakegu.

Informazioaren Segurtasuna

Programazioa

Herri Zientzia

karrera

Informatikako legeria

Web garapena

GTK

Eta azkenik, inor iraindu ez dadin, gutxien bisitatutako hubaren balorazioa emango diot β€œgtk”. Urtebeteren buruan argitaratu zen ΠΎΠ΄Π½Π° Artikuluak, "automatikoki" ere balorazioko lehen lerroa hartzen duena.

Ondorioa

Ez da ondoriorik izango. Irakurketa zoriontsua guztioi.

Iturria: www.habr.com

Gehitu iruzkin berria