Habrastatistics: exploració de les seccions més i menys visitades del lloc

Hola Habr.

В part anterior El trànsit d'Habr es va analitzar d'acord amb els paràmetres principals: el nombre d'articles, les seves vistes i puntuacions. No obstant això, la qüestió de la popularitat de les seccions del lloc va romandre sense examinar. Va ser interessant mirar-ho amb més detall i trobar els centres més populars i més impopulars. Finalment, miraré l'efecte geektimes amb més detall, i acabaré amb una nova selecció dels millors articles basats en nous rànquings.

Habrastatistics: exploració de les seccions més i menys visitades del lloc

Per a aquells que estiguin interessats en el que va passar, la continuació està sota el tall.

Permeteu-me recordar-vos una vegada més que les estadístiques i les valoracions no són oficials, no tinc cap informació privilegiada. Tampoc es garanteix que no m'he equivocat en algun lloc o que m'he perdut alguna cosa. Però tot i així, crec que va resultar interessant. Primer començarem amb el codi; els que no estiguin interessats en això poden saltar-se els primers apartats.

Recopilació de dades

A la primera versió de l'analitzador, només es va tenir en compte el nombre de visualitzacions, comentaris i puntuacions d'articles. Això ja és bo, però no permet fer consultes més complexes. És hora d'analitzar les seccions temàtiques del lloc; això us permetrà fer una investigació força interessant, per exemple, veure com ha canviat la popularitat de la secció "C++" durant diversos anys.

S'ha millorat l'analitzador d'articles, ara retorna els hubs als quals pertany l'article, així com el sobrenom de l'autor i la seva puntuació (aquí també es poden fer moltes coses interessants, però això vindrà més endavant). Les dades es guarden en un fitxer csv que s'assembla a això:

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

Rebrem una llista dels principals centres temàtics del lloc.

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)

La funció find_between i la classe Str seleccionen una cadena entre dues etiquetes, les vaig utilitzar més d'hora. Els centres temàtics estan marcats amb un "*" perquè es puguin ressaltar fàcilment, i també podeu descomentar les línies corresponents per obtenir seccions d'altres categories.

La sortida de la funció get_hubs és una llista força impressionant, que guardem com a diccionari. En concret us presento la llista sencera perquè pugueu estimar el seu volum.

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

Per comparar, les seccions de geektimes semblen més modestes:

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

La resta de nuclis es van conservar de la mateixa manera. Ara és fàcil escriure una funció que retorni el resultat tant si l'article pertany a geektimes com a un centre de perfils.

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

Funcions similars es van fer per a altres seccions (“desenvolupament”, “administració”, etc.).

Tractament

És hora de començar a analitzar. Carreguem el conjunt de dades i processem les dades del concentrador.

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)

Ara podem agrupar les dades per dia i mostrar el nombre de publicacions per a diferents hubs.

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

Mostrem el nombre d'articles publicats mitjançant Matplotlib:

Habrastatistics: exploració de les seccions més i menys visitades del lloc

Vaig dividir els articles "geektimes" i "geektimes only" al gràfic, perquè Un article pot pertànyer a les dues seccions alhora (per exemple, "DIY" + "microcontroladors" + "C++"). Vaig utilitzar la designació "perfil" per destacar articles de perfil al lloc, encara que potser el terme anglès profile per a això no és del tot correcte.

A la part anterior, van preguntar sobre l'"efecte geektimes" associat al canvi de les normes de pagament dels articles per a geektimes aquest estiu. Mostrem els articles de geektimes per separat:

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

El resultat és interessant. La proporció aproximada de visualitzacions d'articles de geektimes respecte al total és d'1:5. Però tot i que el nombre total de visualitzacions va fluctuar notablement, la visualització d'articles d'"entreteniment" es va mantenir aproximadament al mateix nivell.

Habrastatistics: exploració de les seccions més i menys visitades del lloc

També podeu notar que el nombre total de visualitzacions d'articles a la secció "geektimes" encara va baixar després de canviar les regles, però "a ull", no més del 5% dels valors totals.

És interessant veure el nombre mitjà de visualitzacions per article:

Habrastatistics: exploració de les seccions més i menys visitades del lloc

Per als articles d'"entreteniment" és aproximadament un 40% per sobre de la mitjana. Això probablement no és sorprenent. El fracàs de principis d'abril no em queda clar, potser és el que va passar, o és una mena d'error d'anàlisi, o potser un dels autors de geektimes se'n va anar de vacances ;).

Per cert, el gràfic mostra dos pics més notables en el nombre de visualitzacions d'articles: les vacances d'Any Nou i de maig.

Hubs

Passem a l'anàlisi promesa dels hubs. Enumerem els 20 centres principals per nombre de visualitzacions:

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

Resultat:

Habrastatistics: exploració de les seccions més i menys visitades del lloc

Sorprenentment, el centre més popular en termes de vistes va ser "Seguretat de la informació"; els 5 principals líders també incloïen "Programació" i "Ciència popular".

Antitop ocupa Gtk i Cocoa.

Habrastatistics: exploració de les seccions més i menys visitades del lloc

Us diré un secret, també es poden veure els principals hubs aquí, tot i que no s'hi mostra el nombre de visualitzacions.

Classificació

I finalment, la qualificació promesa. Utilitzant les dades d'anàlisi del concentrador, podem mostrar els articles més populars dels centres més populars per a aquest any 2019.

Seguretat de la informació

Programació

Ciència Popular

carrera

Legislació en informàtica

desenvolupament web

GTK

I finalment, perquè ningú s'ofengui, donaré la qualificació del hub menys visitat "gtk". En un any es va publicar одна L'article, que també ocupa "automàticament" la primera línia de la qualificació.

Conclusió

No hi haurà conclusió. Feliç lectura a tothom.

Font: www.habr.com

Afegeix comentari