Habrastatistics: explorando as seções mais e menos visitadas do site

Oi Habr.

Na parte anterior , a participação de Habr foi analisada pelos principais parâmetros - o número de artigos, suas opiniões e classificações. No entanto, a questão da popularidade das seções do site não foi considerada. Tornou-se interessante examinar isso com mais detalhes e encontrar os hubs mais populares e menos populares. Por fim, examinarei o “efeito geektimes” com mais detalhes e, no final, os leitores receberão uma nova seleção dos melhores artigos sobre as novas classificações.



Quem se importa com o que aconteceu, continuou sob o corte.

Recordo mais uma vez que estatísticas e classificações não são oficiais, não tenho nenhuma informação privilegiada. Também não é garantido que não me enganei em algum lugar ou que não perdi nada. Mas ainda assim, acho que ficou interessante. Começaremos com o código primeiro, para quem isso é irrelevante, as primeiras seções podem ser ignoradas.

Coleta de dados


Na primeira versão do analisador, apenas o número de visualizações, comentários e a classificação dos artigos foram levados em consideração. Isso já é bom, mas não permite que você faça consultas mais complexas. É hora de analisar as seções temáticas do site. Isso permitirá estudos interessantes, por exemplo, para ver como a popularidade da seção "C ++" mudou ao longo de vários anos.

O analisador de artigos foi aprimorado, agora retorna os hubs aos quais o artigo pertence, assim como o apelido do autor e sua classificação (aqui você também pode fazer muitas coisas interessantes, mas isso mais tarde). Os dados são salvos em um arquivo csv aproximadamente do seguinte 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 ... 

Obtenha uma lista dos principais hubs temáticos do 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) 

A função find_between e a classe Str destacam uma linha entre duas tags, usei-as anteriormente . Os hubs temáticos estão marcados com "*", para facilitar o destaque, você também pode descomentar as linhas correspondentes para obter seções de outras categorias.

Na saída da função get_hubs, obtemos uma lista bastante impressionante, que salvamos como um dicionário. Eu cito especialmente a lista inteira para que seu volume possa ser estimado.

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

Para comparação, as seções dos geeks parecem mais 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'} 

Da mesma forma, os hubs restantes foram salvos. Agora é fácil escrever uma função que retorne o resultado, o artigo refere-se a tempos de geek ou a um hub de perfis.

 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 

Funções semelhantes foram feitas para outras seções (“desenvolvimento”, “administração” etc.).

Processamento


É hora de começar a análise. Carregamos o conjunto de dados e processamos os dados dos 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) 

Agora podemos agrupar os dados por dia e exibir o número de publicações por diferentes 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() 

Exiba o número de artigos publicados usando o Matplotlib:



Dividi os artigos “geektimes” e “geektimes only” no gráfico, porque um artigo pode pertencer às duas seções simultaneamente (por exemplo, “DIY” + “microcontroladores” + “C ++”). Com a designação “perfil”, destaquei os artigos de perfil do site, embora seja possível que o termo inglês profile não esteja totalmente correto para isso.

Na parte anterior, perguntamos sobre o "efeito geektimes" associado à mudança nas regras para pagamento de artigos por geektimes a partir deste verão. Derivamos artigos separados para os geeks:

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

O resultado é interessante. A proporção aproximada de visualizações de artigos geektimes em relação ao total em torno de 1: 5. Mas se o número total de visualizações flutuasse visivelmente, a visualização de artigos "divertidos" era mantida aproximadamente no mesmo nível.



Você também pode observar que o número total de visualizações de artigos na seção "geektimes" depois de alterar as regras ainda caiu, mas "a olho nu", não mais que 5% dos valores totais.

É interessante ver o número médio de visualizações por artigo:



Para artigos "divertidos", é cerca de 40% acima da média. Provavelmente isso não é surpreendente. O fracasso no início de abril não está claro para mim, talvez tenha sido, ou seja algum tipo de erro de análise, ou talvez um dos geeks dos autores tenha saído de férias;).

A propósito, no gráfico há mais dois picos perceptíveis no número de visualizações de artigos - feriados de Ano Novo e Maio.

Hubs


Vamos seguir para a análise prometida dos hubs. Exibiremos os 20 principais hubs pelo número de visualizações:

 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:



Surpreendentemente, o hub de "Segurança da informação" acabou sendo o mais popular em termos de visualização, também "Programação" e "Ciência popular" estão entre os 5 principais líderes.

O antítopo toma Gtk e cacau.



Vou lhe contar um segredo, os principais hubs também podem ser vistos aqui , embora o número de visualizações não seja mostrado lá.

Classificação


E, finalmente, a classificação prometida. Usando os dados da análise de hubs, podemos exibir os artigos mais populares nos hubs mais populares para este ano de 2019.

Segurança da informação


Programação


Ciência popular


Carreira profissional


Legislação em TI


Desenvolvimento Web


GTK

E, finalmente, para não ofender ninguém, darei a classificação do hub menos visitado "gtk". Nele, um artigo foi publicado ao longo do ano, e também “automaticamente” ocupa a primeira linha da classificação.


Conclusão


Não haverá conclusão. Gostam de ler para todos.

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


All Articles