Habrastatistics: explorarea celor mai și cele mai puțin vizitate secțiuni ale site-ului

Hei Habr.

В partea anterioară Traficul lui Habr a fost analizat în funcție de principalii parametri - numărul de articole, vizualizările și ratingurile acestora. Cu toate acestea, problema popularității secțiunilor site-ului a rămas neexaminată. A devenit interesant să privim acest lucru în detaliu și să găsim cele mai populare și mai nepopulare hub-uri. În cele din urmă, mă voi uita la efectul geektimes mai detaliat, încheind cu o nouă selecție a celor mai bune articole bazate pe noile clasamente.

Habrastatistics: explorarea celor mai și cele mai puțin vizitate secțiuni ale site-ului

Pentru cei care sunt interesați de ceea ce s-a întâmplat, continuarea este sub tăietură.

Permiteți-mi să vă reamintesc încă o dată că statisticile și ratingurile nu sunt oficiale, nu am nicio informație privilegiată. De asemenea, nu este garantat că nu am greșit undeva sau am omis ceva. Dar totuși, cred că s-a dovedit interesant. Vom începe mai întâi cu codul; cei care nu sunt interesați de acest lucru pot sări peste primele secțiuni.

Colectare de date

În prima versiune a analizatorului, a fost luat în considerare doar numărul de vizualizări, comentarii și evaluări ale articolelor. Acest lucru este deja bun, dar nu vă permite să faceți interogări mai complexe. Este timpul să analizați secțiunile tematice ale site-ului; acest lucru vă va permite să faceți cercetări destul de interesante, de exemplu, să vedeți cum s-a schimbat popularitatea secțiunii „C++” de-a lungul mai multor ani.

Analizatorul de articole a fost îmbunătățit, acum returnează hub-urile cărora le aparține articolul, precum și porecla autorului și ratingul acestuia (se pot face și aici o mulțime de lucruri interesante, dar asta va veni mai târziu). Datele sunt salvate într-un fișier csv care arată cam așa:

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

Vom primi o listă cu principalele hub-uri tematice ale site-ului.

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)

Funcția find_between și clasa Str selectează un șir între două etichete, le-am folosit mai devreme. Hub-urile tematice sunt marcate cu un „*”, astfel încât să poată fi evidențiate cu ușurință și, de asemenea, puteți decomenta liniile corespunzătoare pentru a obține secțiuni din alte categorii.

Ieșirea funcției get_hubs este o listă destul de impresionantă, pe care o salvăm ca dicționar. Vă prezint în mod specific lista în întregime, astfel încât să puteți estima volumul acesteia.

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

Pentru comparație, secțiunile Geektimes par mai modeste:

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

Buturile rămase au fost păstrate în același mod. Acum este ușor să scrieți o funcție care returnează rezultatul, indiferent dacă articolul aparține geektimes sau unui hub de profil.

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

Funcții similare au fost realizate pentru alte secții („dezvoltare”, „administrare”, etc.).

prelucrare

Este timpul să începem analiza. Încărcăm setul de date și procesăm datele hub.

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)

Acum putem grupa datele pe zi și putem afișa numărul de publicații pentru diferite hub-uri.

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

Afișăm numărul de articole publicate folosind Matplotlib:

Habrastatistics: explorarea celor mai și cele mai puțin vizitate secțiuni ale site-ului

Am împărțit articolele „geektimes” și „geektimes only” în grafic, pentru că Un articol poate aparține ambelor secțiuni în același timp (de exemplu, „DIY” + „microcontrolere” + „C++”). Am folosit denumirea „profil” pentru a evidenția articolele de profil de pe site, deși poate că termenul în engleză profil pentru aceasta nu este în întregime corect.

În partea anterioară am întrebat despre „efectul geektimes” asociat cu modificarea regulilor de plată pentru articolele pentru geektimes începând din această vară. Să afișăm separat articolele Geektimes:

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

Rezultatul este interesant. Raportul aproximativ al vizionărilor articolelor Geektimes față de total este undeva în jur de 1:5. Dar, în timp ce numărul total de vizionări a fluctuat considerabil, vizionarea articolelor de „divertisment” a rămas aproximativ la același nivel.

Habrastatistics: explorarea celor mai și cele mai puțin vizitate secțiuni ale site-ului

De asemenea, puteți observa că numărul total de vizualizări ale articolelor din secțiunea „geektimes” a scăzut în continuare după modificarea regulilor, dar „pe ochi”, cu cel mult 5% din valorile totale.

Este interesant să ne uităm la numărul mediu de vizionări per articol:

Habrastatistics: explorarea celor mai și cele mai puțin vizitate secțiuni ale site-ului

Pentru articolele „divertisment” este cu aproximativ 40% peste medie. Acest lucru probabil nu este surprinzător. Eșecul de la începutul lunii aprilie este neclar pentru mine, poate asta s-a întâmplat, sau este un fel de eroare de analiză, sau poate că unul dintre autorii geektimes a plecat în vacanță ;).

Apropo, graficul arată încă două vârfuri vizibile ale numărului de vizualizări ale articolelor - sărbătorile de Anul Nou și mai.

Huburi

Să trecem la analiza promisă a hub-urilor. Să enumerăm primele 20 de hub-uri după numărul de vizualizări:

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

Rezultat:

Habrastatistics: explorarea celor mai și cele mai puțin vizitate secțiuni ale site-ului

În mod surprinzător, cel mai popular hub în ceea ce privește punctele de vedere a fost „Securitatea informațiilor”; primii 5 lideri au inclus și „Programare” și „Știința populară”.

Antitop ocupă Gtk și Cocoa.

Habrastatistics: explorarea celor mai și cele mai puțin vizitate secțiuni ale site-ului

Vă spun un secret, se văd și huburile de top aici, deși numărul de vizualizări nu este afișat acolo.

evaluare

Și în sfârșit, ratingul promis. Folosind datele de analiză a hub-urilor, putem afișa cele mai populare articole pentru cele mai populare hub-uri pentru acest an 2019.

Securitatea informațiilor

Programare

Știința Populară

carieră

Legislația în IT

dezvoltare web

GTK

Și, în sfârșit, pentru ca nimeni să nu fie jignit, voi acorda ratingul celui mai puțin vizitat hub „gtk”. Într-un an a fost publicat одна Articolul, care de asemenea „automat” ocupă prima linie a ratingului.

Concluzie

Nu va fi nicio concluzie. Lectură fericită tuturor.

Sursa: www.habr.com

Adauga un comentariu