Habrastatistics: udforske de mest og mindst besøgte dele af webstedet

Hej Habr.

В forrige del Habrs trafik blev analyseret i henhold til hovedparametrene - antallet af artikler, deres synspunkter og vurderinger. Spørgsmålet om webstedssektionernes popularitet forblev dog uundersøgt. Det blev interessant at se nærmere på dette og finde de mest populære og mest upopulære hubs. Til sidst vil jeg se på geektimes-effekten mere detaljeret, og slutter med et nyt udvalg af de bedste artikler baseret på nye placeringer.

Habrastatistics: udforske de mest og mindst besøgte dele af webstedet

For dem, der er interesserede i, hvad der skete, er fortsættelsen under skæring.

Lad mig igen minde dig om, at statistikken og vurderingerne ikke er officielle, jeg har ingen insider-oplysninger. Det er heller ikke garanteret, at jeg ikke lavede en fejl et eller andet sted eller gik glip af noget. Men alligevel synes jeg, det er blevet interessant. Vi starter med koden først; de, der ikke er interesserede i dette, kan springe de første afsnit over.

Dataindsamling

I den første version af parseren blev der kun taget hensyn til antallet af visninger, kommentarer og artikelvurderinger. Dette er allerede godt, men det giver dig ikke mulighed for at lave mere komplekse forespørgsler. Det er tid til at analysere de tematiske sektioner af webstedet; dette vil give dig mulighed for at lave ret interessant forskning, for eksempel se, hvordan populariteten af ​​"C++"-sektionen har ændret sig over flere år.

Artikelparseren er blevet forbedret, nu returnerer den de hubs, som artiklen tilhører, samt forfatterens kaldenavn og hans vurdering (mange interessante ting kan også gøres her, men det kommer senere). Dataene gemmes i en csv-fil, der ser sådan ud:

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

Vi vil modtage en liste over de vigtigste tematiske hubs på webstedet.

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)

Find_between-funktionen og Str-klassen vælger en streng mellem to tags, jeg brugte dem tidligere. Tematiske hubs er markeret med et "*", så de nemt kan fremhæves, og du kan også fjerne kommentarer til de tilsvarende linjer for at få sektioner af andre kategorier.

Outputtet af get_hubs-funktionen er en ret imponerende liste, som vi gemmer som en ordbog. Jeg præsenterer specifikt listen i sin helhed, så du kan vurdere dens 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'}

Til sammenligning ser nørdetidssektionerne mere beskedne ud:

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 resterende nav blev bevaret på samme måde. Nu er det nemt at skrive en funktion, der returnerer resultatet, uanset om artiklen tilhører geektimes eller en 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

Lignende funktioner blev lavet for andre sektioner ("udvikling", "administration" osv.).

Behandling

Det er tid til at begynde at analysere. Vi indlæser datasættet og behandler hub-dataene.

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 kan vi gruppere dataene efter dag og vise antallet af publikationer for forskellige 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()

Vi viser antallet af publicerede artikler ved hjælp af Matplotlib:

Habrastatistics: udforske de mest og mindst besøgte dele af webstedet

Jeg delte artiklerne "nørdetider" og "kun nørdetider" i diagrammet, fordi En artikel kan tilhøre begge sektioner på samme tid (f.eks. "DIY" + "microcontrollers" + "C++"). Jeg brugte betegnelsen "profil" til at fremhæve profilartikler på siden, selvom det engelske udtryk profile for dette måske ikke er helt korrekt.

I den foregående del spurgte vi til "nørdetidseffekten" forbundet med ændringen af ​​betalingsreglerne for artikler til nørdetider, der starter til sommer. Lad os vise geektimes-artiklerne separat:

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

Resultatet er interessant. Det omtrentlige forhold mellem visninger af geektimes-artikler og totalen er et sted omkring 1:5. Men mens det samlede antal visninger svingede mærkbart, forblev visningen af ​​"underholdnings"-artikler på nogenlunde samme niveau.

Habrastatistics: udforske de mest og mindst besøgte dele af webstedet

Du kan også bemærke, at det samlede antal visninger af artikler i "geektimes"-sektionen stadig faldt efter ændring af reglerne, men "ved eye", med højst 5% af de samlede værdier.

Det er interessant at se på det gennemsnitlige antal visninger pr. artikel:

Habrastatistics: udforske de mest og mindst besøgte dele af webstedet

For "underholdnings"-artikler er det omkring 40 % over gennemsnittet. Dette er nok ikke overraskende. Fejlen i begyndelsen af ​​april er uklar for mig, måske er det det, der skete, eller det er en form for parsefejl, eller måske tog en af ​​geektime-forfatterne på ferie ;).

I øvrigt viser grafen to mere mærkbare toppe i antallet af visninger af artikler - nytårs- og majferien.

Hubs

Lad os gå videre til den lovede analyse af hubs. Lad os liste de 20 bedste hubs efter antal visninger:

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

Resultat:

Habrastatistics: udforske de mest og mindst besøgte dele af webstedet

Overraskende nok var den mest populære hub med hensyn til synspunkter "Informationssikkerhed"; top 5 ledere inkluderede også "Programmering" og "Populær videnskab".

Antitop indtager Gtk og Kakao.

Habrastatistics: udforske de mest og mindst besøgte dele af webstedet

Jeg skal fortælle dig en hemmelighed, de øverste hubs kan også ses her, selvom antallet af visninger ikke vises der.

Rating

Og endelig den lovede vurdering. Ved hjælp af hub-analysedata kan vi vise de mest populære artikler for de mest populære hubs for dette år 2019.

Informationssikkerhed

Programmering

Populær Videnskab

karriere

Lovgivning inden for IT

web-udvikling

GTK

Og endelig, så ingen bliver fornærmet, vil jeg give vurderingen af ​​den mindst besøgte hub "gtk". Inden for et år blev den udgivet одна Artiklen, som også "automatisk" indtager første linje i vurderingen.

Konklusion

Der bliver ingen konklusion. God læsning alle sammen.

Kilde: www.habr.com

Tilføj en kommentar