Ріп мережних за допомогою словників Node.js, ч. 2: динамічні сторінки; підключення NW.js

попередній частині були описані базові операції і супутні завдання при копіюванні мережевих словників за допомогою Node.js. В цій частині описується використання важливого додаткового інструменту для конвертації веб-джерел особливого рівня складності.

I. Навіщо нам NW.js?
1. Чим складніша структура веб-сторінок словника, тим більше підстав спиратися на весь спектр можливостей, що надається відточеним браузерних движком. JSDOM — досить розвинена бібліотека, але навіть вона не зрівняється з повним набором засобів з Chromium.

2. Люди, які займаються створенням і конвертуванням цифрових словників, — значною мірою гуманітарії, яких волею долі занесло в сферу IT. Іноді їм комфортніше працювати з GUI, ніж з інтерфейсом командного рядка, особливо якщо вони не пишуть утиліти самі, а користуються готовими розробками колег. NW.js надає прості способи створення GUI до тривіальним додатків для аналізу, обробки і конвертації веб-сторінок.

Як приклад для короткого опису цього інструменту я вибрав сайт www.wordspy.com. Word Spy — постійно поповнюється словник англійських неологізмів, які вже встигли стати частиною мови. Тобто не були створені і одноразово вжиті авторами для приватних потреб (такі слова називаються «окказионализмами»), але «засвітилися» в кількох друкованих і мережевих джерелах різного походження. Порівняно з Urban Dictionary, послужившему ілюстрацією для першої цієї статті, Word Spy є дві істотні відмінності: вміст сторінок формується асинхронної роботою скриптів, а структура цих сторінок значною мірою непередбачувана і складна (тоді як в Urban Dictionary для текстів словникових статей використовувався гранично малий набір тегів, а їх порядок і поєднання були единообразны). Це і стало вирішальною причиною звернутися до NW.js.

Я не планую повторювати тут частині офіційній документації, вже досить повній і систематичної, — якщо ви ще не знайомі з NW.js, краще почати з неї (потім можна перегорнути wiki-сторінки на GitHub — хоча багато з них вже застаріли, там все ще трапляється щось цікаве, не згадане в основній документації). Обмежуся лише нотатками про застосування проекту до обраної задачі.

II. Підготовчий етап
1. Отримання списку адрес словникових статей
В основному, перший підготовчий скрипт буде багато в чому нагадувати програму з першою статті. Ми поки навіть не будемо підключати NW.js, оскільки нам потрібно буде лише повисмикувати необхідні посилання зі сторінок, а з цим успішно впорається і JSDOM.

Код скрипта
.

Зазначу лише суттєві відмінності.

а. Оскільки до часу завантаження сторінки і отримання функцією, що реагує на цю подію, об'єктів
window
та
document
вміст сторінки ще не готове, нам потрібно буде ввести додатковий цикл перевірок (оскільки сторінка наповнюється асинхронної роботою скриптів, відстеження події
load
нам нічого не дасть; можна було б повісити обробники подій на зміни DOM, але в даній ситуації це здається невиправданим ускладненням). Проаналізувавши роботу сайтовых скриптів, ми знаходимо якийсь показовий елемент сторінки, наявність якої означає завершення будівництва потрібної нам структури (у даному випадку блоку зі списком посилань на словникові статті). Селектор цього елемента ми і визначаємо додатково до вже знайомим нам змінним (
selectorsToCheck
в початковому блоці коду; на той майбутній випадок, коли для різних сторінок потрібні різні перевірочні елементи, ми зробимо цю змінну масивом). Другим додаванням буде число мілісекунд, що задає періодичність перевірки ключового елемента (
checkFrequency
).

b. Word Spy містить зручне двоступеневе зміст всього словника: 1) список усіх тегів, розбитий на кілька тематичних блоків; 2) посилання на кожен з тегів відкриває список усіх вокабул, що належать до цього тегу. Ми додамо до нашого словника як перший список тегів, так і всі списки вокабул під тегами. Для цього в наш первісний масив адрес (
tocURLs
), який стане джерелом списку словникових статей, ми додамо згадану відправну сторінку з тегами. Так само, на відміну від скрипта з першої статті, де цей масив називався
abc
, ми відразу перетворимо його в список URL, а не будемо формувати його на льоту з алфавіту, так як адресу з тегами не вписується в єдиний шаблон URL.

ст. Дещо в нашій теперішній задачі спроститься: Word Spy — словник, на порядки менший за обсягом порівняно з Urban Dictionary, тому і списки адрес, і словникові статті у нього односторінкові. Нам не доведеться перевіряти наявність багатосторінкових продовжень в цьому скрипті, ні в скрипті збереження самого словника, що спростить і побудова URL, і відповідні ділянки коду.

р. функції
getDoc
трохи змінюється бібліотечний запит
jsdom.env
: Urban Dictionary був статичним словником, тут же нам доведеться вимагати завантаження і виконання скриптів на сторінках, що відображається в опціях запиту.

д. Оскільки в нашому коді з'являється ще один асинхронний момент, ми розділимо колишню функцію
processDoc
на дві: у функції
checkDoc
ми будемо перевіряти як можливі помилки, так і закінчення роботи сайтовых скриптів, а обробку готового документа перенесемо в відстрочену функцію
processDoc
. Цикл перевірки здійснює деяке число ітерацій (скажімо, поки не пройде 5 секунд). Якщо за цей час з'явився перевірочний елемент, ми переходимо до функції обробки документа. Якщо елемента після тайм-ауту немає, ми перевіряємо, чи не було переадресації: якщо немає, можна запідозрити затримку на сервері і повторити запит, якщо сервер переадресував нас кудись, залишається лише видати попередження користувачу і тимчасово завершити роботу програми. Досвід показав, що на відпрацювання сайтовых скриптів в більшості випадків потрібно 100-400 мілісекунд, хоча іноді затримка становила і кілька секунд, і лише зрідка перевищувала тайм-аут (у таких випадках достатньо було одного повторного запиту).

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

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

Код скрипта
.

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

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

b. Масив ключових селекторів
selectorsToCheck
буде містити тепер два елемента: для звичайних сторінок словника і сторінки зі списком тегів (або вокабул, об'єднаних одним тегом).

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

р. Аналіз кожної сторінки буде полягати в отриманні всіх тегів з нас цікавить елемента, реєстрації їх імен в суммирующем об'єкт
tags
(з постійним збільшенням статистики по кожному тегу), записи в файл адреси сторінок і списку тегів на ній. В кінці роботи скрипта у файл записується також підсумковий об'єкт
tags
. Таким чином ми отримуємо загальну статистику тегів, так і розподіл їх по сторінках, що дає нам можливість подивитися приклади вживання тега, відкривши будь-який з адрес, під яким цей тег записаний. Якщо робота скрипта переривалася, вже записаної в файл інформації ми можемо відновлювати статистичний об'єкт
tags
. Ці два схожих процесу — читання сторінок і читання вичавок лода — ми бачимо в двох відповідних місцях скрипта: в початковій частині (під рядком
console.log('Reading the tag file...');
) і в функції
processDoc
.

В іншому код програми не містить нічого незнайомого.

III. Збереження словника
Програми NW.js складаються як мінімум з двох файлів: службового у форматі JSON, що описує основні параметри програми, і сторінки HTML, що описує GUI і містить сценарії. Останні можна винести в окремий файл (файли) і послатися на них з локального або мережного адресою.

1.
package.json

Ось мінімальний вміст нашого службового файлу:

{
"name": "NW.WordSpy.get_dic",
"main": "WordSpy.get_dic.html",
"window": {
"title": "Save WordSpy.com"
}
}


Програми NW.js при першому запуску створюють в системній папці для даних користувача свою папку, і назва її буде сформовано за назвою програми з поля
name
.

Поле
main
містить шлях до основного файлу з елементами GUI і головним скриптом програми.

Необов'язковий підрозділ
window
містить параметри створюваного вікна програми, і ми поки обмежимося заголовком.

Детальніше про формат і складових службового файлу можна прочитати в довідці.

2.
WordSpy.get_dic.html

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

Код HTML-сторінки
.

В заголовну частину розмітки, крім необхідного мінімуму, можна додати довільний блок CSS. Тут він носить чисто ілюстративний характер, і ми не будемо на ньому зупинятися.

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

Далі слід кнопка, яка запускає основна дія програми, а за нею — поле для виводу інформації. За допомогою CSS і деяких скриптових хитрощів ми зробимо його схожим на вікно виводу консолі, щоб зберегти зв'язок з звичними консольними варіантами наших скриптів.

Невидимий елемент
audio
буде служити для залучення користувальницького уваги — раніше ми користувалися для цього консольним плеєром. Адреса звукового файлу може бути будь-яким іншим, я використав один із системних файлів стандартної звукової схеми подій.

Нарешті, останній елемент буде грати ключову роль «браузера» — у цей вбудований фрейм ми будемо завантажувати наші сторінки для аналізу та вилучення даних. Про особливості кадрів NW.js та деяких пов'язаних з ними застереження можна почитати по вже знайомій нам посилання.

Вид програми на початку і в кінці процесу збереження словника можна оцінити по скріншотам:





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

3.
WordSpy.get_dic.js

Код скрипта
.

В коментарях я спробую зупинитися тільки на відмінностях і нововведення в порівнянні з консольним скриптом з попередньої статті, тому що і основна структура, і безліч ділянок коду будуть загальними.

а. Перша відмінність ми бачимо на початку вступної частини. З'являються змінні для вікна та документа самої програми (так нам буде легше не плутати їх з змінними вікна та документа, що завантажуються сторінок), а потім і для кожного елемента GUI. Оскільки шляхи до файлів будуть будуватися динамічно (не раз і назавжди ключів командного рядка, а у відповідь на дії користувача), ми будемо зберігати їх як змінюються властивості об'єкта
io
, а не як розрізнений набір констант. Ще одна відмінність — набори селекторів різного призначення для більш зручних маніпуляцій зі складною структурою документа (вони нам вже знайомі по попереднім скрипту для аналізу тегів). Нарешті, оскільки інтерактивність зростає, наприкінці вступної частини ми створимо кілька змінних-індикаторів для поточного стану програми та команд.

b. При роботі з GUI спокуса не вчасно закрити вікно більше, ніж при роботі з консоллю. Тому ми створимо трохи більшу систему запобіжників від некоректного завершення програми. Для початку призначимо обробником закриття вікна функцію
onExit()
, про дії якої скажемо пізніше.

ст. Як ми могли бачити з довідки, стандартні заходи HTML 5 залишилися в силі, і ми не можемо ставити кінцеві адреси файлів за допомогою атрибутів чи властивостей наших файлових полів — це можна зробити тільки користувальницьким дією через діалогове вікно. Але ми можемо скоротити час і зусилля, зберігаючи шлях до папки, в якій користувачеві пропонується вибрати файл (а якщо файл і папка, попередній і кінцевий адресу дивом співпадуть). Для цього ми скористаємося ще одним службовим файлом в форматі JSON
config.json
, в якому будемо зберігати об'єкт з двома властивостями, за числом потрібних нам шляхів. На початку роботи програма буде перевіряти наявність цього файла: якщо він є, вона прочитає вміст об'єкт
config
і запише у властивості
nwworkingdir
для обох полів потрібні шляху. Якщо файлу немає, об'єкт буде порожнім і початковий каталог буде визначено звичайним для браузера чином.

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

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

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

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

ж. В функції
setSpeedInfo()
значна зміна торкнулося лише звукового сигналу. Я поки залишив частоту оновлення і формат інформації про швидкості роботи на колишньому рівні (раз на годину), але при необхідності їх можна підкоригувати (адже Urban Dictionary зберігався багато днів, а Word Spy — близько півтори години, так що частоту перерахунку та одиниці виміру можна підвищити до хвилин).

з. Функція
updateInfo(str)
відповідає за уподібнення інформаційного блоку консолі. Ми задаємо розмір буфера в 10 рядків і обрубаем зайві рядки спочатку (там найстаріша інформація), прокручуючи блок до останнього рядка. Через цю функцію ми виводимо постійно поточну інформацію в процесі збереження. При невеликих словниках таку поведінку можна відключити (тоді збережеться весь протокол ріпа), але при довгому процесі подібні обмеження економлять пам'ять і видаляють надмірність (тим більше що все потрібне пишеться в логи).

в. Функція
logError(evt)
покликана реагувати на подію
error
у вікні вбудованого фрейма. У мене вона поки жодного разу не спрацювала.

. Функція
secureLow(str)
служить низькорівневої обробки тексту завантажуваних сторінок для приведення його до вимог DSL, а саме для екранування спецсимволов. Тоді як
secureHigh
служить для обробки текстових блоків (видалення зайвих пробілів, вставка відступів перед тілом словникових статей DSL, спеціальна вставка для збереження порожніх рядків). В консольному варіанті з першою статті ми обходилися однією функцією, але тут наш порядок вилучення та оформлення інформації дещо зміниться, і нам доведеться цю обробку розділити.

к.
saveDic()
— основна функція програми, яка запускається при натисканні на кнопку збереження словника. Вона багато в чому відповідає початковій, процедурної частини нашого консольного скрипта з першої статті, але є і ряд відмінностей. Першим ділом включаємо змінну-індикатор процесу збереження і змінюємо вигляд і поведінка головної кнопки: тепер вона відповідатиме за переривання процесу. Також відключаємо виконали свою роль файлові поля. Потім виробляємо вже знайомі маніпуляції з файлами: перевіряємо наявність списку адрес, створюємо заготовки словника і звітів, читаємо список адрес, читаємо інформацію про вже збережених сторінках при її наявності в балці збереження і за необхідності скорочуємо завдання, нарешті починаємо цикл збереження, запитуючи першу сторінку в списку. Новим на цьому відрізку коду буде завдання обробників подій
load
та
error
для вікна вбудованого фрейма, необхідних для роботи нашого циклу.

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

м. Функція
checkDoc()
запускається автоматично у відповідь на повне завантаження сторінки в наш вбудований браузер. Вона частково знайома нам за попереднім скриптам цієї статті. Тільки тепер ми починаємо її зі створення змінних, дозволяють нам не плутати головні об'єкти вікна програми і вікна завантаженої сторінки. Потім слід знайомий нам цикл перевірок на готовність вмісту сторінки. Залежно від його результатів ми або переходимо до обробки інформації, або перезавантажуємо сторінку, або завершуємо роботу з повідомленням про невідомої помилку.

н функції
processDoc(iWin, iDoc, iLoc, iter)
містить витяг, обробка і збереження словникових даних сторінки. Вона найбільш відрізняється від відповідної консольної частини коду — і в силу відмінностей словника, і з причини особливостей нового інструменту.

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

Потім ми формуємо заголовок майбутньої словникової статті.

Якщо це звичайна сторінка словника, ми витягуємо з потрібного елемента основний заголовок, а потім додаємо до нього варіанти написання, форми і деривати (ми вважали це необхідним, оскільки моделювання морфології неологізмів в оболонці словника може бути ускладнене, краще полегшити словником цю роботу). Тут ми вперше зустрічаємося з однією з головних причин підключення NW.js: властивістю елементів
innerText
. Воно не було доступним для JSDOM (пояснення причин), у розпорядженні бібліотеки було лише властивість
textContent
, дуже незручний для вилучення тексту з складних елементів (через змішування тексту розмітки (HTML) і відображуваного тексту). Властивість
innerText
забезпечує нам необхідну для складних сторінок впевненість: яке б не було будова словникової статті або її частин, ми візьмемо саме корисний читабельний текст (який ми отримали, копіюй ми інформацію з вікна сторінки системними засобами через буфер обміну). Це ж властивість дозволяє нам тимчасово виключати зайвий текст перед витяганням (так ми, наприклад, прибираємо граматичні поноси перед приміщенням форм слова в список заголовків): варто нам приховати непотрібні елементи, і їх текст не потрапляє до складу властивості (потім ми включаємо відображення, і помітити залишаються в складі тіла статті).

Якщо ж ми обробляємо одну з сторінок тегів, завдання спрощується — ми зберігаємо тільки основний заголовок, випереджаючи його поєднанням «# ». Таким чином, загальний список тегів можна буде знайти під вокабулой «# Tags by Category», а списки заголовків, об'єднаних одним тегом, — під вокабулами типу «# acronyms and abbreviations» і т. д.

Далі ми приступаємо до отримання та переоформлення основної частини статті. Список тегів, отриманий на попередньому етапі, дозволив нам накидати приблизний план обробки, так щоб ми могли звести несподіванки до мінімуму. Ця обробка буде деяким ризикованим компромісом: частина структури статей можна передбачити і впевнено використовувати, від інших сюрпризів можна лише грубо перестрахуватися.

При наших вставки і заміни ми будемо користуватися методом
insertAdjacentHTML()
, тому що він найбільш щадний по відношенню до структури розмітки.

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

Потім спускаємося трохи нижче по структурі. Укладаємо цитати в лапки. Розставляємо маркери списків. Виправляємо випадкові помилки розмітки (наприклад, в одній із статей псевдотеги
smirk
та
flame
випадково перетворюються з частини цитованого коду розмітку і зникають з надрукованого тексту). Вставляємо в текст вміст, що додається на сторінку за допомогою
CSS
і тим самим не включається до властивість
innerText
.

Після цього ми починаємо надавати відповідні елементи в теги DSL.

Виділяємо кольором і товщиною шрифту заголовки і підзаголовки. Виділяємо курсивом і товщиною шрифту елементи, які виділяються так на сторінці або за властивістю самих тегів, або через CSS. При цьому що ми можемо передбачити заздалегідь (і запитати за допомогою селекторів з назвами тегів або їх класів), а що-то нам доведеться з'ясовувати на льоту: для цього ми будемо запитувати всі елементи
span
статті, перевіряти їх обчислювані стилістичні параметри і при необхідності додавати теги. При цьому ми постараємося не дублювати одні і ті ж оформляють теги всередині один одного (на відміну від HTML, DSL таке не допускається) — для цього ми будемо позначати оброблені елементи спеціальним атрибутом, а потім перевіряємо його наявність вгору по дереву DOM.

Наступний крок — і ми позначили всі верхні та нижні індекси.

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

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

Нарешті, ми вставили додаткові відступи для виділення цитат (тобто прикладів вживання для кожного неологізм).

В результаті ми отримали два в одному: повністю збережену розмітку HTML, а всередині неї — розмітку DSL, при цьому без всякого конфлікту між ними. І коли ми запитаємо властивість
innerText
, від HTML залишиться лише структурований читабельний текст, наділений теги DSL, готовий для безпосереднього збереження в якості словникового коду.

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

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

4. Ресурси
Програма на NW.js, як вже говорилося, створює в системній папці користувача свою папку, в чомусь подібну папці профілю браузера. Можна припустити, що в ній, серед іншого, зберігаються файли кеша, прискорює завантаження однотипних сторінок. У моєму випадку ця підпапка після збереження словника посідала 138 мегабайт. В кінці роботи її можна безболісно видалити наступний раз програма створить її автоматично (хіба що на це піде певний час при першому запуску).

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

Словник (станом сайту на 16.02.2016) викладений на rghost.net і drive.google.com. Архів включає DSL-код у форматі UTF-8 і UTF-16, а також скомпільовані в LSD словники під три останні версії ABBYY Lingvo. Заголовків: 5827; карток: 3419; прикладів вживання: 9311.

Дякую за увагу.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.