Habra istatistikleri: sitenin en çok ve en az ziyaret edilen bölümlerini keşfetme

Merhaba Habr.

В önceki bölüm Habr'ın trafiği ana parametrelere (makale sayısı, görüşler ve derecelendirmeler) göre analiz edildi. Ancak site bölümlerinin popülerliği konusu incelenmeden kaldı. Buna daha detaylı bakmak ve en popüler ve en popüler olmayan merkezleri bulmak ilginç hale geldi. Son olarak geektimes etkisine daha detaylı bakacağım ve yeni sıralamalara dayalı en iyi makalelerden oluşan yeni bir seçkiyle bitireceğim.

Habra istatistikleri: sitenin en çok ve en az ziyaret edilen bölümlerini keşfetme

Ne olduğunu merak edenler için devamı kesim altında.

İstatistiklerin ve derecelendirmelerin resmi olmadığını, içeriden herhangi bir bilgiye sahip olmadığımı bir kez daha hatırlatayım. Ayrıca bir yerde hata yapmadığımın ya da bir şeyi kaçırmadığımın da garantisi yok. Ama yine de ilginç olduğunu düşünüyorum. Önce kodla başlayacağız, bu konuyla ilgilenmeyenler ilk bölümleri atlayabilir.

Veri toplama

Ayrıştırıcının ilk sürümünde yalnızca görüntüleme sayısı, yorum sayısı ve makale derecelendirmeleri dikkate alınıyordu. Bu zaten iyidir ancak daha karmaşık sorgular yapmanıza izin vermez. Sitenin tematik bölümlerini analiz etme zamanı geldi; bu, oldukça ilginç araştırmalar yapmanıza, örneğin “C++” bölümünün popülaritesinin birkaç yıl içinde nasıl değiştiğini görmenize olanak tanıyacaktır.

Makale ayrıştırıcı iyileştirildi, artık makalenin ait olduğu merkezlerin yanı sıra yazarın takma adını ve derecelendirmesini de döndürüyor (burada da pek çok ilginç şey yapılabilir, ancak bu daha sonra gelecek). Veriler şuna benzeyen bir csv dosyasına kaydedilir:

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

Sitenin ana tematik merkezlerinin bir listesini alacağız.

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 işlevi ve Str sınıfı iki etiket arasında bir dize seçer, ben onları kullandım daha erken. Tematik merkezler kolayca vurgulanmaları için bir "*" ile işaretlenmiştir ve ayrıca diğer kategorilerin bölümlerine ulaşmak için ilgili satırların yorumlarını kaldırabilirsiniz.

get_hubs fonksiyonunun çıktısı, sözlük olarak kaydettiğimiz oldukça etkileyici bir listedir. Hacmini tahmin edebilmeniz için listeyi bütünüyle özellikle sunuyorum.

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

Karşılaştırma için geektimes bölümleri daha mütevazı görünüyor:

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

Geriye kalan göbekler de aynı şekilde korunmuştur. Artık makalenin geektimes'a mı yoksa bir profil merkezine mi ait olduğu sonucunu döndüren bir işlev yazmak çok kolay.

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

Diğer bölümler (“geliştirme”, “yönetim” vb.) için de benzer işlevler yapıldı.

işleme

Analize başlamanın zamanı geldi. Veri setini yüklüyoruz ve hub verilerini işliyoruz.

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)

Artık verileri güne göre gruplandırabilir ve farklı merkezler için yayın sayısını görüntüleyebiliriz.

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'i kullanarak yayınlanan makalelerin sayısını görüntülüyoruz:

Habra istatistikleri: sitenin en çok ve en az ziyaret edilen bölümlerini keşfetme

Grafikte "geektimes" ve "sadece geektimes" makalelerini böldüm çünkü Bir makale aynı anda her iki bölüme de ait olabilir (örneğin, “Kendin Yap” + “mikrodenetleyiciler” + “C++”). Sitedeki profil makalelerini vurgulamak için "profil" tanımını kullandım, ancak bunun için İngilizce profil terimi tamamen doğru olmayabilir.

Önceki bölümde, bu yazdan itibaren geektimes makaleleri için ödeme kurallarında yapılan değişiklikle bağlantılı “geektimes etkisi”ni sormuştuk. Geektimes makalelerini ayrı ayrı görüntüleyelim:

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

Sonuç ilginç. Geektimes makalelerinin görüntülemelerinin toplam sayıya oranı yaklaşık 1:5 civarındadır. Ancak toplam görüntüleme sayısı gözle görülür şekilde dalgalanırken, "eğlence" içerikli makalelerin görüntülenmesi yaklaşık olarak aynı seviyede kaldı.

Habra istatistikleri: sitenin en çok ve en az ziyaret edilen bölümlerini keşfetme

Ayrıca, "geektimes" bölümündeki makalelerin toplam görüntüleme sayısının, kurallar değiştirildikten sonra da düştüğünü, ancak "gözle" toplam değerlerin en fazla %5'i kadar düştüğünü fark edebilirsiniz.

Makale başına ortalama görüntüleme sayısına bakmak ilginçtir:

Habra istatistikleri: sitenin en çok ve en az ziyaret edilen bölümlerini keşfetme

“Eğlence” makaleleri için bu ortalamanın yaklaşık %40 üzerindedir. Bu muhtemelen şaşırtıcı değil. Nisan ayının başındaki başarısızlık benim için net değil, belki de olan budur, ya da bir tür ayrıştırma hatasıdır ya da belki de geektimes yazarlarından biri tatile gitmiştir ;).

Bu arada, grafik, makalelerin görüntülenme sayısında gözle görülür iki zirveyi daha gösteriyor - Yeni Yıl ve Mayıs tatilleri.

Hub'lar

Hub'ların vaat edilen analizine geçelim. En iyi 20 hub'ı izlenme sayısına göre sıralayalım:

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

Sonuç:

Habra istatistikleri: sitenin en çok ve en az ziyaret edilen bölümlerini keşfetme

Şaşırtıcı bir şekilde, görüşler açısından en popüler merkez "Bilgi Güvenliği" olurken, ilk 5 lider arasında "Programlama" ve "Popüler bilim" de yer aldı.

Antitop, Gtk ve Cocoa'yı işgal ediyor.

Habra istatistikleri: sitenin en çok ve en az ziyaret edilen bölümlerini keşfetme

Sana bir sır vereceğim, üst göbekler de görülebilir burada, ancak görüntüleme sayısı burada gösterilmemektedir.

Değerlendirme

Ve son olarak vaat edilen derecelendirme. Hub analiz verilerini kullanarak 2019 yılının en popüler hub'larına ilişkin en popüler makaleleri görüntüleyebiliriz.

Bilgi güvenliği

Programlama

Popüler Bilim

kariyer

Bilişim Mevzuatı

web Geliştirme

GTK

Ve son olarak kimsenin rahatsız olmaması için en az ziyaret edilen hub “gtk” derecelendirmesini vereceğim. Bir yıl içinde yayınlandı bir Ayrıca “otomatik olarak” derecelendirmenin ilk satırını işgal eden makale.

Sonuç

Hiçbir sonuca varılmayacak. Herkese keyifli okumalar.

Kaynak: habr.com

Yorum ekle