Tag Archives: вікіпедія

Скільки слів треба щоб написати вікіпедію? І які зустрічаються частіше?

Виявляється відтоді як я рахував символи вікіпедії пройшло вже більше року. Рахував я їх за допомогою 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

Частота “року” наводить на думку що в українській вікіпедії якийсь перекос на історичні методи викладу. 🙂

Символи вікіпедії, детальніший аналіз

Щодо моєї попередньої публікації, мені зауважили що є різні простори імен. Тому сьогодні буде аналіз лише тих символів які можна зустріти в статтях (а не в категоріях, шаблонах, на сторінках користувачів і проектів), розбивка по різних алфавітах (може кому треба буде для частотного аналізу).

Загалом статті складаються з 3 613 435 448 символів, з яких різних 23 717. Вони розподілені так (намальовано лише початок розподілу, бо решта – неозрброєним оком не видно):

Ось таблиця найвживаніших 256, де спершу йде номер в рейтингу, потім символ, а останнє число – скільки раз він зустрічається. Якщо символ невидимий – то в дужках вказаний його номер в таблиці юнікоду:

1) пробіл (32) 442790853
2) а 160531292
3) о 157877564
4) н 138215454
5) і 117277229
6) р 105263631
7) и 98886082
8) е 88928818
9) т 87767363
10) | 80797095
11) в 78200503
12) с 75999527
13) ] 73604421
14) [ 73599732
15) л 70900301
16) к 69356359
17) новий рядок (10) 57091749
18) e 52089242
19) у 50198598
20) д 48912768
21) = 46361768
22) м 46138865
23) t 41512840
24) п 40914157
25) a 39627848
26) r 36179188
27) n 36087343
28) я 34788627
29) ь 33853333
30) . 32022232
31) s 31881844
32) i 30972299
33) з 30652689
34) o 30492305
35) 1 30006539
36) l 29367163
37) г 28746761
38) 0 27570049
39) й 26579527
40) - 24869609
41) ' 24751079
42) б 23634039
43) 2 21423701
44) , 21261521
45) / 20051917
46) c 19845819
47) p 19831300
48) ц 19183628
49) } 18895464
50) { 18889983
51) ч 18359390
52) d 17813691
53) b 16517162
54) 9 16240786
55) g 15262611
56) х 14936744
57) u 14902347
58) f 14303148
59) ї 13406838
60) h 13050086
61) m 12543340
62) < 12111655
63) : 12102999
64) > 12094377
65) ж 11923903
66) 3 10947193
67) 5 10647152
68) " 10484943
69) 4 10091884
70) w 9560852
71) К 9364738
72) П 9190333
73) 8 9177988
74) ю 9157643
75) ; 9037575
76) 6 8918805
77) С 8870091
78) ) 8867662
79) ( 8851064
80) ш 8821755
81) 7 8188158
82) ф 7340920
83) А 7166375
84) y 7039008
85) & 7006502
86) В 6869441
87) _ 6022842
88) М 5970386
89) є 5956858
90) k 5778567
91) Г 5745696
92) * 5712005
93) — 5519856
94) Л 4852833
95) S 4825612
96) Р 4712055
97) v 4646612
98) Н 4635296
99) Д 4580191
100) A 4422679
101) D 4409130
102) Т 4356396
103) щ 4143981
104) Б 4112548
105) У 4017973
106) R 3910061
107) Ф 3848824
108) I 3805943
109) « 3732952
110) » 3730320
111) C 3706725
112) О 3626277
113) E 3501507
114) P 3206479
115) І 3168901
116) F 3042662
117) З 2886284
118) L 2875664
119) T 2762480
120) % 2668723
121) B 2665154
122) M 2605927
123) N 2471967
124) G 2455406
125) # 2432361
126) x 2377037
127) ! 2307947
128) z 1683501
129) H 1652473
130) V 1627598
131) j 1580981
132) K 1542325
133) Ч 1534632
134) Х 1497880
135) U 1430170
136) Ш 1413847
137) O 1364521
138) Е 1326679
139) W 1071605
140) Ц 970737
141) – 873533
142) J 866094
143) ? 838918
144) Q 767843
145) Я 759389
146) Є 680158
147) X 636113
148) Y 617782
149) Ж 607830
150) ы 548375
151)   511929
152) Ю 503103
153) q 490449
154) + 467382
155) ́ 381075
156) \ 343889
157) 328026
158) Z 313673
159) ґ 293475
160) Й 286295
161) № 279189
162) é 263700
163) И 240639
164) Ґ 138039
165) ° 134968
166) ’ 129122
167) Щ 112586
168) ½ 92609
169) э 89526
170) … 88401
171) ó 87628
172) â 84646
173) ~ 75512
174) ü 67713
175) ^ 66068
176) ł 65770
177) Э 65262
178) ă 63282
179) á 62358
180) ъ 60515
181) ţ 60459
182) Ї 59899
183) • 58273
184) ² 57176
185) − 51293
186) × 51201
187) ё 50264
188) ä 49539
189) “ 47666
190) ö 47657
191) $ 46091
192) † 42959
193) ş 41864
194) è 41182
195) α 39774
196) í 38230
197) „ 34637
198) ο 34016
199) → 28048
200) ا 27473
201) · 26648
202) ‎ 26007
203) ν 24624
204) ń 24312
205) ι 24220
206) ę 24171
207) Ь 24064
208) ± 23585
209) ′ 23492
210) ” 23461
211) τ 23408
212) ș 23387
213) ς 23318
214) ρ 22519
215) š 21622
216) ś 21499
217) à 21334
218) ą 20463
219) ў 19269
220) ​ 18949
221) ε 18309
222) č 18232
223) @ 18014
224) É 17552
225) ç 17282
226) λ 17177
227) ل 16987
228) ³ 15756
229) σ 14667
230) κ 14317
231) ż 13948
232) η 13811
233) μ 12411
234) ر 12083
235) ي 11783
236) ā 11684
237) ñ 11460
238) م 11324
239) ć 11226
240) υ 11130
241) ن 11115
242) ß 10974
243) π 10708
244) ί 10461
245) ë 9850
246) ı 9824
247) ú 9761
248) ž 9598
249) ά 9572
250) ț 9284
251) γ 9152
252) ‘ 9122
253) ա 8975
254) ѣ 8947
255) و 8929
256) қ 8841

Ось вони ж, по групах:

Український алфавіт

2) а 160531292
3) о 157877564
4) н 138215454
5) і 117277229
6) р 105263631
7) и 98886082
8) е 88928818
9) т 87767363
11) в 78200503
12) с 75999527
15) л 70900301
16) к 69356359
19) у 50198598
20) д 48912768
22) м 46138865
24) п 40914157
28) я 34788627
29) ь 33853333
33) з 30652689
37) г 28746761
39) й 26579527
42) б 23634039
48) ц 19183628
51) ч 18359390
56) х 14936744
59) ї 13406838
65) ж 11923903
71) К 9364738
72) П 9190333
74) ю 9157643
77) С 8870091
80) ш 8821755
82) ф 7340920
83) А 7166375
86) В 6869441
88) М 5970386
89) є 5956858
91) Г 5745696
94) Л 4852833
96) Р 4712055
98) Н 4635296
99) Д 4580191
102) Т 4356396
103) щ 4143981
104) Б 4112548
105) У 4017973
107) Ф 3848824
112) О 3626277
115) І 3168901
117) З 2886284
133) Ч 1534632
134) Х 1497880
136) Ш 1413847
138) Е 1326679
140) Ц 970737
145) Я 759389
146) Є 680158
149) Ж 607830
152) Ю 503103
159) ґ 293475
160) Й 286295
163) И 240639
164) Ґ 138039
167) Щ 112586
182) Ї 59899
207) Ь 24064

Англійський алфавіт

18) e 52089242
23) t 41512840
25) a 39627848
26) r 36179188
27) n 36087343
31) s 31881844
32) i 30972299
34) o 30492305
36) l 29367163
46) c 19845819
47) p 19831300
52) d 17813691
53) b 16517162
55) g 15262611
57) u 14902347
58) f 14303148
60) h 13050086
61) m 12543340
70) w 9560852
84) y 7039008
90) k 5778567
95) S 4825612
97) v 4646612
100) A 4422679
101) D 4409130
106) R 3910061
108) I 3805943
111) C 3706725
113) E 3501507
114) P 3206479
116) F 3042662
118) L 2875664
119) T 2762480
121) B 2665154
122) M 2605927
123) N 2471967
124) G 2455406
126) x 2377037
128) z 1683501
129) H 1652473
130) V 1627598
131) j 1580981
132) K 1542325
135) U 1430170
137) O 1364521
139) W 1071605
142) J 866094
144) Q 767843
147) X 636113
148) Y 617782
153) q 490449
158) Z 313673

Цифри

35) 1 30006539
38) 0 27570049
43) 2 21423701
54) 9 16240786
66) 3 10947193
67) 5 10647152
69) 4 10091884
73) 8 9177988
76) 6 8918805
81) 7 8188158

Пунктуація

1) пробіл (32) 442790853
17) новий рядок (10) 57091749
10) | 80797095
13) ] 73604421
14) [ 73599732
21) = 46361768
30) . 32022232
40) – 24869609
41) ‘ 24751079
44) , 21261521
45) / 20051917
49) } 18895464
50) { 18889983
62) < 12111655
63) : 12102999
64) > 12094377
68) ” 10484943
75) ; 9037575
78) ) 8867662
79) ( 8851064
85) & 7006502
87) _ 6022842
92) * 5712005
93) — 5519856
109) « 3732952
110) » 3730320
120) % 2668723
125) # 2432361
127) ! 2307947
141) – 873533
143) ? 838918
151) нерозривний пробіл (160) 511929
154) + 467382
155) ́ 381075
156) \ 343889
157) TAB (9) 328026
161) № 279189
165) ° 134968
166) ’ 129122
168) ½ 92609
170) … 88401
173) ~ 75512
175) ^ 66068
183) • 58273
184) ² 57176
185) − 51293
186) × 51201
189) “ 47666
191) $ 46091
192) † 42959
197) „ 34637
199) → 28048
200) ا 27473
201) · 26648
202) символ зліва-направо 26007
208) ± 23585
209) ′ 23492
210) ” 23461
220) пробіл нульової довжини (8203) 18949
223) @ 18014
228) ³ 15756
252) ‘ 9122

Латиниця з наворотами

162) é 263700
171) ó 87628
172) â 84646
174) ü 67713
176) ł 65770
178) ă 63282
179) á 62358
181) ţ 60459
188) ä 49539
190) ö 47657
193) ş 41864
194) è 41182
196) í 38230
204) ń 24312
206) ę 24171
212) ș 23387
215) š 21622
216) ś 21499
217) à 21334
218) ą 20463
222) č 18232
224) É 17552
225) ç 17282
231) ż 13948
236) ā 11684
237) ñ 11460
239) ć 11226
242) ß 10974
245) ë 9850
246) ı 9824
247) ú 9761
248) ž 9598
250) ț 9284

Застарілі кириличні букви

150) ы 548375
169) э 89526
177) Э 65262
180) ъ 60515
187) ё 50264
219) ў 19269
254) ѣ 8947
256) қ 8841

Грецькі букви

195) α 39774
198) ο 34016
203) ν 24624
205) ι 24220
211) τ 23408
213) ς 23318
214) ρ 22519
221) ε 18309
226) λ 17177
229) σ 14667
230) κ 14317
232) η 13811
233) μ 12411
240) υ 11130
243) π 10708
244) ί 10461
249) ά 9572
251) γ 9152

Арабські

227) ل 16987
234) ر 12083
235) ي 11783
238) م 11324
241) ن 11115
255) و 8929

Вірменська

253) ա 8975

Парні символи

Минулого разу мені стало цікаво чому кількість дужок не співпадає. Може варто було б написати правильний алгоритм зі стеком, але я поки напевне спробую знайти статті де просто кількість дужок не співпадає аби подивитись в чому річ.

При цьому не є збалансованими майже всі дужки. Щодо круглих ясно, бо навіть в цій публікації в мене закриваючі масово переважають відкриваючі, але щодо квадратних і фігурних, які задають посилання і шаблони (трансклюзію сторінок). Якщо запустити програму яка стеком перевіряє парність:

То отримаємо нескінченний потік помилок у всіх статтях:

Швеція Закриваюча дужка ] без відкриваючої до неї біля "eritage Foundation]]]:\n* ВВП&nbsp;— $ 24"
Сумська область До кінця сторінки не закриті наступні дужки [[
Тернопільська область Закриваюча дужка ] без відкриваючої до неї біля "ьні вечори. Дебют]]»]] (Тернопіль), міжн"
Теліга Олена Іванівна Закриваюча дужка ] без відкриваючої до неї біля "нього шкільного віку] / [[Анна Багряна]]"
Західний Буг До кінця сторінки не закриті наступні дужки [
Андрухович Юрій Ігорович Закриваюча дужка ] без відкриваючої до неї біля ". Літ, 29 січня 2013]</ref> були наведен"
Данило Галицький Закриваюча дужка } без відкриваючої до неї біля "а Романівна]] ({Пом}} після [[1241]]), о"
29 Закриваюча дужка } без відкриваючої до неї біля "377a.htm}}{{ref-en}}}</ref>\n\n== Народили"

Переважкно люди в джерелах ставлять зайву закриваючу квадратну, або забувають відкрити квадратну, тому що якось дуже недбало ставляться до оформлення джерел. Але тих помилок так багато що я не знаю як їх всіх виправити. 😦 Може варто написати якийсь лінтер для вікіпедії, який би про ці помилки авторам повідомляв?

Скільки символів потрібно щоб написати вікіпедію?

Не так важливо скільки там людей говоритиме українською коли настане технологічна сингулярність. Важлива сумарна обчислювальна потужність інтелекту що володіє українською. 😉 І взагалі варто опановувати хоч якісь основи навчання машин – це професія в якій роботи ще не скоро замінять людей. Щоб навчити машину мови – їй треба багааатезно прикладів. І найбільший шмат української мови який можна легко згодувати машині – вікіпедія. Тому сьогодні ми спробуємо з’ясувати що потрібно щоб отримати цей масив тексту, і порахувати на ньому якусь простеньку статистику, для якої не треба бази даних а вистачить оперативної пам’яті.

Перше що нам потрібно – копія бази даних вікіпедії. Тому що вікіпедія містить більше чверті мільйона статтей, і навіть якщо ми робитимемо по запиту на секунду, що вікіпедія не схвалює для всяких там приватних павуків, то складання індексу займе в нас (750000 сек)/ 3600 / 24 = 8.68 діб > тижня. Тому заходимо на
https://dumps.wikimedia.org/ , вибираємо дамп який більше подобається, наприклад останній дамп що містить статті (без сторінок обговорень) української вікіпедії і ставимо на скачування.

А поки воно скачується підготуємось його розпаковувати. Ми скачуємо заархівований XML, який при розпаковуванні займає щось біля 5GB. Всередині є багато тисяч елементів , кожен з яких містить деталі про сторінку. Ось код на Go який містить функцію Read що розархівовує і водночас парсить XML, та повертає канал в який кидає сторінку за сторінкою, а в головній функції ітерується по всіх сторінках і підраховує кількість символів в їх тексті. В кінці виводить статистику:

Можна запускати його як go run main.go *.xml.bz2, а можна зробити go build і отримати скомпільований код, який вже запускати. Думаю за швидкістю вони співмірні.

Дізнаємось що українська вікіпедія станом на 3 березня (бо такий в мене дамп) складається з 4 138 529 133 символів, серед яких різних лише 24 190.

Різних символів не так вже й багато, для порівняння остання версія юнікоду має 136 755, але все одно забагато як на мене. Наприклад якщо захочеться скласти таблицю як часто який символ йде після даного (біграми), то в ній буде 585 156 100 клітинок. Це треба буде пару гігабайт оперативки аби порахувати лише диграми. Сторінок там приблизно 1 600 000 (а не 750 000), тому що крім статтей є ще шаблони, категорії, і тому подібні речі. Тому я думаю треба відкинути неважливі символи, наприклад взявши за поріг що символ має з’являтись у вікіпедії хоча б 16 000 разів (якщо менше, то шанс побачити його на випадково взятій сторінці менше 1%). А таких лише 229, і ось їхній номер в рейтингу, написання і кількість використань у вікіпедії:

229) 市: 17347
228) λ: 18004
227) ç: 18915
226) ε: 19037
225) č: 19328
224) ​: 19388
223) ñ: 19431
222) ل: 19786
221) ў: 20711
220) @: 21351
219) ą: 21381
218) É: 22425
217) ś: 22544
216) š: 22885
215) ′: 23578
214) ș: 23592
213) ρ: 23834
212) ±: 24008
211) ς: 24200
210) τ: 24465
209) ι: 25389
208) à: 25608
207) ę: 25616
206) ν: 25716
205) ń: 25991
204) ”: 29662
203) ا: 32288
202) ο: 35475
201) ‎: 36211
200) „: 38162
199) →: 38323
198) í: 40358
197) ş: 42223
196) α: 43669
195) †: 45094
194) Ь: 46660
193) è: 50851
192) ö: 50927
191) “: 51455
190) ä: 51800
189) −: 52985
188) $: 55963
187) ё: 55973
186) ²: 58338
185) ţ: 60505
184) ×: 61094
183) ă: 63880
182) ъ: 64358
181) á: 65593
180) ^: 68237
179) ł: 69450
178) Э: 69917
177) Ї: 72349
176) ü: 74505
175) â: 86866
174) ½: 92706
173) э: 97009
172) …: 98257
171) ~: 104201
170) ó: 113417
169) °: 138459
168) Щ: 144944
167) Ґ: 153541
166) ·: 170010
165) ’: 172548
164) é: 308459
163) №: 308648
162) И: 309865
161) Й: 333321
160) •: 349671
159) \: 352804
158) ґ: 364613
157) ́: 389166
156) Z: 408490
155) +: 522576
154)  : 555014
153) Ю: 573682
152) q: 574906
151) ы: 604142
150) : 630305
149) Ж: 690154
148) Y: 723229
147) X: 749583
146) Є: 765597
145) Q: 818419
144) Я: 930365
143) –: 942145
142) J: 961000
141) ?: 1046419
140) Ц: 1098377
139) W: 1185842
138) Е: 1540741
137) O: 1628778
136) Х: 1696070
135) Ш: 1724585
134) K: 1802117
133) V: 1805294
132) H: 1816527
131) Ч: 1855640
130) j: 1864115
129) z: 2061416
128) U: 2194056
127) G: 2744752
126) N: 2852071
125) M: 3061885
124) #: 3154223
123) L: 3181356
122) !: 3189165
121) B: 3369071
120) З: 3437745
119) x: 3452972
118) F: 3520991
117) T: 3583564
116) %: 3642707
115) І: 3655626
114) P: 3725526
113) E: 3972385
112) »: 3977385
111) «: 3980397
110) Ф: 4202078
109) I: 4388726
108) R: 4396989
107) У: 4451260
106) О: 4554868
105) Б: 4662681
104) C: 4700683
103) щ: 4747530
102) Т: 4828013
101) Р: 5285196
100) Д: 5297868
99) A: 5304565
98) Н: 5308271
97) D: 5412754
96) Л: 5463510
95) v: 5601059
94) S: 5771420
93) —: 5876110
92) Г: 6196594
91) *: 6308184
90) _: 6536077
89) М: 6714179
88) є: 6725025
87) k: 6994569
86) &: 7431439
85) А: 8011695
84) В: 8034456
83) ф: 8301528
82) y: 8544168
81) ;: 9651561
80) С: 9778231
79) 7: 9836255
78) ш: 10032005
77) ю: 10424706
76) П: 10504497
75) w: 10609713
74) (: 10822363
73) ): 10861049
72) 6: 10922925
71) К: 11311101
70) 8: 11392131
69) “: 11480435
68) 4: 11994852
67) 5: 12855281
66) 3: 13149018
65) ж: 13461373
64) >: 14339625
63) <: 14353972
62) ї: 14915551
61) h: 15043805
60) m: 15056183
59) х: 16421470
58) f: 16484180
57) u: 17314042
56) g: 17753361
55) b: 18992562
54) 9: 19917121
53) :: 20761022
52) ц: 21063193
51) ч: 21089655
50) d: 21132407
49) p: 22184976
48) /: 22493164
47) c: 22688474
46) {: 23253440
45) }: 23256779
44) ,: 24259060
43) 2: 25895208
42) б: 27028220
41) ': 27719818
40) -: 29433960
39) й: 29504263
38) г: 32647216
37) з: 34131397
36) 0: 34211416
35) l: 35713324
34) .: 36224151
33) s: 36570492
32) o: 36864174
31) 1: 37256409
30) ь: 37873838
29) i: 37914159
28) я: 40403600
27) n: 41935115
26) r: 43452098
25) п: 45627724
24) a: 47339118
23) t: 49644507
22) м: 51192263
21) =: 52585455
20) д: 54742090
19) у: 56282864
18) e: 60511715
17)
: 68855138
16) к: 77929326
15) л: 78327225
14) [: 81918527
13) ]: 81923177
12) с: 85389668
11) в: 88260931
10) |: 90345542
9) т: 99966314
8) е: 100782938
7) и: 110643874
6) р: 118651169
5) і: 130028472
4) н: 154575149
3) о: 178231842
2) а: 180553597
1) : 506900824

Цих 229 символів використовуються сумарно 4 136 342 085 разів, а це складає 99.95% всіх символів вікіпедії. Тому рештою думаю дійсно можна знехтувати.

Як написати бота до Telegram?

Легко. 🙂 Давайте напишемо бота який перекладатиме нам всяке з німецької:

Приклад діалогу

Для цього нам треба поговорити з botFather-ом:

А зараз трохи не по темі цієї статті. Ось код який перетворює вікідані на словник, шукаючи всі сутності які мають мітки однією мовою, а потім показучи їх мітки іншою мовою, використовуючи хитрий запит SPARQL:

import json
import requests

def translate(from_lang, to_lang, word):
    '''
        Переклдає мітки елементів вікіданих з мови на мову. Повертає список варіантів перекладу
    '''
    res = sparql('''
        SELECT  ?ukLabel WHERE {
          ?item ?label "%s"@%s.
          ?item rdfs:label ?ukLabel filter(lang(?ukLabel) = "%s")
        } LIMIT 10
    ''' % (word, from_lang, to_lang))
    return list(map(
        lambda e: e['ukLabel']['value'],
        res['results']['bindings']
    ))

def sparql(query):
    ''' Отримує JSON дані запиту SPARQL до вікіданих '''
    res = requests.get(
        'https://query.wikidata.org/sparql',
        params={
            'query': query,
            'format': 'json'
        }
    )
    return json.loads(res.text)

А тепер повертаємось до теми телеграмного бота. Аби його написати треба поставити бібліотеку:

pip install pyTelegramBotAPI

Ось її Github: https://github.com/eternnoir/pyTelegramBotAPI

А далі – елементарно як писати консольну програму:

import telebot

TOKEN = '' # тут вставити те що BotFather сказав

bot = telebot.TeleBot(TOKEN)

@bot.message_handler(content_types=["text"]) # Якщо прийдуть нові повідомлення
def respond_to_message(message):
    translations = translate('de', 'uk', message.text) # Отримати переклади тексту повідомленя
    resp = '\n'.join(translations) if translations else 'На жаль, перекладу слова %s не знайдено' % message.text
    bot.send_message( # відправити назад
        message.chat.id, # в той самий чат з якого прийшло (можна напевне й в інший)
        resp
    )

if __name__ == '__main__':
     bot.polling(none_stop=True) # Запустити бота аби той сидів на лінії і слухав повідомлення.

Поки що все, бо й висипатись іноді треба. Пізніше нагадайте мені не забути написати більше про SPARQL, як поставити собі локальну mediawiki і розширення до неї, як логінити сторонні застосунки через OAuth, і як переписати інтерфейс вікіпедії на Vue.js. 🙂


Filed under: Кодерство, Павутина Tagged: вікіпедія, Python

Як написати свій букмарклет?

Букмарклет (bookmark) – це слово створене поєднанням слів bookmark (закладка) і applet (application (застосунок) + зменшувальний суфікс -let). Таким чином правильним перекладом bookmarklet було б “закластуночок” (чи “застокладочка”), але ви цього краще не кажіть Юрку Зеленому. Але я відхиляюсь. Отож, букмарклет, це маленька програма що міститься в закладці.

Як її туди помістили? Ну, виявляється браузери крім протоколів http, ftp, ітп, розуміють такий протокол як javascript. І коли отримують посилання з цим протоколом, при кліку по ньому не змінюють сторінку, а виконують його href як код JavaScript. Щоб побачити приклад простого букмарклету, створіть документ bookmarklet.htm, і помістіть в нього такий код:

<html>
<head><meta charset="utf-8" /></head>
<body>
<a href="javascript:
    alert('Привіт!')
">Мій букмарклет</a>
</body>
</html>

Якщо відкрити його в браузері, ви побачите чисту сторінку з єдиним посиланням Мій букмарклет, яке при кліку буде показувати повідомлення “Привіт!”. Якщо перетягнути це посилання на панель закладок в браузері, то тепер ви зможете в будь-який момент отримати привітання на будь-якій сторінці яку дивитесь, досить лише натиснути букмарклет.

Але це не дуже корисний букмарклет. Щоб він робив щось корисніше, досить замінити код в рядку 5, на якийсь корисніший.

Наприклад є такий сайт – lib.rus.ec. Вони хоч мають домен еквадору – але москалі, і коли відкрити їх сайт, то вискакує повідомлення, яке блокує доступ до сторінки, і просить грошей за чужі книжки. Я переконаний що в цьому випадку дозволяється використати принцип “грабуй награбоване”, і додати в букмарклет такий код:

 document.getElementsByClassName('lsp-overlay')[0].remove()

І тоді при кліку по букмарклету це вікно пропадатиме. Клас, правда? (Не забудьте забрати виклик alert() який ми написали там раніше)

Але давайте зробимо ще щось складніше і корисніше. Давайте зробимо букмарклет, який оформлює посилання на веб-сторінку в форматі вікіпедії cite web, і дає її нам в якомусь вікні. Це такий шаблон, який гарно оформлює посилання, але заповнювати його руками – ще та морока.

Для букмарклетів є кілька правил. Їх код все таки міститься всередині атрибуту елемента HTML, і повинен відповідати стандартам цієї мови. Наприклад всі подвійні лапки """ (ну ви зрозуміли), повинні бути заміненими HTML-кодом ". Крім того, перехід на новий рядок вважається еквівалентним пробілу, тому однорядкові коментарі можливі лише в кінці скрипта:

alert('Це вискочить');
// alert('Це не вискочить бо закоментовано');
alert('Це теж не вискочить бо коментар продовжується');

Ну і тому що ввесь скрипт здається браузеру написаним на одному рядку – бажано шанувати крапки з комою.

Якщо ми хочемо написати великий букмарклет, який можливо використовуватиме jQuery, та інші бібліотеки, нам захочеться їх якось підвантажувати. Але, віднедавна в адміністраторів серверів з’явилась нова можливість збільшувати безпеку вводячи обмеження на джерела з яких на сайт завантажуються ресурси – Content Security Policy, тому такі букмарклети можуть працювати не всюди. Можливо доведеться все переписати, мініфікувати і запакувати, але на всяк випадок, ось вам код підвантаження скрипта:

(function () {
    var script = document.createElement('script');
    script.src='https://gist.githubusercontent.com/bunyk/6ae97e5c3de490cfb4a1/raw/b7fcfde5c40314262893761418a6a25dd6ed0ce8/cite_web.js';
    document.body.appendChild(script);
}());

Подібним чином вже з того скрипта ми можемо підвантажити jQuery та наприклад CSS.

Я тут наковбасив трохи JavaScript, який можливо не на всіх сайтах працює, і не всі дані статті витягує, але на моєму блозі він дає щось подібне до цього:

cite_web

Щоб поставити собі такий, перенесіть оце посилання в закладки: “{{cite web|…“, а тоді в контекстному меню закладки натисніть “Властивості”, чи “Редагувати”, і замініть текст на

(function () {
    var script = document.createElement('script');
    script.src='https://gist.githubusercontent.com/bunyk/6ae97e5c3de490cfb4a1/raw/3102c3e285c6e24a9aed98d1bc7f0b7abeda20fc/cite_web.js';
    document.body.appendChild(script);
}());

Ось код:


Filed under: Кодерство, Павутина Tagged: Лайфхаки, вікіпедія, JavaScript

Генштаб для грамнацистів

Щойно з’явилась ідея супер-пупер сервісу для всіх хто пише і вичитує.

Сценарії використання

  1. Користувач бачить помилку на якомусь сайті. (Ідея націлена на вікіпедію, але має працювати на будь-яких інших сайтах, наприклад тут.) Нехай він зареєстрований на нашому порталі грамнацистів, і встановив букмарклет. Користувач виділяє ту помилку, натискає на букмарклет, той букмарклет показує попап з формою, де вписаний рядок з помилкою, полем куди можна ввести правильний варіант, пояснення помилки і посилання на словники та правопис.
  2. Користувач пише статтю. Він хоче написати її грамотно. Він вводить її текст, натискає інший букмарклет, і той використовуючи алгоритм Ахо-Корасік чи щось легше, підкреслює всі помилки і в попапах показує коментарі користувачів що їх виправили, та варіанти виправлень.
  3. Користувач вважає що помилка – не помилка, і попередній користувач помилився додавши її до словника. В попапі він клацає кнопочку “обговорення” і переходить на наш портал, де може обговорити.
  4. Адміністратори/редактори (вікіпедії) вирішують хто правий, а хто ні. Помилка яка насправді такою не була, позначається, і при наступних спробах її ввести, йому будуть повідомляти що це не помилка, і якщо він не згоден – посилати в обговорення.
  5. Найпопулярніші помилки, які вже точно помилки експортуються в файл що читається роботом який лазить по вікіпедії і виправляє всі статті та перевіряє всі нові правки. Якщо користувач вніс правку з помилкою – йому приходить повідомлення з поясненням суті помилки, проханням так не робити і порадою поставити букмарклет. :) На зразок такого:

  6. Треба врахувати те, що помилок існує більше ніж правильних слів (наприклад в слові “пиво” можна помилитися 32^4 = 2^{20} \approx 10^6 мільйоном різних способів, і то якщо не враховувати що також існують пропуск, дефіс, апостроф та інші символи. Тому варто намагатись вводити лише часті помилки, а решту залишати на hunspell. Словник для hunspell теж дозволити редагувати спільно, через букмарклети.

P.S. Я ще можу змиритись з тим що Firefox мені підкреслює hunspell, latex, букмарклет, попап і подібне. Але “блозі”, “кнопочку” “P.S.” і т.п. міг би вже вивчити.


Filed under: Кодерство, Павутина Tagged: вікіпедія, освіта, цілі

Генштаб для грамнацистів

Щойно з’явилась ідея супер-пупер сервісу для всіх хто пише і вичитує.

Сценарії використання

  1. Користувач бачить помилку на якомусь сайті. (Ідея націлена на вікіпедію, але має працювати на будь-яких інших сайтах, наприклад тут.) Нехай він зареєстрований на нашому порталі грамнацистів, і встановив букмарклет. Користувач виділяє ту помилку, натискає на букмарклет, той букмарклет показує попап з формою, де вписаний рядок з помилкою, полем куди можна ввести правильний варіант, пояснення помилки і посилання на словники та правопис.
  2. Користувач пише статтю. Він хоче написати її грамотно. Він вводить її текст, натискає інший букмарклет, і той використовуючи алгоритм Ахо-Корасік чи щось легше, підкреслює всі помилки і в попапах показує коментарі користувачів що їх виправили, та варіанти виправлень.
  3. Користувач вважає що помилка – не помилка, і попередній користувач помилився додавши її до словника. В попапі він клацає кнопочку “обговорення” і переходить на наш портал, де може обговорити.
  4. Адміністратори/редактори (вікіпедії) вирішують хто правий, а хто ні. Помилка яка насправді такою не була, позначається, і при наступних спробах її ввести, йому будуть повідомляти що це не помилка, і якщо він не згоден – посилати в обговорення.
  5. Найпопулярніші помилки, які вже точно помилки експортуються в файл що читається роботом який лазить по вікіпедії і виправляє всі статті та перевіряє всі нові правки. Якщо користувач вніс правку з помилкою – йому приходить повідомлення з поясненням суті помилки, проханням так не робити і порадою поставити букмарклет. :) На зразок такого:

  6. Треба врахувати те, що помилок існує більше ніж правильних слів (наприклад в слові “пиво” можна помилитися 32^4 = 2^{20} \approx 10^6 мільйоном різних способів, і то якщо не враховувати що також існують пропуск, дефіс, апостроф та інші символи. Тому варто намагатись вводити лише часті помилки, а решту залишати на hunspell. Словник для hunspell теж дозволити редагувати спільно, через букмарклети.

P.S. Я ще можу змиритись з тим що Firefox мені підкреслює hunspell, latex, букмарклет, попап і подібне. Але “блозі”, “кнопочку” “P.S.” і т.п. міг би вже вивчити.


Filed under: Кодерство, Павутина Tagged: вікіпедія, освіта, цілі

bwikibot 0.4.2, відтепер з автовиправленням правопису!

Автовиправлення правопису, як пізніше виявилось – передчасна ідея, але суть публікації в тому що bwikibot активно розвивається і на сьогодні вже має версію 0.4.13. Очікуйте 0.5.0!
Правда не всього правопису, а лише деяких іменників чоловічого роду в родовому відмінку, але й це вже краще ніж нічого.

bwikibot встановлюється просто як:

sudo pip install bwikibot

Або, якщо цією програмою ще хтось крім мене користується (дякую вам) і вже встановив, то оновлюємо:

sudo pip install --upgrade bwikibot

Нагадаю що pip повинен бути для третього Python, підтримка другого можлива лише теоретично, якщо хтось допоможе мені з тестами.

Після чого виправлення правопису запускається командою:

python3 main.py spell "Пориньте у Python 3/Модульне тестування"

correction_diff

І якщо вказана сторінка існує на вікі, на яку він залогінений, то текст цієї сторінки буде прочитано, будуть здійснені всі можливі автозаміни, вам буде показаний диф між новою і старою версіями (треба буде подумати над тим, як зробити так аби цей диф виглядав коротшим) і після отримання від вас згоди на запис, виправлена версія записана назад у вікі.

Що він може? Небагато, поки-що. В нього є словник genitive_a_u.txt, в якому містяться слова чоловічого роду в родовому відмінку з закінченнями “а”, “у”. Словник поки що невеликий, містить сім слів: аргументу, графа, менеджменту, методу, об’єкта, параметра, файла.

Проте це слова які я не вмію правильно писати, хоча пишу часто. :) Словник буде доповнюватись. Потім може ще додам словник автовиправлень частих помилок загального виду, і якісь n-грами.

Ну і щойно виявив помилку. Мій коректор хоче замінювати слово “аргументами” на “аргументуми”. :) Треба згадати що там в кінці шаблону треба поставити… Здається "аргумент\b"?

А ще, коли проект виросте до справді корисного, напевне варто буде його відокремити і додати можливість перевірки і автовиправлення блоґу через xml-rpc.

Ах, і якщо хтось знає як не винаходити велосипед, а інтегрувати aspell чи щось подібне – поділіться досвідом.

Доповнення на 16:15 : Помилку з “аргументуми”, занадто довгі дифи, виправлено в версії 0.4.3, також для цієї версії тести проходяться як в Python3 так і в Python2, але вручну я тестував тільки 3.


Filed under: Інструменти, Кодерство, Павутина Tagged: вікіпедія, мова, Python

Кібернетична вікі. Епізод II. Атака ботів

Не так давно
на одному
сервері кібцентру…

В Кібернетичній вікі
неспокійно. Більше сотні
користувачів демонструють
наміри закидати її спамом.

Рух спамерів очолюваний
загадковим невідомо ким
(чи взагалі ніким не очолюваний),
перешкоджає малочисельним кіберджедаям
зберігати чистоту й порядок на сайті.

Адміністрато bunyk повертається в КібВікі
щоб зайнятись принциповим питанням: створенням
бота для допомоги джедаям в малоуспішній боротьбі…

Пафосні титри подивились – переходим до банального сюжету про те як добро перемогло зло. :) Зі мною була нова сила (Python 3), і розуміння того що об’єктно-орієнтоване програмування призначене для зменшення кількості подібних параметрів в різних методах. І знання PEP8 яке дозволяє писати код на 8 та більше балів за Пайлінтом.

До того дії всіх ботів я відкидав вручну, слідкуючи за вікі через RSS. Коли їх було 1-2 на день це ще можна було терпіти. Але коли вони повалили десятками я на деякий час забив.

Я вирішив не морочити собі голову з PywikipediaБотом, для роботи якого на cybwiki ще треба створювати окремий файл конфігурації, а змайструвати свого. Свого писати набагато приємніше ніж лазити по сотнях файлів чужого. Крім того, мені дуже соромно за старий bunykwikibot, бо той код – блювотний. Зрозуміло що рік тому я цього не міг бачити. Тому маючи ще один день відпустки який залишився з проходження практики вирішив використати цей шанс щоб виправити ситуацію.

Знайомтесь – bunykwikibot2. Поки що різноманітних запитів до вікі в нього менше. Але зате він вміє логінитись і писати. І з ним приємно працювати.

Покажу на прикладі сьогоднішнього бою проти спамерів (ввесь код):

#!/usr/bin/python3

Ага. Рухаємось вперед. Бо якщо не ми, то хто?

from api import Wiki

Простіше простого. І не треба завантажувати штук п’ять функцій!

def hunt_bots():
    wiki = Wiki('http://cybportal.univ.kiev.ua/w/api.php')
    wiki.session_file('bunyk_on_cybwiki.session')

Це в нашого скрипта головна функція. При запуску створює новий клієнт wiki, для API за заданою адресою. Другий рядочок каже що дані сесії (куки і ще дещо) будуть завантажуватись з вказаного файлу. А якщо їх там нема – попросити в користувача залогінитись і записати туди дані сесії.

    def marked_bot(user):
        userpage = user.userpage().read() or ''
        return '{{Spambot}}' in userpage

Як розрізнити спам і не спам? Пол Грем написав статтю про Баєсівський класифікатор, а я як останній неуч її все ще не читав. Тому вирішив просто позначити всіх ботів вручну, шаблончиком {{Spambot}}. Функція вище отримує текст сторінки користувача, і дивиться чи там не написано що користувач – бот.

Зрозуміло бота ще треба позначити. Цим займається наступна функція:

    def shoot_bot(bot):
        bot.userpage().write('{{Spambot}}', 'Попався!')

Перший її рядок замінює текст сторінки бота позначкою про те що він бот, супроводжуючи цю дію бойовим криком “Попався!”.

        for contrib in bot.contributions():
            spam_page = wiki.page(contrib['title'])
            only_bots = True
            for user in spam_page.contributors():
                if not marked_bot(user):
                    only_bots = False
                    break
            if only_bots:
                spam_page.delete('Спам.')

Далі по черзі розглядаються всі сторінки які бот редагував. І якщо єдиними редакторами цих сторінок були вже позначені боти – сторінки теж видаляються.

        bot.block('Die spammers, die!')

Після цього бот блокується з іншим грізним криком.

Але основна частина скрипта – визначення хто бот, а хто ні:

    for user in wiki.all_users:
        userpage = user.userpage()
        page_text = userpage.read()
        if page_text:
            if '{{Spambot}}' in page_text:
                print(user.name, '- ботяра')
                continue
            if '{{Approved}}' in page_text:
                print(user.name, '- чесний')
                continue
            print(page_text)
        else:
            print('Сторінка користувача порожня')
        edits = user.edit_count()
        print(userpage.title, 'Редагувань: %d' % edits)

Я розглядав кожного користувача. Якщо він вже позначений – виводив повідомлення і брав наступного. Якщо ж ні – виводив текст його сторінки користувача, логін і кількість редагувань.

        if edits < 3:
            verdict = True
        else:
            verdict = ask_y_n('Це бот, як думаєте?'
                '(y/n або порожній рядок якщо не знаєте)'
            )

Спочатку кожну справу я розглядав вручну. Потім до мене якось дійшло що кожен бот залогінювався і робив не більше двох редагувань. Видно вони на більше не розраховані бо їх банять і доводиться реєструвати нові аккаунти. Правда через це мало не “постраждав” Номіровський тому що статтю про себе він редагував переважно анонімно, але я все виправив руками.

        if verdict == '':
            continue
        elif verdict:
            shoot_bot(user)
        else:
            userpage.append(
                '{{Approved}}',
                'Підтвердження того що власник сторінки - не бот.'
            )

Ну і коли сторону користувача визначено, він або розстрілювався, або позначався як наш. Якщо не визначено – пропускався.

Ну після довгої підготовки бій був коротким:
Робота бота

Результати: заблоковано 114 ботів. 40 нормальних користувачів підтверджено.

Тепер напевне пора думати що робити з наступними навалами? Банити всіх хто не поставить на свою сторінку відповідний шаблон, а анонімів за географічною ознакою? Чи таки вчити баєсівський класифікатор розрізняти дифи? Побачимо.


Filed under: Кодерство, Павутина Tagged: CybWiki, вікіпедія, Python