Habrastatistiek: verken die mees en minste besoekte dele van die webwerf

Haai Habr.

В vorige deel Habr se verkeer is ontleed volgens die hoofparameters - die aantal artikels, hul sienings en graderings. Die kwessie van die gewildheid van die terreinafdelings het egter onondersoek gebly. Dit het interessant geword om in meer besonderhede hierna te kyk en die gewildste en mees ongewilde spilpunte te vind. Laastens sal ek in meer besonderhede na die geektimes-effek kyk, en eindig met 'n nuwe keuse van die beste artikels gebaseer op nuwe ranglys.

Habrastatistiek: verken die mees en minste besoekte dele van die webwerf

Vir diegene wat belangstel in wat gebeur het, is die voortsetting onder die knie.

Laat ek jou weereens herinner dat die statistieke en graderings nie amptelik is nie, ek het geen binne-inligting nie. Dit is ook nie gewaarborg dat ek nie iewers 'n fout gemaak het of iets gemis het nie. Maar tog dink ek dit het interessant geword. Ons sal eers met die kode begin; diegene wat nie hierin belangstel nie, kan die eerste afdelings oorslaan.

Data-insameling

In die eerste weergawe van die ontleder is slegs die aantal kyke, opmerkings en artikelgraderings in ag geneem. Dit is reeds goed, maar dit laat jou nie toe om meer komplekse navrae te maak nie. Dit is tyd om die tematiese afdelings van die webwerf te ontleed; dit sal jou toelaat om nogal interessante navorsing te doen, byvoorbeeld om te sien hoe die gewildheid van die "C++"-afdeling oor 'n paar jaar verander het.

Die artikelontleder is verbeter, nou gee dit die hubs terug waartoe die artikel behoort, sowel as die skrywer se bynaam en sy gradering (baie interessante dinge kan ook hier gedoen word, maar dit sal later kom). Die data word gestoor in 'n csv-lêer wat soos volg lyk:

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
...

Ons sal 'n lys van die belangrikste tematiese spilpunte van die webwerf ontvang.

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)

Die find_between-funksie en die Str-klas kies 'n string tussen twee etikette, ek het dit gebruik vroeër. Tematiese hubs is gemerk met 'n "*" sodat hulle maklik uitgelig kan word, en jy kan ook die ooreenstemmende lyne ontmerk om afdelings van ander kategorieë te kry.

Die afvoer van die get_hubs-funksie is 'n redelik indrukwekkende lys, wat ons as 'n woordeboek stoor. Ek bied spesifiek die lys in sy geheel aan sodat jy die volume daarvan kan skat.

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'}

Ter vergelyking lyk die geektimes-afdelings meer beskeie:

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'}

Die oorblywende spilpunte is op dieselfde manier bewaar. Nou is dit maklik om 'n funksie te skryf wat die resultaat gee of die artikel aan geektimes of 'n profielhub behoort.

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

Soortgelyke funksies is gemaak vir ander afdelings (“ontwikkeling”, “administrasie”, ens.).

Processing

Dit is tyd om te begin analiseer. Ons laai die datastel en verwerk die spilpuntdata.

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)

Nou kan ons die data per dag groepeer en die aantal publikasies vir verskillende spilpunte vertoon.

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()

Ons vertoon die aantal gepubliseerde artikels met Matplotlib:

Habrastatistiek: verken die mees en minste besoekte dele van die webwerf

Ek het die artikels "geektimes" en "geektimes only" in die grafiek verdeel, want 'n Artikel kan gelyktydig aan albei afdelings behoort (byvoorbeeld "DIY" + "mikrobeheerders" + "C++"). Ek het die benaming “profiel” gebruik om profielartikels op die webwerf uit te lig, alhoewel die Engelse term profiel hiervoor dalk nie heeltemal korrek is nie.

In die vorige deel het ons gevra oor die "geektime-effek" wat verband hou met die verandering in die betaalreëls vir artikels vir geektye wat hierdie somer begin. Kom ons vertoon die geektimes-artikels afsonderlik:

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()

Die resultaat is interessant. Die benaderde verhouding van kyke van geektime-artikels tot die totaal is iewers rondom 1:5. Maar terwyl die totale aantal kyke merkbaar gewissel het, het die kyk van "vermaak"-artikels op ongeveer dieselfde vlak gebly.

Habrastatistiek: verken die mees en minste besoekte dele van die webwerf

Jy kan ook sien dat die totale aantal kyke van artikels in die "geektimes"-afdeling steeds gedaal het nadat die reëls verander is, maar "per oog", met nie meer as 5% van die totale waardes nie.

Dit is interessant om te kyk na die gemiddelde aantal kyke per artikel:

Habrastatistiek: verken die mees en minste besoekte dele van die webwerf

Vir “vermaak”-artikels is dit sowat 40% bo die gemiddelde. Dit is waarskynlik nie verbasend nie. Die mislukking aan die begin van April is vir my onduidelik, miskien is dit wat gebeur het, of dit is een of ander ontledingsfout, of dalk het een van die geektime-skrywers met vakansie gegaan ;).

Terloops, die grafiek toon nog twee opvallende pieke in die aantal kyke van artikels - die Nuwejaar en Mei-vakansies.

Hubs

Kom ons gaan oor na die beloofde ontleding van spilpunte. Kom ons lys die top 20 spilpunte volgens aantal kyke:

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()

Gevolg:

Habrastatistiek: verken die mees en minste besoekte dele van die webwerf

Verbasend genoeg was die gewildste middelpunt in terme van sienings “Inligtingsekerheid”; die top 5 leiers het ook “Programmering” en “Populêre wetenskap” ingesluit.

Antitop beslaan Gtk en Kakao.

Habrastatistiek: verken die mees en minste besoekte dele van die webwerf

Ek sal jou 'n geheim vertel, die boonste hubs kan ook gesien word hier, hoewel die aantal kyke nie daar gewys word nie.

Rating

En uiteindelik, die beloofde gradering. Deur spilpuntontledingsdata te gebruik, kan ons die gewildste artikels vir die gewildste spilpunte vir hierdie jaar 2019 vertoon.

Inligtings sekuriteit

Programmering

Populêre Wetenskap

loopbaan

Wetgewing in IT

webblad ontwikkeling

GTK

En ten slotte, sodat niemand aanstoot neem nie, sal ek die gradering gee van die minste besoekte hub "gtk". Binne 'n jaar is dit gepubliseer een Die artikel, wat ook "outomaties" die eerste reël van die gradering beklee.

Gevolgtrekking

Daar sal geen gevolgtrekking wees nie. Lekker lees almal.

Bron: will.com

Voeg 'n opmerking