Хабрастатистика: истражување на најмногу и најмалку посетените делови на страницата

Еј Хабр.

В претходниот дел Сообраќајот на Хабр беше анализиран според главните параметри - бројот на статии, нивните погледи и оценки. Сепак, прашањето за популарноста на деловите на страницата остана неиспитано. Стана интересно да се погледне ова подетално и да се најдат најпопуларните и најнепопуларните центри. Конечно, ќе го разгледам ефектот geektimes подетално, завршувајќи со нов избор на најдобри статии врз основа на нови рангирања.

Хабрастатистика: истражување на најмногу и најмалку посетените делови на страницата

За оние кои се заинтересирани за тоа што се случи, продолжението е под рез.

Уште еднаш да потсетам дека статистиката и рејтингот не се официјални, немам никакви инсајдерски информации. Исто така, не е гарантирано дека не сум згрешил некаде или нешто пропуштив. Но, сепак, мислам дека испадна интересно. Прво ќе започнеме со кодот, оние кои не се заинтересирани за ова, можат да ги прескокнат првите делови.

Собирање на податоци

Во првата верзија на парсерот беа земени предвид само бројот на прегледи, коментари и оценки на написите. Ова е веќе добро, но не ви дозволува да поставувате посложени прашања. Време е да ги анализирате тематските делови на страницата, ова ќе ви овозможи да направите доста интересно истражување, на пример, да видите како се промени популарноста на делот „C++“ во текот на неколку години.

Парсерот на статии е подобрен, сега ги враќа центрите на кои припаѓа статијата, како и прекарот на авторот и неговиот рејтинг (и тука може да се направат многу интересни работи, но тоа ќе дојде подоцна). Податоците се зачувани во CSV-датотека која изгледа вака:

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

Ќе добиеме список на главните тематски центри на страницата.

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 и класата Str избираат низа помеѓу две ознаки, јас ги користев порано. Тематските центри се означени со „*“ за да можат лесно да се идентификуваат, а исто така можете да ги откоментирате соодветните линии за да добиете делови од други категории.

Излезот од функцијата get_hubs е прилично импресивна листа, која ја зачувуваме како речник. Конкретно го прикажувам списокот во целост за да можете да го процените неговиот волумен.

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

За споредба, секциите за geektimes изгледаат поскромно:

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

Останатите центри беа зачувани на ист начин. Сега е лесно да се напише функција која го враќа резултатот, без разлика дали статијата припаѓа на geektimes или на центар на профилот.

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

Слични функции беа направени и за други делови („развој“, „администрација“ итн.).

Обработка

Време е да почнеме да анализираме. Ние ја вчитуваме базата на податоци и ги обработуваме податоците од центарот.

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)

Сега можеме да ги групираме податоците по ден и да го прикажеме бројот на публикации за различни центри.

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

Го прикажуваме бројот на објавени статии користејќи Matplotlib:

Хабрастатистика: истражување на најмногу и најмалку посетените делови на страницата

Ги поделив написите „geektimes“ и „geektimes only“ во табелата, затоа што Статијата може да припаѓа на двата дела во исто време (на пример, „DIY“ + „микроконтролери“ + „C++“). Ја користев ознаката „профил“ за да ги истакнам написите на профилот на страницата, иако можеби англискиот термин профил за ова не е сосема точен.

Во претходниот дел тие прашаа за „ефектот geektimes“ поврзан со промената на правилата за плаќање на написите за geektimes ова лето. Ајде да ги прикажеме написите на geektimes одделно:

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

Резултатот е интересен. Приближниот сооднос на прегледите на статиите на geektimes со вкупниот број е некаде околу 1:5. Но, додека вкупниот број на прегледи значително флуктуираше, гледаноста на написите за „забава“ остана на приближно исто ниво.

Хабрастатистика: истражување на најмногу и најмалку посетените делови на страницата

Можете исто така да забележите дека вкупниот број на прегледи на статии во делот „geektimes“ сепак падна по промената на правилата, но „на око“, за не повеќе од 5% од вкупните вредности.

Интересно е да се погледне просечниот број на прегледи по статија:

Хабрастатистика: истражување на најмногу и најмалку посетените делови на страницата

За написите за „забава“ тоа е околу 40% над просекот. Ова веројатно не е изненадувачки. Неуспехот на почетокот на април ми е неразбирлив, можеби тоа е она што се случило, или е некаква грешка при парсирање или можеби некој од авторите на geektimes отишол на одмор ;).

Патем, графикот покажува уште два забележливи врвови во бројот на прегледи на статии - новогодишните и мајските празници.

Хабови

Да преминеме на ветената анализа на хабовите. Ајде да ги наведеме најдобрите 20 центри по број на прегледи:

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

Резултатот е:

Хабрастатистика: истражување на најмногу и најмалку посетените делови на страницата

Изненадувачки, најпопуларниот центар во однос на погледите беше „Информациската безбедност“ меѓу првите 5 лидери, исто така, „Програмирање“ и „Популарна наука“.

Антитоп ги зафаќа Гтк и Какао.

Хабрастатистика: истражување на најмногу и најмалку посетените делови на страницата

Ќе ти кажам една тајна, се гледаат и врвните хабови тука, иако бројот на прегледи не е прикажан таму.

Оценка

И конечно, ветениот рејтинг. Користејќи ги податоците за анализа на центри, можеме да ги прикажеме најпопуларните статии за најпопуларните центри за оваа 2019 година.

Безбедност на информации

Програмирање

Популарна наука

кариера

Законодавство во ИТ

веб развој

GTK

И, конечно, за никој да не се навреди, ќе дадам оцена на најмалку посетениот центар „gtk“. Во рок од една година беше објавен еден Написот, кој исто така „автоматски“ ја зазема првата линија на рејтингот.

Заклучок

Заклучок нема да има. Среќно читање на сите.

Извор: www.habr.com

Додадете коментар