Habrastatistik: Erkundung der am meisten und am wenigsten besuchten Abschnitte der Website

Hallo Habr.

Im vorherigen Teil wurde Habrs Anwesenheit anhand der Hauptparameter analysiert - der Anzahl der Artikel, ihrer Ansichten und Bewertungen. Die Frage nach der Beliebtheit der Abschnitte der Website wurde jedoch nicht berücksichtigt. Es wurde interessant, dies genauer zu untersuchen und die beliebtesten und unbeliebtesten Hubs zu finden. Abschließend werde ich den „Geektimes-Effekt“ genauer untersuchen und am Ende erhalten die Leser eine neue Auswahl der besten Artikel zu den neuen Bewertungen.



Wen kümmert es, was passiert ist, fuhr unter dem Schnitt fort.

Ich erinnere Sie noch einmal daran, dass Statistiken und Bewertungen nicht offiziell sind, ich habe keine Insiderinformationen. Es kann auch nicht garantiert werden, dass ich mich nicht irgendwo geirrt habe oder etwas nicht verpasst habe. Trotzdem finde ich es interessant. Wir beginnen zuerst mit dem Code, für den dies irrelevant ist. Die ersten Abschnitte können übersprungen werden.

Datenerfassung


In der ersten Version des Parsers wurden nur die Anzahl der Aufrufe, Kommentare und die Bewertung der Artikel berücksichtigt. Dies ist bereits gut, ermöglicht jedoch keine komplexeren Abfragen. Es ist an der Zeit, die thematischen Abschnitte der Website zu analysieren. Auf diese Weise können wir interessante Studien durchführen, um beispielsweise festzustellen, wie sich die Popularität des Abschnitts "C ++" über mehrere Jahre verändert hat.

Der Artikel-Parser wurde verbessert und gibt nun die Hubs zurück, zu denen der Artikel gehört, sowie den Spitznamen des Autors und seine Bewertung (hier können Sie auch viele interessante Dinge tun, aber dies später). Die Daten werden in einer CSV-Datei vom ungefähr folgenden Typ gespeichert:

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

Holen Sie sich eine Liste der wichtigsten thematischen Hubs der Website.

 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-Funktion und die Str-Klasse markieren eine Linie zwischen zwei Tags, die ich zuvor verwendet habe. Thematische Hubs sind mit "*" gekennzeichnet, damit sie leicht hervorgehoben werden können. Sie können auch die entsprechenden Zeilen auskommentieren, um Abschnitte anderer Kategorien zu erhalten.

Am Ausgang der Funktion get_hubs erhalten wir eine ziemlich beeindruckende Liste, die wir als Wörterbuch speichern. Ich zitiere speziell die gesamte Liste, damit das Volumen geschätzt werden kann.

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

Zum Vergleich sehen Geektimes-Abschnitte bescheidener aus:

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

Ebenso wurden die verbleibenden Hubs gespeichert. Jetzt ist es einfach, eine Funktion zu schreiben, die das Ergebnis zurückgibt. Der Artikel bezieht sich auf Geektimes oder auf einen Profil-Hub.

 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 

Ähnliche Funktionen wurden für andere Abschnitte ("Entwicklung", "Verwaltung" usw.) erstellt.

Verarbeitung


Es ist Zeit, mit der Analyse zu beginnen. Wir laden den Datensatz und verarbeiten die Daten der Hubs.

 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) 

Jetzt können wir die Daten nach Tag gruppieren und die Anzahl der Veröffentlichungen nach verschiedenen Hubs anzeigen.

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

Zeigen Sie die Anzahl der veröffentlichten Artikel mit Matplotlib an:



Ich habe die Artikel "Geektimes" und "Nur Geektimes" in der Grafik unterteilt, weil Ein Artikel kann gleichzeitig zu beiden Abschnitten gehören (z. B. „DIY“ + „Mikrocontroller“ + „C ++“). Mit der Bezeichnung „Profil“ habe ich die Profilartikel der Website hervorgehoben, obwohl es möglich ist, dass der englische Begriff Profil dafür nicht ganz korrekt ist.

Im vorherigen Teil haben wir nach dem "Geektimes-Effekt" gefragt, der mit der Änderung der Regeln für die Bezahlung von Artikeln für Geektimes ab diesem Sommer verbunden ist. Wir leiten separate Geektimes-Artikel ab:

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

Das Ergebnis ist interessant. Das ungefähre Verhältnis der Ansichten von Artikeln geektimes zur Gesamtzahl liegt irgendwo bei 1: 5. Wenn jedoch die Gesamtzahl der Aufrufe merklich schwankte, wurde die Anzeige von "unterhaltsamen" Artikeln ungefähr auf dem gleichen Niveau gehalten.



Sie können auch feststellen, dass die Gesamtzahl der Aufrufe von Artikeln im Abschnitt "Geektimes" nach Änderung der Regeln immer noch gesunken ist, jedoch "per Auge" nicht mehr als 5% der Gesamtwerte.

Es ist interessant, die durchschnittliche Anzahl der Aufrufe pro Artikel zu sehen:



Für "unterhaltsame" Artikel sind es etwa 40% über dem Durchschnitt. Dies ist wahrscheinlich nicht überraschend. Der Fehler Anfang April ist mir nicht klar, vielleicht war es das, oder ist es eine Art Analysefehler, oder vielleicht hat einer der Autoren geektimes Urlaub gemacht;).

Übrigens sind in der Grafik zwei weitere auffällige Spitzen in der Anzahl der Artikelansichten zu sehen - Neujahrs- und Mai-Feiertage.

Hubs


Fahren wir mit der versprochenen Analyse der Hubs fort. Wir werden die Top 20 Hubs nach Anzahl der Aufrufe anzeigen:

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

Ergebnis:



Überraschenderweise erwies sich der Hub „Informationssicherheit“ als der beliebteste in Bezug auf die Anzeige. Auch „Programmierung“ und „Populärwissenschaft“ gehören zu den Top 5 der führenden Unternehmen.

Antitop nimmt Gtk und Kakao.



Ich werde Ihnen ein Geheimnis verraten, die Top-Hubs sind auch hier zu sehen, obwohl die Anzahl der Ansichten dort nicht angezeigt wird.

Bewertung


Und schließlich die versprochene Bewertung. Mit den Daten aus der Analyse von Hubs können wir die beliebtesten Artikel zu den beliebtesten Hubs für dieses Jahr 2019 anzeigen.

Informationssicherheit


Programmierung


Populärwissenschaft


Karriere


Gesetzgebung in der IT


Webentwicklung


GTK

Und schließlich, um niemanden zu beleidigen, gebe ich Ihnen die Bewertung des am wenigsten besuchten Hubs "gtk". Darin wurde im Laufe des Jahres ein Artikel veröffentlicht, der auch „automatisch“ die erste Zeile der Bewertung belegt.


Fazit


Es wird keine Schlussfolgerung geben. Viel Spaß beim Lesen für alle.

Source: https://habr.com/ru/post/de467429/


All Articles