Habrastatistiques : explorer les sections les plus et les moins visitées du site

Hé Habr.

В section précédente Le trafic de Habr a été analysé selon les principaux paramètres - le nombre d'articles, leurs vues et notes. Cependant, la question de la popularité des sections du site n’a pas été examinée. Il est devenu intéressant d’examiner cela plus en détail et de trouver les hubs les plus populaires et les plus impopulaires. Enfin, j'examinerai plus en détail l'effet geektimes, en terminant par une nouvelle sélection des meilleurs articles basée sur de nouveaux classements.

Habrastatistiques : explorer les sections les plus et les moins visitées du site

Pour ceux qui s’intéressent à ce qui s’est passé, la suite est sous le cut.

Permettez-moi de vous rappeler encore une fois que les statistiques et les notes ne sont pas officielles, je n'ai aucune information privilégiée. Il n’est pas non plus garanti que je n’ai pas commis d’erreur quelque part ou que j’ai raté quelque chose. Mais je pense quand même que cela s’est avéré intéressant. Nous commencerons par le code ; ceux qui ne sont pas intéressés peuvent sauter les premières sections.

Collecte de données

Dans la première version de l’analyseur, seuls le nombre de vues, les commentaires et les notes des articles étaient pris en compte. C’est déjà bien, mais cela ne permet pas de faire des requêtes plus complexes. Il est temps d’analyser les sections thématiques du site, cela vous permettra de faire des recherches assez intéressantes, par exemple de voir comment la popularité de la section « C++ » a évolué sur plusieurs années.

L'analyseur d'articles a été amélioré, il renvoie désormais les hubs auxquels appartient l'article, ainsi que le pseudo de l'auteur et sa note (beaucoup de choses intéressantes peuvent être faites ici aussi, mais cela viendra plus tard). Les données sont enregistrées dans un fichier csv qui ressemble à ceci :

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

Nous recevrons une liste des principaux pôles thématiques du site.

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 fonction find_between et la classe Str sélectionnent une chaîne entre deux balises, je les ai utilisées plus tôt. Les hubs thématiques sont marqués d'un « * » afin de pouvoir être facilement mis en évidence, et vous pouvez également décommenter les lignes correspondantes pour obtenir des sections d'autres catégories.

Le résultat de la fonction get_hubs est une liste assez impressionnante, que nous enregistrons sous forme de dictionnaire. Je vous présente spécifiquement la liste dans son intégralité afin que vous puissiez en estimer le volume.

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

A titre de comparaison, les sections geektimes semblent plus 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'}

Les pôles restants ont été préservés de la même manière. Il est désormais facile d'écrire une fonction qui renvoie le résultat, que l'article appartienne à geektimes ou à un hub de profils.

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

Des fonctions similaires ont été créées pour d'autres sections (« développement », « administration », etc.).

Traitement

Il est temps de commencer à analyser. Nous chargeons l'ensemble de données et traitons les données du 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)

Nous pouvons désormais regrouper les données par jour et afficher le nombre de publications pour différents 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()

Nous affichons le nombre d'articles publiés à l'aide de Matplotlib :

Habrastatistiques : explorer les sections les plus et les moins visitées du site

J'ai divisé les articles « geektimes » et « geektimes uniquement » dans le tableau, car Un article peut appartenir aux deux sections à la fois (par exemple, « DIY » + « microcontrôleurs » + « C++ »). J'ai utilisé la désignation « profil » pour mettre en évidence les articles de profil sur le site, même si le terme anglais profile pour cela n'est peut-être pas tout à fait correct.

Dans la partie précédente, nous avons posé des questions sur « l'effet geektimes » associé au changement des règles de paiement des articles pour geektimes à partir de cet été. Affichons les articles du geektimes séparément :

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

Le résultat est intéressant. Le rapport approximatif entre les vues des articles de Geektimes et le total se situe autour de 1:5. Mais si le nombre total de vues a sensiblement fluctué, le nombre de visionnages d'articles « divertissement » est resté à peu près au même niveau.

Habrastatistiques : explorer les sections les plus et les moins visitées du site

Vous pouvez également remarquer que le nombre total de vues des articles de la section « geektimes » a encore diminué après avoir modifié les règles, mais « à l'œil nu », de pas plus de 5 % des valeurs totales.

Il est intéressant de regarder le nombre moyen de vues par article :

Habrastatistiques : explorer les sections les plus et les moins visitées du site

Pour les articles « divertissement », il est environ 40 % supérieur à la moyenne. Ce n’est probablement pas surprenant. L'échec de début avril n'est pas clair pour moi, c'est peut-être ce qui s'est passé, ou c'est une sorte d'erreur d'analyse, ou peut-être que l'un des auteurs de Geektimes est parti en vacances ;).

À propos, le graphique montre deux pics plus visibles dans le nombre de vues d'articles : les vacances du Nouvel An et de mai.

Hubs

Passons à l'analyse promise des hubs. Listons les 20 meilleurs hubs par nombre de vues :

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

Résultat:

Habrastatistiques : explorer les sections les plus et les moins visitées du site

Étonnamment, le centre d'opinions le plus populaire en termes de points de vue était « Sécurité de l'information » ; les 5 principaux leaders incluaient également « Programmation » et « Science populaire ».

Antitop occupe Gtk et Cocoa.

Habrastatistiques : explorer les sections les plus et les moins visitées du site

Je vais vous dire un secret, les meilleurs hubs sont également visibles ici, bien que le nombre de vues n'y soit pas indiqué.

Classement

Et enfin, la note promise. À l'aide des données d'analyse des hubs, nous pouvons afficher les articles les plus populaires sur les hubs les plus populaires pour cette année 2019.

Sécurité de l'information

Programmation

Science populaire

carrière

Législation en informatique

développement web

GTK

Et enfin, pour que personne ne soit offensé, je donnerai la note du hub le moins visité « gtk ». En un an, il a été publié un L'article, qui occupe également « automatiquement » la première ligne du classement.

Conclusion

Il n'y aura pas de conclusion. Bonne lecture à tous.

Source: habr.com

Ajouter un commentaire