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

Hola Habr.

В parte anterior El tráfico de Habr se analizó según los parámetros principales: el número de artículos, sus vistas y valoraciones. Sin embargo, la cuestión de la popularidad de las secciones del sitio quedó sin examinar. Se volvió interesante analizar esto con más detalle y encontrar los centros más populares e impopulares. Finalmente, analizaré el efecto geektimes con más detalle, terminando con una nueva selección de los mejores artículos basada en nuevos rankings.

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

Para aquellos que estén interesados ​​en lo que pasó, la continuación está cortada.

Permítanme recordarles una vez más que las estadísticas y calificaciones no son oficiales, no tengo ninguna información privilegiada. Tampoco se garantiza que no cometí un error en alguna parte o que no me perdí algo. Pero aun así creo que resultó interesante. Comenzaremos primero con el código; aquellos que no estén interesados ​​en esto pueden saltarse las primeras secciones.

Сбор данных

En la primera versión del analizador, sólo se tuvo en cuenta el número de visitas, comentarios y valoraciones de los artículos. Esto ya está bien, pero no te permite hacer consultas más complejas. Es hora de analizar las secciones temáticas del sitio, esto le permitirá realizar investigaciones bastante interesantes, por ejemplo, ver cómo ha cambiado la popularidad de la sección "C++" a lo largo de varios años.

El analizador de artículos ha sido mejorado, ahora devuelve los centros a los que pertenece el artículo, así como el apodo del autor y su calificación (aquí también se pueden hacer muchas cosas interesantes, pero eso vendrá más adelante). Los datos se guardan en un archivo csv similar a este:

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

Recibiremos 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 seleccionan una cadena entre dos etiquetas, yo las usé más temprano. Los centros temáticos están marcados con un "*" para que se puedan resaltar fácilmente y también puedes descomentar las líneas correspondientes para obtener secciones de otras categorías.

El resultado de la función get_hubs es una lista bastante impresionante, que guardamos como un diccionario. Te presento específicamente la lista en su totalidad para que puedas 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'}

En comparación, las secciones de 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'}

Los centros restantes se conservaron de la misma forma. Ahora es fácil escribir una función que devuelva el resultado ya sea que el artículo pertenezca 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 crearon funciones similares para otras secciones (“desarrollo”, “administración”, etc.).

transformación

Es hora de empezar a analizar. Cargamos el conjunto de datos y procesamos los datos del centro.

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 la cantidad de publicaciones para 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()

Mostramos la cantidad de artículos publicados usando Matplotlib:

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

Dividí los artículos "geektimes" y "geektimes only" en el gráfico, porque Un artículo puede pertenecer a ambas secciones al mismo tiempo (por ejemplo, “DIY” + “microcontroladores” + “C++”). Utilicé la designación "perfil" para resaltar artículos de perfil en el sitio, aunque quizás el término en inglés perfil para esto no sea del todo correcto.

En la parte anterior preguntábamos por el “efecto geektimes” asociado al cambio en las reglas de pago de los artículos para geektimes a partir de este verano. Mostremos los artículos de geektimes por separado:

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 los artículos de geektimes con respecto al total es de alrededor de 1:5. Pero mientras el número total de visualizaciones fluctuó notablemente, la visualización de artículos de “entretenimiento” se mantuvo aproximadamente en el mismo nivel.

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

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

Es interesante observar el número medio de visitas por artículo:

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

En el caso de los artículos de “entretenimiento”, está aproximadamente un 40% por encima del promedio. Probablemente esto no sea sorprendente. No me queda claro el fallo de principios de abril, tal vez eso fue lo que pasó, o algún tipo de error de análisis, o tal vez uno de los autores del geektimes se fue de vacaciones ;).

Por cierto, el gráfico muestra dos picos más notables en el número de visitas a artículos: el Año Nuevo y las vacaciones de mayo.

Hubs

Pasemos al prometido análisis de los centros. Enumeremos los 20 centros principales por número 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:

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

Sorprendentemente, el centro más popular en términos de opiniones fue “Seguridad de la información”; entre los cinco primeros también se encuentran “Programación” y “Ciencia popular”.

Antitop ocupa Gtk y Cocoa.

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

Te contaré un secreto, los hubs superiores también se pueden ver. aquí, aunque allí no se muestra el número de visualizaciones.

Clasificación

Y por último, la calificación prometida. Utilizando los datos de análisis de centros, podemos mostrar los artículos más populares de los centros más populares para este año 2019.

Seguridad de la información

Программирование

Ciencia popular

carrera

Legislación en TI

desarrollo web

GTK

Y finalmente, para que nadie se ofenda, daré una calificación del centro “gtk” menos visitado. Al cabo de un año se publicó uno El artículo, que también “automáticamente” ocupa la primera línea de la calificación.

Conclusión

No habrá ninguna conclusión. Feliz lectura a todos.

Fuente: habr.com

Añadir un comentario