Add loader and processor modules with documentation and output files

- Implemented loader in `modules/loader.py` to download HTML pages using curl and urllib.
- Created processor in `modules/processor.py` for processing the text of the Russian Criminal Code (УК РФ).
- Added README files for both loader and processor explaining their functionality and usage.
- Generated output files including original text, prepared text, and subject index in CSV and JSON formats.
This commit is contained in:
Dmitry
2026-04-16 21:38:00 +03:00
parent 502f48a279
commit 0b41577a42
15 changed files with 8770 additions and 103 deletions
+176
View File
@@ -0,0 +1,176 @@
## Как работает crawler
Crawler находится в `modules/crawler.py`. Его задача простая: пройти по страницам статей УК РФ на ConsultantPlus и вернуть только содержательный текст статей.
В коде используются: `urllib`/`curl` для загрузки, `lxml` и `XPath` для разбора HTML, регулярные выражения для очистки строк.
## Общий алгоритм
1. Загружаем стартовую страницу УК РФ:
```python
START_URL = "https://www.consultant.ru/document/cons_doc_LAW_10699/"
```
2. Ищем ссылки на статьи вида `УК РФ Статья 1...`.
3. Берем первую статью.
4. Загружаем страницу статьи.
5. Извлекаем текст из блока `document-page__content`.
6. Удаляем заголовки, служебные блоки и редакционные пометки.
7. Переходим к следующей странице по ссылке `pages__right`.
8. Повторяем, пока не дойдем до последней статьи или до лимита `max_pages`.
Главная функция:
```python
pages = crawl_document()
```
Она возвращает список словарей:
```python
{
"url": "адрес страницы",
"title": "заголовок статьи",
"text": "очищенный текст статьи",
}
```
## Загрузка страницы
Загрузка вынесена в `modules/loader.py`.
Для обычных сайтов используется `urllib`, а для ConsultantPlus сначала пробуется `curl`, потому что сайт иногда нестабильно отдает большие HTML-страницы.
В crawler используется функция:
```python
download_document_page(url)
```
Она делает несколько попыток с таймаутами `8`, `30`, `30` секунд. Для страниц статей проверяется, что HTML полный и содержит `</html>`.
Для стартовой страницы допускается частичный HTML:
```python
download_document_page(start_url, allow_partial=True)
```
Это нужно потому, что оглавление может успеть прийти даже тогда, когда сайт оборвал конец HTML.
## Определение страницы статьи
Страница считается статьей, если ее заголовок подходит под регулярное выражение:
```python
ARTICLE_RE = re.compile(r"^\s*(?:УК РФ,?\s+)?Статья\s+\d+(?:\.\d+)?\.")
```
То есть подходят такие варианты:
```text
УК РФ Статья 1. ...
УК РФ, Статья 53.1. ...
Статья 361. ...
```
Проверка выполняется функцией:
```python
is_article_page(page_html)
```
## Извлечение текста статьи
Текст достается функцией:
```python
extract_page_text(page_html)
```
Она делает четыре шага:
1. Парсит HTML через `lxml.html.fromstring`.
2. Находит основной блок:
```text
document-page__content
```
3. Удаляет служебные элементы:
- `h1`;
- `doc-style`;
- `doc-insert`;
- `doc-roll`.
4. Берет текст из абзацев `p`.
Дополнительно функция `clean_text()`:
- заменяет неразрывные пробелы;
- схлопывает лишние пробелы;
- убирает номера пунктов вроде `1.`, `2.`, `а)`.
## Удаление служебных строк
Функция:
```python
service_line(line)
```
убирает строки, которые не относятся к содержанию статьи:
```text
(в ред. Федерального закона ...)
(см. текст в предыдущей редакции)
(часть третья введена ...)
Президент
Б.ЕЛЬЦИН
13 июня 1996 года
N 63-ФЗ
```
## Переход к следующей статье
Основной способ перехода:
```python
extract_next_document_url(page_html, current_url)
```
Функция ищет правую ссылку ConsultantPlus:
```text
pages__right
```
Если такой ссылки нет, crawler использует запасной список `known_urls`. В него заранее складываются все найденные ссылки на статьи. Это помогает продолжить обход, если нижняя навигация не попала в загруженный HTML.
## Защита от лишних ссылок
Crawler проверяет, что каждая ссылка относится именно к УК РФ:
```python
is_same_document(url, prefix)
```
Для УК РФ правильный префикс:
```text
/document/cons_doc_LAW_10699/
```
Поэтому crawler не уходит в новости, другие кодексы, комментарии и внешние документы.
## Где используется результат
В `main.py` результат объединяется в один текст:
```python
pages = crawl_document(SOURCE_URL)
original_text = "\n".join(page["text"] for page in pages)
```
После этого `original_text` передается в `modules/processor.py`, где строятся подготовленный текст и предметный указатель.