## Как работает 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: ```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`, где строятся подготовленный текст и предметный указатель.