Habrastatistics: explorando las secciones más y menos visitadas del sitio

Hola Habr

En la parte anterior , la asistencia de Habr fue analizada por los parámetros principales: la cantidad de artículos, sus opiniones y calificaciones. Sin embargo, no se consideró la cuestión de la popularidad de las secciones del sitio. Se volvió interesante examinar esto con más detalle y encontrar los centros más populares y más impopulares. Finalmente, examinaré el "efecto geektimes" con más detalle, y al final, los lectores recibirán una nueva selección de los mejores artículos sobre las nuevas calificaciones.



A quién le importa lo que pasó, continuó bajo el corte.

Les recuerdo una vez más que las estadísticas y calificaciones no son oficiales, no tengo ninguna información privilegiada. Tampoco está garantizado que no me haya equivocado en alguna parte o que no me haya perdido algo. Pero aún así, creo que resultó interesante. Primero comenzaremos con el código, para quien esto es irrelevante, se pueden omitir las primeras secciones.

Recogida de datos


En la primera versión del analizador, solo se tuvo en cuenta el número de vistas, comentarios y la calificación de los artículos. Esto ya es bueno, pero no le permite hacer consultas más complejas. Es hora de analizar las secciones temáticas del sitio, esto le permitirá hacer una investigación bastante interesante, por ejemplo, ver cómo la popularidad de la sección "C ++" ha cambiado durante varios años.

Se ha mejorado el analizador del artículo, ahora devuelve los centros a los que pertenece el artículo, así como el apodo del autor y su calificación (aquí también puede hacer muchas cosas interesantes, pero esto más adelante). Los datos se guardan en un archivo csv de aproximadamente el siguiente tipo:

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

Obtenga una lista de los principales centros temáticos del sitio.

 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ón find_between y la clase Str resaltan una línea entre dos etiquetas, las usé antes . Los centros temáticos están marcados con "*", para que sean fáciles de resaltar, también puede descomentar las líneas correspondientes para obtener secciones de otras categorías.

En la salida de la función get_hubs, obtenemos una lista bastante impresionante, que guardamos como diccionario. Cito especialmente la lista completa para que se pueda estimar su volumen.

 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 modo de comparación, las secciones geektimes parecen más modestas:

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

Del mismo modo, los centros restantes se guardaron. Ahora es fácil escribir una función que devuelva el resultado, el artículo hace referencia a geektimes o a un centro de perfiles.

 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 

Se hicieron funciones similares para otras secciones ("desarrollo", "administración", etc.).

Procesamiento


Es hora de comenzar el análisis. Cargamos el conjunto de datos y procesamos los datos de los centros.

 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) 

Ahora podemos agrupar los datos por día y mostrar el número de publicaciones por diferentes centros.

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

Muestre la cantidad de artículos publicados con Matplotlib:



Dividí los artículos "geektimes" y "geektimes only" en el gráfico, porque un artículo puede pertenecer a ambas secciones simultáneamente (por ejemplo, "DIY" + "microcontroladores" + "C ++"). Con la designación "perfil", destaqué los artículos de perfil del sitio, aunque es posible que el término inglés perfil no sea del todo correcto para esto.

En la parte anterior, preguntamos sobre el "efecto geektimes" asociado con el cambio en las reglas para pagar artículos por geektimes de este verano. Derivamos artículos geektimes separados:

 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 resultado es interesante. La proporción aproximada de vistas de artículos geektimes al total en algún lugar alrededor de 1: 5. Pero si el número total de visitas fluctuaba notablemente, la visualización de artículos "entretenidos" se mantuvo aproximadamente al mismo nivel.



También puede observar que, sin embargo, el número total de vistas de artículos en la sección "geektimes" después de cambiar las reglas disminuyó, pero "a simple vista", en no más del 5% de los valores totales.

Es interesante ver el número promedio de visitas por artículo:



Para los artículos "entretenidos", es aproximadamente un 40% superior al promedio. Esto probablemente no sea sorprendente. El fracaso a principios de abril no está claro para mí, tal vez lo fue, o es algún tipo de error de análisis, o tal vez uno de los autores geektime se fue de vacaciones;).

Por cierto, en el gráfico hay dos picos más notables en el número de vistas de artículos: vacaciones de año nuevo y mayo.

Hubs


Pasemos al análisis prometido de los centros. Mostraremos los 20 centros principales según la cantidad de vistas:

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

Resultado:



Sorprendentemente, el centro de "Seguridad de la información" resultó ser el más popular en términos de visualización, también "Programación" y "Ciencia popular" se encuentran entre los 5 principales líderes.

Antitope toma Gtk y Cocoa.



Te diré un secreto, los principales centros también se pueden ver aquí , aunque la cantidad de vistas no se muestra allí.

Calificación


Y finalmente, la calificación prometida. Usando los datos del análisis de los centros, podemos mostrar los artículos más populares sobre los centros más populares para este año 2019.

Seguridad de la información


Programacion


Ciencia popular


Carrera


Legislación en TI


Desarrollo web


GTK

Y finalmente, para no ofender a nadie, le daré la calificación del centro "gtk" menos visitado. En él, un artículo fue publicado durante el año, también ocupa "automáticamente" la primera línea de la calificación.


Conclusión


No habrá conclusión. Disfruta leyendo a todos.

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


All Articles