Парсинг
Парсинг
Парсинг (parsing, синтаксический разбор) — процесс превращения плоской
последовательности символов или байтов в структуру, с которой может работать
программа: дерево, таблицу, набор объектов. Парсер — программа, которая это
делает, опираясь на правила формата (грамматику).
История
Само слово пришло из лингвистики: латинское pars orationis — «часть речи».
В английских школах XIX века «to parse» означало разобрать предложение по
членам — подлежащее, сказуемое, дополнение. Информатика забрала термин почти
без изменений: разница лишь в том, что вместо предложения — строка кода или
данных, а вместо школьника — программа.
Ключевые вехи:
- 1956 год — американский лингвист Ноам Хомский публикует иерархию
формальных грамматик (иерархия Хомского). Это математический фундамент:
она объясняет, какие языки можно разобрать простыми средствами, а для
каких нужен полноценный парсер. - 1959–1960 годы — Джон Бэкус и Питер Наур создают BNF (форма
Бэкуса–Наура) для описания синтаксиса языка ALGOL 60. Это первый
общепринятый способ записать грамматику языка программирования так, чтобы
по ней можно было построить парсер. - 1965 год — Дональд Кнут описывает LR-парсинг — метод, позволяющий
разбирать языки программирования за линейное время. На этой теории до сих
пор стоят генераторы парсеров. - Примерно 1975 год — Стивен Джонсон в Bell Labs пишет Yacc («Yet Another
Compiler-Compiler») — инструмент, который по описанию грамматики сам
генерирует парсер. Yacc и его свободный наследник GNU Bison десятилетиями
были стандартом. - 2004 год — Брайан Форд описывает PEG (parsing expression grammars) —
более практичный для программистов формализм; на его идеях построены многие
современные парсеры. - 2010-е — наши дни — расцвет «парсинга данных»: HTML-парсеры браузеров,
библиотеки вроде BeautifulSoup и tree-sitter, а слово «парсинг» в бизнесе
стало почти синонимом сбора данных с чужих сайтов (веб-скрейпинга).
Парсинг — не продукт, у него нет владельца. Это фундаментальная техника,
такая же базовая, как сортировка или поиск.
Что это такое
Компьютер хранит и передаёт всё как последовательность байтов. Файл на диске,
ответ сервера, лог, HTML-страница — это просто длинная лента символов. Но
программе, чтобы что-то сделать с данными, нужна структура: понять, что
вот тут — заголовок, тут — список из трёх элементов, а тут — число, а не
строка из цифр. Парсинг — это и есть переход от ленты символов к структуре.
Классический парсинг состоит из двух слоёв. Первый — лексический анализ
(токенизация): лента символов режется на «слова»-токены. Из строки
{"price": 100} получаются токены: открывающая скобка, строка price,
двоеточие, число 100, закрывающая скобка. Второй слой — синтаксический
анализ: из потока токенов по правилам грамматики собирается дерево —
обычно AST (abstract syntax tree, абстрактное синтаксическое дерево).
Полезные различения, которые часто путают:
- Парсинг vs регулярные выражения. Regex ищет плоские шаблоны в тексте.
Парсер понимает вложенность: скобки внутри скобок, теги внутри тегов.
Известный теоретический факт: регулярными выражениями в классическом
смысле нельзя корректно разобрать произвольно вложенные структуры —
например, HTML. Для этого нужен парсер. - Парсинг vs веб-скрейпинг. Скрейпинг — это вся задача целиком: скачать
страницы, обойти защиту, извлечь данные, сложить в базу. Парсинг — только
этап «извлечь структуру из скачанного». В русскоязычном бизнес-жаргоне
«парсинг сайтов» часто означает весь скрейпинг — это устоявшаяся, хоть и
неточная, привычка. - Парсинг vs сериализация. Это два конца одной трубы. Сериализация —
превращение структуры в текст (объект → JSON-строка). Парсинг — обратный
путь (JSON-строка → объект). - Парсинг vs валидация. Валидация отвечает «данные корректны?», парсинг —
«вот структура данных». Хороший парсер делает и то и другое сразу: если
разбор удался, данные заведомо соответствуют формату. Отсюда известный
принцип проектирования «parse, don't validate» (разбирай, а не проверяй).
Аналогии из жизни
Разбор предложения в школе. Учитель диктует: «Мама мыла раму», ты
подчёркиваешь подлежащее одной чертой, сказуемое — двумя. Ты превратил
цепочку слов в структуру ролей — это буквально парсинг, отсюда и термин.
Где ломается: человек опирается на смысл и контекст, парсер — только на
формальные правила. Фразу «Косил косой косой косой» человек распутает,
классический парсер без семантики — нет: у него либо однозначная грамматика,
либо ошибка разбора.
Сортировочный центр почты. На ленту сыплется поток посылок. Сортировщик
читает у каждой индекс, город, улицу — и раскладывает по контейнерам. Из
хаотичного потока получается структура «регион → город → отделение».
Где ломается: посылка с нечитаемым адресом уходит в ручной разбор, и
процесс не останавливается. А строгий парсер на первой же «нечитаемой
посылке» падает с ошибкой на весь файл — если его специально не научили
пропускать плохие записи. Обработка ошибок в парсере — отдельная работа,
которая «бесплатно» не даётся.
Приготовление по рецепту из книги. Рецепт — это текст: «взбить 3 яйца,
добавить 200 г муки». Читая, ты выделяешь ингредиенты, количества, действия
и порядок — строишь в голове план готовки. Текст стал структурой.
Где ломается: рецепт рассчитан на додумывание — «соль по вкусу», «жарить
до готовности». Человек заполняет пробелы опытом, парсер не может: формат
должен быть определён полностью, любая двусмысленность — это либо ошибка,
либо жёсткое правило по умолчанию, зашитое заранее.
Как это работает
Разберём на маленьком примере. Есть строка JSON:
{"name": "Prime", "rooms": [1, 2]}
Шаг 1. Лексер (tokenizer, токенизатор). Идёт по символам слева направо
и режет ленту на токены — минимальные осмысленные кусочки:
{ "name" : "Prime" , "rooms" : [ 1 , 2 ] }
Каждый токен получает тип: LBRACE (открывающая фигурная скобка), STRING,
COLON, NUMBER и так далее. Уже здесь ловится часть ошибок: строка без
закрывающей кавычки — ошибка лексера.
Шаг 2. Парсер. Читает поток токенов и сверяет его с грамматикой.
Грамматика JSON говорит примерно следующее: «объект — это {, затем ноль
или больше пар "строка: значение" через запятую, затем }; значение — это
строка, число, объект, массив, true, false или null». Замечай рекурсию:
значение может быть объектом, внутри которого снова значения. Именно
рекурсия делает regex бессильным и требует настоящего парсера.
По ходу разбора строится дерево:
объект
/ \
name rooms
| |
"Prime" массив
/ \
1 2
Шаг 3. Дальше — по назначению. Для данных (JSON, YAML) дерево сразу
превращается в объекты языка: словарь, список, число. Для языков
программирования после синтаксиса идёт семантический анализ: проверка
типов, областей видимости («а объявлена ли переменная x?») — это уже за
пределами парсинга, но живёт рядом.
Два слова о стратегиях, которые встречаются в статьях:
- Нисходящий разбор (top-down, LL) — парсер идёт от общего к частному:
«жду объект → значит, жду{→ жду пару ключ-значение…». Самая читаемая
реализация — рекурсивный спуск (recursive descent): по функции на каждое
правило грамматики. Так написаны вручную парсеры многих продакшн-языков —
например, Clang для C++ и парсер Go. - Восходящий разбор (bottom-up, LR) — парсер собирает мелкие кусочки в
крупные, пока не получится всё дерево. Мощнее по классу грамматик, но
вручную такое не пишут — генерируют инструментами (Yacc/Bison).
Практический вывод из этой кухни: если формат стандартный — JSON, YAML,
XML, CSV, HTML — свой парсер писать не нужно никогда. Берёшь готовый:
json.loads в Python, BeautifulSoup или lxml для HTML. Все тонкости —
кодировки, экранирование, вложенность, битые данные — там уже решены.
Не парси HTML регулярными выражениями
Классические грабли: «достану цену со страницы регуляркой». Работает до первой смены вёрстки или вложенного тега. HTML — рекурсивный формат, ему нужен HTML-парсер (BeautifulSoup, lxml), который строит дерево тегов. Регулярка годится максимум для точечного выхватывания простых подстрок из уже найденного куска.
Где встречается в обычной жизни
- Открываешь любой сайт. Браузер получает HTML-текст и парсит его в
DOM-дерево (Document Object Model — дерево элементов страницы), потом
парсит CSS и JavaScript. Каждая страница — это три-четыре парсера,
отработавших за долю секунды. - Банковское приложение подставляет код из СМС. Телефон распарсил текст
сообщения, нашёл в нём шаблон «код: NNNN» и вытащил число. - Excel открывает выгрузку из интернет-банка. CSV-файл — это лента
символов с запятыми; Excel парсит её в строки и столбцы. Когда числа
«съезжают» из-за запятой в названии — ты наблюдаешь ошибку парсинга. - Календарь предлагает «добавить встречу» из письма. Почтовый клиент
распарсил свободный текст, распознал дату, время и место. - Навигатор понимает «Ленина 12к3». Геокодер парсит адресную строку на
компоненты: улица, дом, корпус — и только потом ищет точку на карте.
Где встречается в IT и бизнесе
- Компиляторы и интерпретаторы. Любой язык программирования начинается с
парсера. Каждый запуск Python-скрипта — сначала парсинг исходника.
Нужно, когда: делаешь свой язык конфигурации, формулы в таблицах, шаблоны. - Сбор данных с сайтов (скрейпинг). Мониторинг цен конкурентов, агрегация
объявлений, проверка выдачи. Скачали HTML → распарсили → достали цифры.
Нужно, когда: данные есть на сайте, а официального API нет. - Обработка логов и мониторинг. Серверные логи — плоский текст; чтобы
посчитать ошибки за час или найти атаку, лог парсят в поля: время, уровень,
сообщение. Весь стек «собрать логи → разобрать → искать» (например,
Elasticsearch + Logstash) стоит на парсинге. - Интеграции и ETL (extract-transform-load — извлечь, преобразовать,
загрузить). Обмен между системами: выгрузка из CRM → парсинг → загрузка в
аналитику. Банковские выписки, фиды объявлений, прайс-листы поставщиков. - Конфигурации и инфраструктура. Всё, что описано текстом — YAML в CI/CD,
nginx.conf, plist в launchd — при каждом запуске парсится. Ошибка «unexpected
token at line 42» — привет от парсера.
Кто пользуется
- Google. Googlebot скачивает и парсит веб в масштабе сотен миллиардов
страниц — индекс поисковика строится парсингом HTML всего интернета. - Браузеры. Chrome (движок Blink + V8), Safari (WebKit), Firefox (Gecko) —
их HTML/CSS/JS-парсеры отрабатывают у миллиардов людей ежедневно; это,
вероятно, самые массовые парсеры в истории. - Все, кто пишет код. Компилятор любого языка, подсветка синтаксиса и
автодополнение в редакторе (VS Code, IDE JetBrains через инкрементальные
парсеры вроде tree-sitter), линтеры, форматтеры. - Финансовый сектор. Биржевые протоколы (FIX), банковские форматы
(SWIFT, ISO 20022) — это парсинг с ценой ошибки в деньги, поэтому там
парсеры строгие и сертифицируемые. - Рынок данных. Целые компании живут сбором и парсингом: агрегаторы цен,
сервисы мониторинга упоминаний, проверка контрагентов (парсинг госреестров).
Альтернативы и конкуренты
- Официальный API вместо парсинга сайта.
Плюсы: стабильный контракт, структура сразу, законно и надёжно.
Минусы: API может не существовать, стоить денег, отдавать не все поля. - Регулярные выражения.
Плюсы: одна строка кода для простых плоских шаблонов, есть везде.
Минусы: не справляются с вложенностью, превращаются в нечитаемого монстра
по мере усложнения формата. - LLM-извлечение (попросить языковую модель достать данные из текста).
Плюсы: работает на неструктурированном и «грязном» тексте, где формальной
грамматики нет вовсе; быстро прототипируется.
Минусы: недетерминированность (может вернуть разное на одном входе),
цена и скорость на больших объёмах, галлюцинации — для критичных данных
нужна перепроверка. - Готовые платформы скрейпинга (Apify, Octoparse и подобные).
Плюсы: без кода, обход блокировок из коробки.
Минусы: подписка, зависимость от вендора, ограниченная гибкость на
нестандартных сайтах.
Когда НЕ стоит использовать
- Когда есть официальный API или выгрузка — потому что парсинг чужого
HTML это всегда гонка: сайт поменял вёрстку — твой парсер молча сломался.
API — это контракт, вёрстка — нет. - Когда формат непредсказуем, а объём мал — потому что парсер окупается
на повторяемости. Разобрать десять писем с разнобойным текстом быстрее
руками или через LLM, чем писать и отлаживать правила, которые устареют
на одиннадцатом письме. - Когда данные защищены законом или договором — потому что «технически
можно скачать» не равно «можно использовать»: персональные данные,
авторский контент, прямой запрет в условиях использования сайта. Риски тут
не технические, а юридические.
Парсер — это всегда договор о формате
Парсинг работает ровно до тех пор, пока источник соблюдает формат. Если формат зафиксирован стандартом (JSON, RFC) — парсер живёт годами. Если формат — чужая вёрстка, которую никто тебе не обещал, закладывай расходы на поддержку: мониторинг поломок и регулярную починку. Это не баг, это природа задачи.
Связанные понятия
- Лексер (токенизатор) — первый этап разбора: режет поток символов на
минимальные осмысленные единицы-токены. - AST (абстрактное синтаксическое дерево) — структура-результат парсинга,
дерево вложенности элементов. - Формальная грамматика (BNF) — запись правил формата, по которой строится
или генерируется парсер. - Регулярные выражения — младший брат парсинга для плоских шаблонов без
вложенности. - Сериализация — обратный процесс: превращение структуры в текст/байты
для хранения или передачи. - Веб-скрейпинг — сбор данных с сайтов, где парсинг — центральный этап
между скачиванием и сохранением.
Литература и источники
- Aho, Sethi, Ullman, Lam — «Compilers: Principles, Techniques, and Tools»
(«Книга дракона», 2-е издание 2006, en; есть русский перевод) — классика
теории компиляторов, главы про лексический и синтаксический анализ. - Robert Nystrom — «Crafting Interpreters» (2021, en) — лучшее современное
введение в написание парсера руками, бесплатно: https://craftinginterpreters.com - Wikipedia (en): https://en.wikipedia.org/wiki/Parsing — обзор и терминология;
оттуда же ссылки на LL/LR-разбор и иерархию Хомского. - Документация Python: модуль
jsonиhtml.parser— посмотреть, как выглядит
использование готовых парсеров: https://docs.python.org/3/library/json.html - Статья «Parse, don't validate» Алексис Кинг (2019, en) — про проектирование
программ вокруг парсинга; искать в Google по точному названию. - Про PEG: Bryan Ford, «Parsing Expression Grammars» (2004, en) — искать по
названию на сайте автора или в ACM.
Где встретилось у меня
Вчера в работе крутились агенты, которые собирают подменные номера
колл-трекинга со страниц застройщиков: скачанные страницы разбираются на
структуру, из них извлекаются телефоны по гео-логике. Параллельно журнал
проектов сам парсил jsonl-логи рабочих сессий, превращая их в структурные
записи «что решили и почему». День прошёл под знаком превращения сырого
текста в структуру — это парсинг и есть.
Краткое резюме
- Парсинг — превращение плоского текста/байтов в структуру (дерево, объекты)
по правилам формата; термин пришёл из школьного разбора предложений. - Два этапа: лексер режет символы на токены, парсер собирает из токенов
дерево по грамматике; для языков дальше идёт семантический анализ. - Regex не заменяет парсер: вложенные форматы (HTML, JSON) регулярками
корректно не разбираются. - Для стандартных форматов всегда бери готовый парсер — свой пиши только
для собственного языка/формата. - Парсинг чужого сайта — это бессрочные расходы на поддержку: формат тебе
никто не обещал; при наличии API выбирай API.