Habrastatistics: بررسی بیشترین و کم بازدیدترین بخش های سایت

هی هابر

В قسمت قبلی ترافیک هابر با توجه به پارامترهای اصلی - تعداد مقالات، بازدیدها و رتبه بندی آنها - تجزیه و تحلیل شد. با این حال، موضوع محبوبیت بخش های سایت بررسی نشده باقی ماند. جالب شد که با جزئیات بیشتری به این موضوع نگاه کنیم و محبوب ترین و نامحبوب ترین هاب ها را پیدا کنیم. در نهایت، من به افکت geektimes با جزئیات بیشتری نگاه می کنم و با انتخاب جدیدی از بهترین مقالات بر اساس رتبه بندی های جدید به پایان می رسم.

Habrastatistics: بررسی بیشترین و کم بازدیدترین بخش های سایت

برای کسانی که به اتفاقی که افتاده علاقه مند هستند، ادامه در زیر برش است.

یک بار دیگر یادآوری می کنم که آمار و رتبه بندی رسمی نیست، من هیچ اطلاعات داخلی ندارم. همچنین تضمین نمی شود که در جایی اشتباه نکرده باشم یا چیزی را از دست داده باشم. اما با این حال، به نظر من جالب بود. ابتدا با کد شروع می کنیم؛ کسانی که علاقه ای به این کار ندارند می توانند قسمت های اول را رد کنند.

جمع آوری داده ها

در نسخه اول تجزیه کننده، تنها تعداد بازدیدها، نظرات و رتبه بندی مقاله در نظر گرفته شده است. این در حال حاضر خوب است، اما به شما اجازه نمی دهد پرس و جوهای پیچیده تری انجام دهید. وقت آن است که بخش های موضوعی سایت را تجزیه و تحلیل کنید؛ این به شما امکان می دهد تحقیقات بسیار جالبی انجام دهید، به عنوان مثال، ببینید که چگونه محبوبیت بخش "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 نمایش می دهیم:

Habrastatistics: بررسی بیشترین و کم بازدیدترین بخش های سایت

من مقالات "geektimes" و "geektimes only" را در نمودار تقسیم کردم، زیرا یک مقاله می تواند به طور همزمان به هر دو بخش تعلق داشته باشد (به عنوان مثال، "DIY" + "microcontrollers" + "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 است. اما در حالی که تعداد کل بازدیدها به طور قابل توجهی در نوسان بود، بازدید از مقالات "سرگرمی" تقریباً در همان سطح باقی ماند.

Habrastatistics: بررسی بیشترین و کم بازدیدترین بخش های سایت

همچنین می توانید متوجه شوید که تعداد کل بازدید از مقالات در بخش "geektimes" پس از تغییر قوانین همچنان کاهش یافته است، اما "با چشم" بیش از 5٪ از مقادیر کل کاهش یافته است.

نگاهی به میانگین تعداد بازدید از هر مقاله جالب است:

Habrastatistics: بررسی بیشترین و کم بازدیدترین بخش های سایت

برای مقالات «سرگرمی» حدود 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()

یافته ها:

Habrastatistics: بررسی بیشترین و کم بازدیدترین بخش های سایت

با کمال تعجب، محبوب‌ترین مرکز از نظر بازدید، «امنیت اطلاعات» بود؛ 5 رهبر برتر نیز شامل «برنامه‌نویسی» و «علم محبوب» بودند.

آنتی تاپ Gtk و Cocoa را اشغال می کند.

Habrastatistics: بررسی بیشترین و کم بازدیدترین بخش های سایت

رازی را به شما می گویم، هاب های برتر نیز دیده می شوند اینجا، اگرچه تعداد بازدیدها در آنجا نشان داده نشده است.

رتبه

و در نهایت امتیاز وعده داده شده. با استفاده از داده های تجزیه و تحلیل هاب، می توانیم محبوب ترین مقالات را برای محبوب ترین هاب ها برای سال 2019 نشان دهیم.

امنیت اطلاعات

برنامه نویسی

علوم پرطرفدار

حرفه ای

قانونگذاری در فناوری اطلاعات

توسعه وب

GTK

و در آخر، برای اینکه کسی توهین نشود، رتبه کم بازدیدترین هاب "gtk" را می دهم. در عرض یک سال منتشر شد одна مقاله ای که "به طور خودکار" نیز خط اول رتبه بندی را اشغال می کند.

نتیجه

نتیجه گیری نخواهد شد. خواندن همه مبارک.

منبع: www.habr.com

اضافه کردن نظر