Habrastatistics: khám phá các phần được truy cập nhiều nhất và ít nhất của trang web

Xin chào, Habr.

В phần trước Lưu lượng truy cập của Habr được phân tích theo các thông số chính - số lượng bài viết, lượt xem và xếp hạng của chúng. Tuy nhiên, vấn đề về mức độ phổ biến của các phần trang web vẫn chưa được xem xét. Thật thú vị khi xem xét vấn đề này chi tiết hơn và tìm ra những trung tâm phổ biến nhất và không được ưa chuộng nhất. Cuối cùng, tôi sẽ xem xét hiệu ứng geektimes chi tiết hơn, kết thúc bằng việc lựa chọn mới các bài viết hay nhất dựa trên thứ hạng mới.

Habrastatistics: khám phá các phần được truy cập nhiều nhất và ít nhất của trang web

Đối với những ai quan tâm đến những gì đã xảy ra, phần tiếp theo đang bị cắt bỏ.

Hãy để tôi nhắc bạn một lần nữa rằng số liệu thống kê và xếp hạng không phải là chính thức, tôi không có bất kỳ thông tin nội bộ nào. Cũng không đảm bảo rằng tôi không mắc lỗi ở đâu đó hoặc bỏ sót điều gì đó. Nhưng tôi vẫn nghĩ nó trở nên thú vị. Chúng ta sẽ bắt đầu với đoạn mã trước; những ai không quan tâm đến điều này có thể bỏ qua phần đầu tiên.

Thu thập dữ liệu

Trong phiên bản đầu tiên của trình phân tích cú pháp, chỉ tính đến số lượt xem, nhận xét và xếp hạng bài viết. Điều này đã tốt rồi, nhưng nó không cho phép bạn thực hiện các truy vấn phức tạp hơn. Đã đến lúc phân tích các phần theo chủ đề của trang web; điều này sẽ cho phép bạn thực hiện những nghiên cứu khá thú vị, chẳng hạn như xem mức độ phổ biến của phần “C++” đã thay đổi như thế nào trong vài năm qua.

Trình phân tích cú pháp bài viết đã được cải tiến, giờ đây nó trả về các trung tâm mà bài viết thuộc về, cũng như biệt hiệu của tác giả và xếp hạng của anh ấy (rất nhiều điều thú vị cũng có thể được thực hiện ở đây, nhưng điều đó sẽ đến sau). Dữ liệu được lưu trong tệp csv trông giống như thế này:

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

Chúng tôi sẽ nhận được danh sách các trung tâm chủ đề chính của trang web.

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)

Hàm find_between và lớp Str chọn một chuỗi giữa hai thẻ, tôi đã sử dụng chúng sớm hơn. Các trung tâm chuyên đề được đánh dấu bằng dấu "*" để có thể dễ dàng đánh dấu chúng và bạn cũng có thể bỏ ghi chú các dòng tương ứng để nhận các phần thuộc các danh mục khác.

Đầu ra của hàm get_hubs là một danh sách khá ấn tượng mà chúng tôi lưu dưới dạng từ điển. Tôi đặc biệt trình bày toàn bộ danh sách để bạn có thể ước tính khối lượng của nó.

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

Để so sánh, các phần của geektimes trông khiêm tốn hơn:

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

Các trung tâm còn lại được bảo quản theo cách tương tự. Giờ đây thật dễ dàng để viết một hàm trả về kết quả cho dù bài viết thuộc về geektimes hay một trung tâm hồ sơ.

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

Các chức năng tương tự đã được thực hiện cho các phần khác (“phát triển”, “quản trị”, v.v.).

Chế biến

Đã đến lúc bắt đầu phân tích. Chúng tôi tải tập dữ liệu và xử lý dữ liệu trung tâm.

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)

Bây giờ chúng tôi có thể nhóm dữ liệu theo ngày và hiển thị số lượng ấn phẩm cho các trung tâm khác nhau.

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

Chúng tôi hiển thị số lượng bài viết đã xuất bản bằng Matplotlib:

Habrastatistics: khám phá các phần được truy cập nhiều nhất và ít nhất của trang web

Tôi đã chia các bài viết “geektimes” và “geektimes only” trong biểu đồ, bởi vì Một bài viết có thể thuộc cả hai phần cùng một lúc (ví dụ: “DIY” + “microcontrollers” + “C++”). Tôi đã sử dụng từ “hồ sơ” để làm nổi bật các bài viết về hồ sơ trên trang web, mặc dù có lẽ thuật ngữ tiếng Anh cho từ này không hoàn toàn chính xác.

Trong phần trước, chúng tôi đã hỏi về “hiệu ứng geektimes” liên quan đến sự thay đổi trong quy tắc thanh toán cho các bài báo dành cho geektimes bắt đầu từ mùa hè này. Hãy hiển thị riêng các bài viết của 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()

Kết quả thật thú vị. Tỷ lệ gần đúng giữa số lượt xem của các bài báo trên geektime trên tổng số lượt xem là khoảng 1:5. Nhưng trong khi tổng số lượt xem dao động đáng kể thì lượt xem các bài báo “giải trí” vẫn ở mức tương đương.

Habrastatistics: khám phá các phần được truy cập nhiều nhất và ít nhất của trang web

Bạn cũng có thể nhận thấy rằng tổng số lượt xem các bài viết trong phần “geektimes” vẫn giảm sau khi thay đổi quy tắc, nhưng “bằng mắt” không quá 5% tổng giá trị.

Thật thú vị khi nhìn vào số lượt xem trung bình trên mỗi bài viết:

Habrastatistics: khám phá các phần được truy cập nhiều nhất và ít nhất của trang web

Đối với các bài viết “giải trí”, con số này cao hơn mức trung bình khoảng 40%. Điều này có lẽ không có gì đáng ngạc nhiên. Thất bại vào đầu tháng XNUMX đối với tôi là không rõ ràng, có thể đó là điều đã xảy ra, hoặc đó là một loại lỗi phân tích cú pháp nào đó, hoặc có thể một trong những tác giả của geektimes đã đi nghỉ;).

Nhân tiện, biểu đồ cho thấy hai đỉnh điểm đáng chú ý hơn về số lượt xem các bài báo - dịp nghỉ Tết và tháng Năm.

Trung tâm

Hãy chuyển sang phần phân tích đã hứa về các trung tâm. Hãy liệt kê 20 trung tâm hàng đầu theo số lượt xem:

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

Kết quả:

Habrastatistics: khám phá các phần được truy cập nhiều nhất và ít nhất của trang web

Điều đáng ngạc nhiên là trung tâm được yêu thích nhất xét về lượt xem lại là “An toàn thông tin”; top 5 dẫn đầu còn có “Lập trình” và “Khoa học phổ thông”.

Antitop chiếm Gtk và Cocoa.

Habrastatistics: khám phá các phần được truy cập nhiều nhất và ít nhất của trang web

Tôi sẽ nói cho bạn một bí mật, những trung tâm hàng đầu cũng có thể được nhìn thấy đây, mặc dù số lượt xem không được hiển thị ở đó.

Đánh giá

Và cuối cùng là đánh giá đã hứa. Bằng cách sử dụng dữ liệu phân tích trung tâm, chúng tôi có thể hiển thị các bài viết phổ biến nhất về các trung tâm phổ biến nhất trong năm 2019.

Bảo mật thông tin

Lập trình

Khoa học Phổ Thông

nghề nghiệp

Pháp luật về CNTT

phát triển web

GTK

Và cuối cùng, để không ai bị xúc phạm, tôi sẽ đưa ra đánh giá về trung tâm ít được truy cập nhất “gtk”. Trong vòng một năm nó đã được xuất bản một Bài báo cũng “tự động” chiếm dòng đầu tiên của bảng xếp hạng.

Kết luận

Sẽ không có kết luận. Chúc mọi người đọc vui vẻ.

Nguồn: www.habr.com

Thêm một lời nhận xét