Habrastatistika: saytın ən çox və ən az ziyarət edilən hissələrini araşdırmaq

Hey Habr.

В əvvəlki hissə Habrın trafiki əsas parametrlərə - məqalələrin sayına, baxışlarına və reytinqlərinə görə təhlil edilib. Bununla belə, sayt bölmələrinin populyarlığı məsələsi araşdırılmamış qalır. Buna daha ətraflı baxmaq və ən populyar və ən populyar olmayan mərkəzləri tapmaq maraqlı oldu. Nəhayət, geektimes effektinə daha ətraflı baxacağam və yeni reytinqlərə əsaslanan ən yaxşı məqalələrin yeni seçimi ilə bitirəcəyəm.

Habrastatistika: saytın ən çox və ən az ziyarət edilən hissələrini araşdırmaq

Baş verənlərlə maraqlananlar üçün davamı kəsilməkdədir.

Bir daha nəzərinizə çatdırım ki, statistika və reytinqlər rəsmi deyil, mənim heç bir insayder məlumatım yoxdur. Həmçinin haradasa səhv etmədiyimə və ya nəyisə qaçırmadığıma zəmanət verilmir. Amma yenə də düşünürəm ki, maraqlı oldu. Əvvəlcə koddan başlayacağıq, bununla maraqlanmayanlar ilk bölmələri atlaya bilərlər.

Məlumatların toplanması

Parserin ilk versiyasında yalnız baxış sayı, şərhlər və məqalə reytinqləri nəzərə alınıb. Bu, artıq yaxşıdır, lakin daha mürəkkəb sorğular etməyə imkan vermir. Saytın tematik bölmələrini təhlil etməyin vaxtı gəldi, bu sizə olduqca maraqlı araşdırmalar aparmağa imkan verəcək, məsələn, “C++” bölməsinin populyarlığının bir neçə il ərzində necə dəyişdiyini görə bilərsiniz.

Məqalənin təhlili təkmilləşdirildi, indi məqalənin aid olduğu mərkəzləri, həmçinin müəllifin ləqəbini və onun reytinqini qaytarır (burada da çox maraqlı şeylər edilə bilər, lakin bu daha sonra gələcək). Məlumat bu kimi görünən csv faylında saxlanılır:

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

Saytın əsas tematik mərkəzlərinin siyahısını alacağıq.

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 funksiyası və Str sinfi iki teq arasında sətir seçir, mən onlardan istifadə etdim əvvəllər. Tematik mərkəzlər "*" işarəsi ilə qeyd olunur ki, onlar asanlıqla vurğulana bilsinlər və siz həmçinin digər kateqoriyaların bölmələrini əldə etmək üçün müvafiq sətirləri şərhdən çıxara bilərsiniz.

get_hubs funksiyasının çıxışı lüğət kimi saxladığımız kifayət qədər təsir edici siyahıdır. Mən konkret olaraq siyahını tam şəkildə təqdim edirəm ki, onun həcmini təxmin edə biləsiniz.

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

Müqayisə üçün geektimes bölmələri daha təvazökar görünür:

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

Qalan qovşaqlar da eyni şəkildə qorunub saxlanılmışdır. İndi məqalənin geektimes və ya profil mərkəzinə aid olub-olmamasından asılı olmayaraq nəticəni qaytaran funksiya yazmaq asandır.

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

Oxşar funksiyalar digər bölmələr üçün də həyata keçirilmişdir (“inkişaf”, “idarəetmə” və s.).

Emal

Analiz etməyə başlamağın vaxtıdır. Biz verilənlər bazasını yükləyirik və hub məlumatlarını emal edirik.

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)

İndi biz məlumatları günə görə qruplaşdıra və müxtəlif mərkəzlər üçün nəşrlərin sayını göstərə bilərik.

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 istifadə edərək dərc edilmiş məqalələrin sayını göstəririk:

Habrastatistika: saytın ən çox və ən az ziyarət edilən hissələrini araşdırmaq

Qrafikdə “geektimes” və “yalnız geektimes” məqalələrini böldüm, çünki Məqalə eyni anda hər iki bölməyə aid ola bilər (məsələn, “DIY” + “mikrokontrollerlər” + “C++”). Saytdakı profil məqalələrini vurğulamaq üçün "profil" təyinatından istifadə etdim, baxmayaraq ki, bunun üçün ingilis dilində profil termini tamamilə düzgün deyil.

Əvvəlki hissədə biz bu yaydan geektimes üçün məqalələr üçün ödəniş qaydalarının dəyişməsi ilə bağlı “geektimes effekti” haqqında soruşmuşduq. Geektimes məqalələrini ayrıca göstərək:

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

Nəticə maraqlıdır. Geektimes məqalələrinin ümumi sayına təxmini nisbəti 1:5 civarındadır. Baxışların ümumi sayı nəzərəçarpacaq dərəcədə dəyişsə də, “əyləncəli” məqalələrə baxış təxminən eyni səviyyədə qaldı.

Habrastatistika: saytın ən çox və ən az ziyarət edilən hissələrini araşdırmaq

Həmçinin qeyd edə bilərsiniz ki, "geektimes" bölməsindəki məqalələrin ümumi baxış sayı qaydaları dəyişdirildikdən sonra yenə də azalıb, lakin "gözlə" ümumi dəyərlərin 5% -dən çox deyil.

Hər məqaləyə orta baxış sayına baxmaq maraqlıdır:

Habrastatistika: saytın ən çox və ən az ziyarət edilən hissələrini araşdırmaq

“Əyləncəli” məqalələr üçün bu, orta hesabla təxminən 40% yuxarıdır. Bu, yəqin ki, təəccüblü deyil. Aprelin əvvəlindəki uğursuzluq mənim üçün aydın deyil, bəlkə də belə oldu, ya da bir növ təhlil xətası və ya geektimes müəlliflərindən biri tətilə getdi;).

Yeri gəlmişkən, qrafik məqaləyə baxış sayında daha iki nəzərə çarpan zirvəni - Yeni il və May bayramlarını göstərir.

Qovşaqlar

Gəlin hubların vəd edilmiş təhlilinə keçək. Baxışların sayına görə ilk 20 mərkəzi sadalayaq:

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

Nəticə:

Habrastatistika: saytın ən çox və ən az ziyarət edilən hissələrini araşdırmaq

Təəccüblüdür ki, baxış baxımından ən populyar mərkəz “İnformasiya təhlükəsizliyi” olub, ilk 5 liderə “Proqramlaşdırma” və “Populyar elm” də daxil olub.

Antitop Gtk və Kakaonu tutur.

Habrastatistika: saytın ən çox və ən az ziyarət edilən hissələrini araşdırmaq

Sizə bir sirr deyim, üst hublar da görünə bilər burada, baxmayaraq ki, baxış sayı orada göstərilmir.

Qiymət

Və nəhayət, vəd edilmiş reytinq. Hub təhlili məlumatlarından istifadə edərək, bu il 2019-cu il üçün ən populyar mərkəzlər üçün ən populyar məqalələri göstərə bilərik.

İnformasiya təhlükəsizliyi

Proqramlaşdırma

Populyar elm

karyera

İT sahəsində qanunvericilik

Veb inkişafı

GTK

Və nəhayət, heç kimin inciməməsi üçün ən az ziyarət edilən "gtk" mərkəzinin reytinqini verəcəyəm. Bir il ərzində nəşr olundu одна Reytinqin birinci cərgəsini həm də “avtomatik” olan məqalə tutur.

Nəticə

Heç bir nəticə olmayacaq. Hər kəsə xoş oxu.

Mənbə: www.habr.com

Добавить комментарий