Habrastatistics: menjelajahi bagian situs yang paling banyak dan paling jarang dikunjungi

Hei Habr.

В bagian sebelumnya Lalu lintas Habr dianalisis berdasarkan parameter utama - jumlah artikel, penayangan, dan peringkatnya. Namun, masalah popularitas bagian situs masih belum teruji. Menjadi menarik untuk melihat ini lebih detail dan menemukan hub paling populer dan paling tidak populer. Terakhir, saya akan melihat efek geektimes lebih detail, diakhiri dengan pilihan artikel terbaik baru berdasarkan peringkat baru.

Habrastatistics: menjelajahi bagian situs yang paling banyak dan paling jarang dikunjungi

Bagi mereka yang tertarik dengan apa yang terjadi, kelanjutannya sedang dalam tahap pemotongan.

Izinkan saya mengingatkan Anda sekali lagi bahwa statistik dan peringkatnya tidak resmi, saya tidak memiliki informasi orang dalam. Juga tidak ada jaminan bahwa saya tidak melakukan kesalahan atau melewatkan sesuatu. Tapi tetap saja, menurutku itu menarik. Kita akan mulai dengan kodenya terlebih dahulu; mereka yang tidak tertarik dengan hal ini dapat melewati bagian pertama.

Pengumpulan data

Pada parser versi pertama, hanya jumlah penayangan, komentar, dan peringkat artikel yang diperhitungkan. Ini sudah bagus, tetapi tidak memungkinkan Anda membuat pertanyaan yang lebih kompleks. Saatnya menganalisis bagian tematik situs; ini akan memungkinkan Anda melakukan penelitian yang cukup menarik, misalnya melihat bagaimana popularitas bagian “C++” telah berubah selama beberapa tahun.

Pengurai artikel telah ditingkatkan, sekarang ia mengembalikan hub tempat artikel tersebut berada, serta nama panggilan penulis dan peringkatnya (banyak hal menarik juga dapat dilakukan di sini, tetapi itu akan dibahas nanti). Data disimpan dalam file csv yang terlihat seperti ini:

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

Kami akan menerima daftar hub tematik utama situs.

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)

Fungsi find_between dan kelas Str memilih string antara dua tag, saya menggunakannya sebelumnya. Hub tematik ditandai dengan "*" sehingga dapat dengan mudah disorot, dan Anda juga dapat menghapus komentar pada baris yang sesuai untuk mendapatkan bagian dari kategori lainnya.

Output dari fungsi get_hubs adalah daftar yang cukup mengesankan, yang kami simpan sebagai kamus. Saya secara khusus menyajikan daftarnya secara keseluruhan sehingga Anda dapat memperkirakan volumenya.

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

Sebagai perbandingan, bagian geektimes terlihat lebih sederhana:

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

Hub yang tersisa dipertahankan dengan cara yang sama. Sekarang mudah untuk menulis fungsi yang mengembalikan hasil apakah artikel tersebut milik geektimes atau hub profil.

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

Fungsi serupa dibuat untuk bagian lain (“pembangunan”, “administrasi”, dll.).

pengolahan

Saatnya untuk mulai menganalisis. Kami memuat kumpulan data dan memproses data hub.

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)

Sekarang kita dapat mengelompokkan data berdasarkan hari dan menampilkan jumlah publikasi untuk hub yang berbeda.

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

Kami menampilkan jumlah artikel yang diterbitkan menggunakan Matplotlib:

Habrastatistics: menjelajahi bagian situs yang paling banyak dan paling jarang dikunjungi

Saya membagi artikel “geektimes” dan “geektimes only” di grafik, karena Sebuah artikel dapat menjadi bagian dari kedua bagian secara bersamaan (misalnya, “DIY” + “mikrokontroler” + “C++”). Saya menggunakan sebutan “profil” untuk menyorot artikel profil di situs, meskipun mungkin istilah profil bahasa Inggris untuk ini tidak sepenuhnya benar.

Pada bagian sebelumnya kami menanyakan tentang “efek geektimes” terkait dengan perubahan aturan pembayaran artikel untuk geektimes mulai musim panas ini. Mari tampilkan artikel geektimes secara terpisah:

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

Hasilnya menarik. Perkiraan rasio penayangan artikel geektimes terhadap totalnya adalah sekitar 1:5. Meskipun jumlah penayangannya sangat berfluktuasi, jumlah penayangan artikel “hiburan” tetap pada tingkat yang sama.

Habrastatistics: menjelajahi bagian situs yang paling banyak dan paling jarang dikunjungi

Anda juga dapat melihat bahwa jumlah total penayangan artikel di bagian “geektimes” masih turun setelah aturan diubah, tetapi “secara kasat mata”, tidak lebih dari 5% dari total nilai.

Menarik untuk melihat rata-rata jumlah penayangan per artikel:

Habrastatistics: menjelajahi bagian situs yang paling banyak dan paling jarang dikunjungi

Untuk artikel “hiburan”, angkanya sekitar 40% di atas rata-rata. Hal ini mungkin tidak mengherankan. Kegagalan di awal April tidak jelas bagi saya, mungkin itu yang terjadi, atau semacam kesalahan penguraian, atau mungkin salah satu penulis geektimes sedang berlibur ;).

Omong-omong, grafik menunjukkan dua puncak yang lebih mencolok dalam jumlah penayangan artikel - liburan Tahun Baru dan Mei.

hub

Mari beralih ke analisis hub yang dijanjikan. Mari kita daftar 20 hub teratas berdasarkan jumlah penayangan:

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

Hasilnya:

Habrastatistics: menjelajahi bagian situs yang paling banyak dan paling jarang dikunjungi

Yang mengejutkan, hub yang paling populer dalam hal pandangan adalah “Keamanan Informasi”; 5 pemimpin teratas juga mencakup “Pemrograman” dan “Ilmu pengetahuan populer”.

Antitop menempati Gtk dan Kakao.

Habrastatistics: menjelajahi bagian situs yang paling banyak dan paling jarang dikunjungi

Saya akan memberi tahu Anda sebuah rahasia, hub teratas juga dapat dilihat di sini, meskipun jumlah penayangan tidak ditampilkan di sana.

Penilaian

Dan terakhir, rating yang dijanjikan. Dengan menggunakan data analisis hub, kami dapat menampilkan artikel terpopuler untuk hub terpopuler tahun 2019 ini.

Keamanan informasi

Pemrograman

Ilmu pengetahuan populer

karier

Perundang-undangan di bidang TI

pengembangan web

GTK

Dan terakhir, agar tidak ada yang tersinggung, saya akan memberikan rating hub yang paling jarang dikunjungi “gtk”. Dalam setahun itu diterbitkan satu Artikel yang juga “otomatis” menempati peringkat pertama.

Kesimpulan

Tidak akan ada kesimpulan. Selamat membaca semuanya.

Sumber: www.habr.com

Tambah komentar