Archive for the ‘Павутина’ Category
Скільки слів треба щоб написати вікіпедію? І які зустрічаються частіше?
Виявляється відтоді як я рахував символи вікіпедії пройшло вже більше року. Рахував я їх за допомогою Go, хоча можна було сильно спростити собі життя і рахувати їх за допомогою Python і pywikipediabot. Сьогодні розкажу як, і як можна побачити з назви – рахуватимемо слова.
Я чомусь боявся що щоб порахувати слова пам’яті не вистачить, тому треба якусь базу даних. Або пробувати все в пам’яті, але аби комп’ютеру не стало погано якось обмежити доступну пам’ять. Але мої 4Gb використовувались лише щось трохи більше ніж на 40% для підрахунку всіх слів включно зі сторінками обговорень, категорій, шаблонів, сторінок опису файлів, і т.п. німецької вікіпедії.
В модулі pywikibot.pagegenerators
є об’єкти XMLDumpOldPageGenerator
і XMLDumpPageGenerator
. Вони приймають назву архіву з XML дампом в конструкторі, а після створення по них можна ітеруватися отримуючи об’єкти сторінки. Не ведіться на слово “Old” в назві першого об’єкта, це означає не депрекацію, а те що текст сторінки буде братись з дампа, а в другому випадку з дампа буде братись лише заголовки, а за свіжим текстом зроблять запит, що сповільнить обробку разів в 500. Тобто замість кількох годин ви будете чекати рік. 🙂
Я спробував проаналізувати німецьку вікіпедію (код буде пізніше), на це пішло 694 хв (трохи менше ніж 12 годин) і вийшло що в ній на 6,425,028 сторінках використовується 2,381,457,397 слів (приблизно 371 на сторінку), з них різних слів 18,349,393. В кінцевому результаті CSV з частотним словничком виходить на 300MB.
Серед тих що зустрічаються лише раз є слова типу PikettdienstPikettdienst (помилка парсера який видаляв розмітку), слово – це юридичний термін швейцарської німецької і перекладається як “служба за викликом”. І є слова на зразок Werkshöfe – подвір’я фабрик.
Топ 50 слів виглядає так, і складає 28% всіх слів загалом:
der | 58761447 | sich | 9169933 |
und | 49084873 | wurde | 9114619 |
die | 44536463 | CET | 8614461 |
in | 35684744 | an | 8385637 |
von | 24448221 | er | 7835324 |
ist | 20614114 | dass | 7550955 |
den | 19454023 | du | 7435099 |
nicht | 17519638 | bei | 7420172 |
das | 17302844 | Diskussion | 7237855 |
zu | 16167971 | aus | 7065523 |
mit | 15906145 | Artikel | 6967243 |
im | 15167140 | oder | 6824420 |
des | 14661593 | werden | 6508092 |
für | 14016308 | war | 6449858 |
auf | 13957013 | nach | 6426826 |
auch | 12849476 | wird | 6117566 |
eine | 11903977 | aber | 6052645 |
ein | 11780352 | am | 6017703 |
Kategorie | 11369651 | sind | 5953632 |
als | 11167157 | Der | 5623930 |
dem | 11124726 | Das | 5545595 |
CEST | 11104741 | einen | 5465687 |
ich | 10886406 | noch | 5409154 |
Die | 10761776 | wie | 5293658 |
es | 10204681 | einer | 5228368 |
До списку потрапили такі вікіпедійно специфічні слова як Kategorie (сторінки без категорій вважаються не комільфо), CEST і CET (центральноєвропейський літній час і центральноєвропейський час, в підписах в обговореннях).
Ну а що без сторінок обговорень? Проблемав тому, що при створенні об’єкту сторінки XMLDumpOldPageGenerator
бере з дампа лише текст і заголовок, простір імен залишається не заповненим і за замовчуванням 0 (основний). Є ще поле isredirect
так при спробі доступу до нього знову здійснюється запит. Тому, краще перейти на рівень нижче і використати XmlDump
з pywikibot.xmlreader
, він використовується так само, просто дає об’єкти не Page, а попростіші, які не вміють робити запити до вікіпедії і не мають методу save
. Але нам його й не треба, правда?
Ось код який ігнорує перенаправлення і всі сторінки крім статтей:
"""Count word frequencies in wikipedia dump""" import csv from collections import Counter from itertools import islice import re import sys import mwparserfromhell from pywikibot.xmlreader import XmlDump def main(): """Iterate over pages and count words""" if len(sys.argv) < 2: print('Please give file name of dump') return filename = sys.argv[1] pages = 0 words = 0 words_counts = Counter() print('Processing dump') for page in XmlDump(filename).parse(): if (page.ns != '0') or page.isredirect: continue try: text = mwparserfromhell.parse(page.text).strip_code() except Exception as e: print(page.title, e) continue text = text.replace('\u0301', '') # remove accents # Ukrainian: # page_words = re.findall( # r'[абвгґдеєжзиіїйклмнопрстуфхцчшщьюя' # r'АБВГҐДЕЄЖЗИІЇЙКЛМНОПРСТУФХЦЧШЩЬЮЯ’\'-]+', # text # ) # Any language: page_words = re.findall(r'\b[^\W\d]+\b', text) pages += 1 words += len(page_words) words_counts.update(page_words) if pages % 123 == 0: print('\rPages: %d. Words: %d. Unique: %d. Processing: %s' % ( pages, words, len(words_counts), (page.title + ' ' * 70)[:70], ), end='') print('Done. Writing csv') with open('common_words.csv', 'w', newline='') as csvfile: csvwriter = csv.writer(csvfile) for item in words_counts.most_common(): csvwriter.writerow(item) if __name__ == '__main__': main()
Він працює майже вдвічі швидше, 381 хвилину, бо обробляє лише 2,295,426 сторінок (обсяг німецької вікіпедії цього року). На цих сторінках є 1,074,446,116 слів (в середньому 468 на сторінку), з них різних – 12,002,417. (Виявляється є аж 6 мільйонів всяких слів які вживаються на всіляких службових сторінках німецької вікіпедії, і яких нема в статтях).
Якщо ж взяти українські статті, то на них треба ще менше часу – 131 хвилину (забув уточнити що в мене SSD), їх є 923238 (скоро мільйон!), слів 238263126 (в середньому 258 на сторінку, треба доповнювати 😉 ). З них різних – 4,571,418. Отак, в мене тепер є частотний словник української на 4.5 мільйони слів. І німецької на 12 мільйонів.
Хоча не спішіть з висновками що українська мова бідніша, бо мої методи потребують вдосконалення. По перше, так як Morgen (ранок) і morgen (завтра) – різні слова, то я не приводив букви в німецькій до одного регістру. (Правда й в українській забув це зробити).
По друге, в німецькому словнику 350590 разів зустрічається слово “www”, бо я вважав словом будь-яку послідовність літер латинки, а в українській відфільтрував кирилицю. Слово youtube зустрічається 8375 разів, а значить є ризик знайти якесь рідкісне слово на зразок “fCn8zs912OE”. 🙂
На WordPress глючить додавання картинок, тому нате вам відео:
А, і ось топ-10 української вікіпедії:
в,4551982
на,3730686
і,3475086
у,3353796
з,3053407
-,2695783
Категорія,2417267
та,2350573
до,1815429
року,1553492
Частота “року” наводить на думку що в українській вікіпедії якийсь перекос на історичні методи викладу. 🙂
Кілька рядків коду що підвищують продуктивність в рази
«Не потоком шумних і галасливих фраз, а тихою, невтомною працею любіть Україну!»
Це клікбейтний заголовок, але тут ми дійсно за пару хвилин напишемо розширення до браузера, щось на зразок resquetime тільки просте як одноклітинні. Назвемо його наприклад higher power
, бо воно працюватиме як підстраховка для нашої власної сили волі.
Створюємо директорію проекту
bunyk@bunyk-thinkpad:~/projects$ mkdir higher_power bunyk@bunyk-thinkpad:~/projects$ cd higher_power/
А в ній файл manifest.json
з наступним вмістом:
{ "manifest_version": 2, "name": "Higher power", "version": "1.0", "description": "Helps you to avoid temptations", "icons": { }, "content_scripts": [ { "matches": ["*://*.facebook.com/*"], "js": ["power.js"], "run_at": "document_start" } ] }
Версії і назва – обов’язкові поля, опис та icons – ні, але корисні, бо відображатиметься в списку додатків, а content_scripts
описує який код завантажуавти при відкриванні певної адреси.
“run_at” каже запускати код ще до того як сторінка завантажиться, без цієї опції браузер пару секунд рендерить стрічку фейсбука, а тоді вже наш аддон починає щось робити.
Створимо цей код, в згаданому в маніфесті файлі power.js
, нариклад так:
window.location.href="https://bunyk.wordpress.com/";
Замість bunyk.wordpress.com можна вписати https://www.udacity.com/, https://www.edx.org/, https://github.com/ чи щось таке.
Тепер відкриваємо в Firefox сторінку “about:debugging” (для інших браузерів самі з’ясуйте що і напишіть в коментарі будь ласка), натискаємо кнопку “Load Temporary Add-on…”, вибираємо будь-який файл з директорії нашого проекту, і насолоджуємось результатом.
Посилання
https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension
Як створити блог з Hugo
Hugo – це такий генератор статичних сайтів. Статичні сайти – це сайти що складаються з набору фіксованих сторінок і не генеруються з шаблонів і запитів до бази даних при кожному завантаженні. Це з одного боку менш зручно бо немає можливості наприклад опублікувати щось автоматично встановивши час публікації, а з іншого боку – менш вимогливо до ресурсів, і краще з точки зору комп’ютерної безпеки. Крім того, wordpress.com зі своїми оновленнями інтерфейсу починає трохи дратувати. Хочеться markdown, свого javascript і стилів. А ще він не підсвічує синтаксис go. 🙂 Але ця стаття публікується на WordPress, яка іронія… Тому що я ще не вирішив що публікуватиму там.
До цього, мій статичний сайт на github генерувався самописним скриптом на python, який перетворював шаблони Mako в HTML, дозволяв вставляти javascript разом з залежностями, і так як я коли це придумував не знав ні про який node.js з npm (точніше знав, але не дуже цікавився), то залежності в мене описувались не в package.json, а в external_assets.py, і збирав їх не webpack чи gulp, чи browserify чи bower, а requirejs.py.
Але це я відхиляюсь від теми. Мова про те що велосипеди треба не винаходити, треба брати і їздити. Тому поїхали!
Прочитати решту цього запису »
Скільки символів потрібно щоб написати вікіпедію?
Не так важливо скільки там людей говоритиме українською коли настане технологічна сингулярність. Важлива сумарна обчислювальна потужність інтелекту що володіє українською. 😉 І взагалі варто опановувати хоч якісь основи навчання машин – це професія в якій роботи ще не скоро замінять людей. Щоб навчити машину мови – їй треба багааатезно прикладів. І найбільший шмат української мови який можна легко згодувати машині – вікіпедія. Тому сьогодні ми спробуємо з’ясувати що потрібно щоб отримати цей масив тексту, і порахувати на ньому якусь простеньку статистику, для якої не треба бази даних а вистачить оперативної пам’яті.
Перше що нам потрібно – копія бази даних вікіпедії. Тому що вікіпедія містить більше чверті мільйона статтей, і навіть якщо ми робитимемо по запиту на секунду, що вікіпедія не схвалює для всяких там приватних павуків, то складання індексу займе в нас (750000 сек)/ 3600 / 24 = 8.68 діб > тижня. Тому заходимо на
https://dumps.wikimedia.org/ , вибираємо дамп який більше подобається, наприклад останній дамп що містить статті (без сторінок обговорень) української вікіпедії і ставимо на скачування.
А поки воно скачується підготуємось його розпаковувати. Ми скачуємо заархівований XML, який при розпаковуванні займає щось біля 5GB. Всередині є багато тисяч елементів , кожен з яких містить деталі про сторінку. Ось код на Go який містить функцію
Read
що розархівовує і водночас парсить XML, та повертає канал в який кидає сторінку за сторінкою, а в головній функції ітерується по всіх сторінках і підраховує кількість символів в їх тексті. В кінці виводить статистику:
Прочитати решту цього запису »
Простеньке Go API з JWT авторизацією
Щоб щось зрозуміти іноді легше писати ніж читати (В мене була потреба зрозуміти як працює middleware jwt авторизації, і я цей код читати не міг. Довелось для розминки написати аналогічне, трохи помогло).
Якщо вам цікаво що таке JWT і для чого, то в двох словах – це можливість видати комусь право доступу до чогось без бази даних де б писало що ми йому таке право давали. Тобто сервер на якому користувач авторизується, і сервер до якого він отримує доступ – це можуть бути два абсолютно окремі сервери, які не те що не мають спільної бази даних, вони навіть не спілкуються мережею. Головне – правильні ключі.
Напишемо наступне супер просте API:
POST /login {user: “”, password: “”} – віддає нам JWT токен для дозволу запису
GET / – віддає нам список записів
POST / – з заголовком “Authorization: Bearer ” дозволяє додати новий запис до списку, якщо ми авторизовані.
Для початку зробимо все без авторизації: