Habrastatistics: esplorante la plej kaj malplej vizitatajn sekciojn de la retejo

Hej Habr.

В antaŭa parto La trafiko de Habr estis analizita laŭ la ĉefaj parametroj - la nombro da artikoloj, iliaj opinioj kaj taksoj. Tamen, la temo de la populareco de la retejosekcioj restis neekzamenita. Iĝis interese rigardi ĉi tion pli detale kaj trovi la plej popularajn kaj plej nepopularajn nabojn. Fine, mi rigardos la geektimes-efekton pli detale, finante per nova elekto de la plej bonaj artikoloj bazitaj sur novaj rangotabeloj.

Habrastatistics: esplorante la plej kaj malplej vizitatajn sekciojn de la retejo

Por tiuj, kiuj interesiĝas pri tio, kio okazis, la daŭrigo estas sub la tranĉo.

Lasu min rememorigi vin denove, ke la statistikoj kaj taksoj ne estas oficialaj, mi ne havas neniun internan informon. Ankaŭ ne estas garantiite, ke mi ne ie faris eraron aŭ maltrafis ion. Sed tamen, mi pensas, ke ĝi rezultis interesa. Ni komencos unue per la kodo; tiuj, kiuj ne interesiĝas pri tio, povas preterlasi la unuajn sekciojn.

Kolekto de datumoj

En la unua versio de la analizilo, nur la nombro da vidoj, komentoj kaj artikoltaksoj estis enkalkulitaj. Ĉi tio jam estas bona, sed ĝi ne permesas al vi fari pli kompleksajn demandojn. Estas tempo analizi la temajn sekciojn de la retejo; ĉi tio permesos al vi fari sufiĉe interesajn esplorojn, ekzemple, vidi kiel la populareco de la sekcio "C++" ŝanĝiĝis dum pluraj jaroj.

La artikola analizilo estis plibonigita, nun ĝi resendas la nabojn al kiuj apartenas la artikolo, kaj ankaŭ la kromnomon de la aŭtoro kaj lian takson (multaj interesaj aferoj ankaŭ povas esti faritaj ĉi tie, sed tio venos poste). La datumoj estas konservitaj en csv-dosiero, kiu aspektas kiel ĉi tio:

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

Ni ricevos liston de la ĉefaj temaj naboj de la retejo.

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)

La funkcio find_between kaj la klaso Str elektas ĉenon inter du etikedoj, mi uzis ilin pli frue. Temaj naboj estas markitaj per "*" tiel ke ili povas esti facile elstarigitaj, kaj vi ankaŭ povas malkomenti la respondajn liniojn por akiri sekciojn de aliaj kategorioj.

La eligo de la funkcio get_hubs estas sufiĉe impona listo, kiun ni konservas kiel vortaro. Mi specife prezentas la liston en ĝia tutaĵo, por ke vi povu taksi ĝian volumon.

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

Por komparo, la geektimes-sekcioj aspektas pli modestaj:

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

La ceteraj naboj estis konservitaj en la sama maniero. Nun estas facile skribi funkcion, kiu resendas la rezulton ĉu la artikolo apartenas al geektimes aŭ al profilcentro.

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

Similaj funkcioj estis faritaj por aliaj sekcioj ("evoluo", "administrado", ktp.).

Procezo

Estas tempo komenci analizi. Ni ŝarĝas la datumaron kaj prilaboras la nabo-datumojn.

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)

Nun ni povas grupigi la datumojn laŭtage kaj montri la nombron da publikaĵoj por malsamaj naboj.

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

Ni montras la nombron da publikigitaj artikoloj uzante Matplotlib:

Habrastatistics: esplorante la plej kaj malplej vizitatajn sekciojn de la retejo

Mi dividis la artikolojn "geektimes" kaj "geektimes nur" en la diagramo, ĉar Artikolo povas aparteni al ambaŭ sekcioj samtempe (ekzemple "DIY" + "mikroregiloj" + "C++"). Mi uzis la nomon "profilo" por reliefigi profilartikolojn en la retejo, kvankam eble la angla termino profile por tio ne estas tute ĝusta.

En la antaŭa parto ni demandis pri la "geektimes-efekto" asociita kun la ŝanĝo en la pagaj reguloj por artikoloj por geektimes ekde ĉi-somere. Ni montru la geektimes-artikolojn aparte:

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

La rezulto estas interesa. La proksimuma proporcio de vidoj de geektimes-artikoloj al la totalo estas ie ĉirkaŭ 1:5. Sed dum la tuta nombro da vidoj rimarkeble variadis, la spektado de "distraj" artikoloj restis proksimume sur la sama nivelo.

Habrastatistics: esplorante la plej kaj malplej vizitatajn sekciojn de la retejo

Vi ankaŭ povas rimarki, ke la tuta nombro da vidoj de artikoloj en la sekcio "geektimes" ankoraŭ malpliiĝis post ŝanĝo de la reguloj, sed "okule", je ne pli ol 5% de la totalaj valoroj.

Estas interese rigardi la averaĝan nombron da vidoj per artikolo:

Habrastatistics: esplorante la plej kaj malplej vizitatajn sekciojn de la retejo

Por "distraj" artikoloj ĝi estas ĉirkaŭ 40% super mezumo. Ĉi tio verŝajne ne estas surpriza. La malsukceso komence de aprilo estas al mi neklara, eble tio okazis, aŭ temas pri ia analiza eraro, aŭ eble unu el la geektimes aŭtoroj feriis ;).

Cetere, la grafikaĵo montras du pliajn rimarkindajn pintojn en la nombro de vidoj de artikoloj - la novjaraj kaj majaj ferioj.

Naboj

Ni transiru al la promesita analizo de naboj. Ni listigu la plej bonajn 20 centrojn laŭ nombro da vidoj:

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

Rezulto:

Habrastatistics: esplorante la plej kaj malplej vizitatajn sekciojn de la retejo

Surprize, la plej populara nabo laŭ vidoj estis "Informa Sekureco"; la supraj 5 gvidantoj ankaŭ inkludis "Programadon" kaj "Populan sciencon".

Antitop okupas Gtk kaj Cocoa.

Habrastatistics: esplorante la plej kaj malplej vizitatajn sekciojn de la retejo

Mi diros al vi sekreton, la supraj naboj ankaŭ videblas tie, kvankam la nombro de vidoj ne estas montrita tie.

Taksado

Kaj fine, la promesita takso. Uzante nab-analizajn datumojn, ni povas montri la plej popularajn artikolojn por la plej popularaj naboj por ĉi tiu jaro 2019.

Informa Sekureco

Programado

Populara Scienco

Kariero

Leĝaro en IT

TTT-disvolviĝo

GTK

Kaj fine, por ke neniu ofendiĝu, mi donos la takson de la malplej vizitata hub "gtk". Ene de jaro ĝi estis publikigita unu La artikolo, kiu ankaŭ "aŭtomate" okupas la unuan linion de la takso.

konkludo

Ne estos konkludo. Feliĉan legadon al ĉiuj.

fonto: www.habr.com

Aldoni komenton