Habrastatistiek: het verkennen van de meest en minst bezochte delen van de site

Hé Habr.

В vorig deel Het verkeer van Habr werd geanalyseerd op basis van de belangrijkste parameters: het aantal artikelen, hun meningen en beoordelingen. De kwestie van de populariteit van de sitesecties bleef echter ononderzocht. Het werd interessant om hier meer in detail naar te kijken en de populairste en meest impopulaire hubs te vinden. Ten slotte ga ik dieper in op het geektimes-effect, eindigend met een nieuwe selectie van de beste artikelen op basis van nieuwe ranglijsten.

Habrastatistiek: het verkennen van de meest en minst bezochte delen van de site

Voor degenen die geïnteresseerd zijn in wat er is gebeurd, het vervolg ligt onder de noemer.

Ik wil u er nogmaals aan herinneren dat de statistieken en beoordelingen niet officieel zijn, ik beschik niet over voorkennis. Het is ook niet gegarandeerd dat ik niet ergens een fout heb gemaakt of iets heb gemist. Maar toch denk ik dat het interessant is geworden. We beginnen eerst met de code; wie hier niet in geïnteresseerd is, kan de eerste secties overslaan.

Gegevensverzameling

In de eerste versie van de parser werd alleen rekening gehouden met het aantal views, commentaren en artikelbeoordelingen. Dit is al goed, maar u kunt er geen complexere vragen mee stellen. Het is tijd om de thematische secties van de site te analyseren; hierdoor kun je behoorlijk interessant onderzoek doen, bijvoorbeeld om te zien hoe de populariteit van de sectie "C++" in de loop van de jaren is veranderd.

De artikelparser is verbeterd en retourneert nu de hubs waartoe het artikel behoort, evenals de bijnaam van de auteur en zijn beoordeling (hier kunnen ook veel interessante dingen worden gedaan, maar dat komt later). De gegevens worden opgeslagen in een csv-bestand dat er ongeveer zo uitziet:

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

We ontvangen een lijst met de belangrijkste thematische hubs van de 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)

De find_between-functie en de Str-klasse selecteren een string tussen twee tags, ik heb ze gebruikt vroeger. Thematische hubs zijn gemarkeerd met een "*", zodat ze gemakkelijk kunnen worden gemarkeerd, en u kunt ook de bijbehorende regels verwijderen om secties van andere categorieën te bekijken.

De uitvoer van de get_hubs-functie is een redelijk indrukwekkende lijst, die we opslaan als een woordenboek. Ik presenteer de lijst specifiek in zijn geheel, zodat u de omvang ervan kunt inschatten.

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

Ter vergelijking: de geektimes-secties zien er bescheidener uit:

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

De overige hubs werden op dezelfde manier bewaard. Nu is het eenvoudig om een ​​functie te schrijven die het resultaat retourneert, ongeacht of het artikel tot geektimes of een profielhub behoort.

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

Soortgelijke functies zijn gemaakt voor andere secties (“ontwikkeling”, “administratie”, enz.).

Verwerking

Het is tijd om te beginnen met analyseren. We laden de dataset en verwerken de hubgegevens.

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)

Nu kunnen we de gegevens per dag groeperen en het aantal publicaties voor verschillende hubs weergeven.

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

We geven het aantal gepubliceerde artikelen weer met Matplotlib:

Habrastatistiek: het verkennen van de meest en minst bezochte delen van de site

Ik heb de artikelen “geektimes” en “geektimes only” in het diagram verdeeld, omdat Een artikel kan tegelijkertijd tot beide secties behoren (bijvoorbeeld “DIY” + “microcontrollers” + “C++”). Ik heb de aanduiding “profiel” gebruikt om profielartikelen op de site onder de aandacht te brengen, hoewel de Engelse term profiel hiervoor misschien niet helemaal correct is.

In het vorige deel vroegen we naar het “geektimes-effect” dat gepaard gaat met de wijziging in de betalingsregels voor artikelen voor geektimes vanaf deze zomer. Laten we de geektimes-artikelen afzonderlijk weergeven:

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

Het resultaat is interessant. De geschatte verhouding tussen weergaven van geektimes-artikelen en het totaal ligt ergens rond de 1:5. Maar hoewel het totale aantal views merkbaar fluctueerde, bleef het aantal bekeken ‘entertainment’-artikelen ongeveer op hetzelfde niveau.

Habrastatistiek: het verkennen van de meest en minst bezochte delen van de site

Je kunt ook merken dat het totale aantal weergaven van artikelen in de sectie “geektimes” nog steeds daalde na het wijzigen van de regels, maar “op het oog”, met niet meer dan 5% van de totale waarden.

Interessant is om te kijken naar het gemiddelde aantal views per artikel:

Habrastatistiek: het verkennen van de meest en minst bezochte delen van de site

Voor ‘entertainment’-artikelen ligt dit ongeveer 40% boven het gemiddelde. Dit is waarschijnlijk niet verrassend. De mislukking begin april is mij onduidelijk, misschien is dat wat er is gebeurd, of is het een soort parseerfout, of misschien is een van de geektimes-auteurs op vakantie geweest ;).

Overigens laat de grafiek nog twee opvallende pieken zien in het aantal artikelweergaven: de nieuwjaars- en meivakantie.

Naven

Laten we verder gaan met de beloofde analyse van hubs. Laten we de 20 beste hubs opsommen op basis van het aantal weergaven:

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

Resultaat:

Habrastatistiek: het verkennen van de meest en minst bezochte delen van de site

Verrassend genoeg was ‘Informatiebeveiliging’ het meest populaire knooppunt in termen van opvattingen; tot de top 5 van leiders behoorden ook ‘Programmering’ en ‘Populaire wetenschap’.

Antitop bezet Gtk en Cocoa.

Habrastatistiek: het verkennen van de meest en minst bezochte delen van de site

Ik zal je een geheimpje verklappen, de tophubs zijn ook te zien hier, hoewel het aantal views daar niet wordt weergegeven.

Waardering

En tot slot de beloofde beoordeling. Met behulp van hub-analysegegevens kunnen we de populairste artikelen voor de populairste hubs van dit jaar 2019 weergeven.

Informatiebeveiliging

Programmering

Populaire wetenschap

carrière

Wetgeving in de IT

webontwikkeling

GTK

En tot slot, zodat niemand beledigd is, zal ik de beoordeling van de minst bezochte hub "gtk" geven. Binnen een jaar was het gepubliceerd één Het artikel, dat ook “automatisch” de eerste regel van de beoordeling bezet.

Conclusie

Er zal geen conclusie zijn. Veel leesplezier allemaal.

Bron: www.habr.com

Voeg een reactie