Сайт Романа ПарпалакаБлог20230121

Мастер костылей, или сущности в DOMDocument

21 января 2023 года, 01:19

Я тут решил потратить некоторое время на собственные проекты. Сейчас перерабатываю движок для поиска Rose. На нем работает поиск на этом сайте, также его использует Илья Бирман в Эгее.

Для извлечения текста из html-исходников решил взять DOM API, как советует народ. Вообще, конечно, регулярки — наше всё :) Но HTML нормально распарсить регулярками нельзя. А мне надо распарсить, чтобы удалить из исходника стили, скрипты и комментарии (попробуйте на досуге написать регулярку, которая будет определять, находится ли разделитель в контексте комментария или нет). Кроме того, парсинг поможет точно разбить текст на абзацы, чтобы их дальше разбить на предложения. Поэтому я взял волю в кулак, посмотрел, как использовать DOM API, и рекурсивно обошел dom-дерево на PHP.

Как оказалось, DOM API не поддерживает HTML5. Это выливается в практическую проблему с непоследовательной обработкой сущностей HTML. Например, если в исходнике написать & ★, при получении текстового содержимого из dom-узла получим & ★. Видим, что первая сущность из HTML предыдущих версий распознается и раскодируется, а вторая из HTML5 — нет. Такие ошибки приведут к искажениям при выводе сниппетов.

Моя первая идея — прогнать текстовое содержимое через html_entity_decode($a, ENT_HTML5);. Действительно, что не доделал встроенный парсер, доделает эта функция. Но проблема в том, что раскодирование неидемпотентно. Если на вход подать ★, то после DOM API мы получим ★. И на этапе повторного раскодирования мы не будем знать, нужно ли раскодировать ★ еще раз, или нет.

Поиск проблемы в гугле разумного решения не выявил. В классе DOMDocument есть какое-то свойство substituteEntities, но в моем случае оно ни на что не влияло. Я уже было подумывал о возврате к регуляркам. Но всё-таки смог придумать обходное решение.

Для начала нужно все вхождения амперсанда заменить на его сущность:

$text = str_replace('&', '&', $text);

После этого DOM API раскодирует копии только одой сущности — этого самого амперсанда. При рекурсивном обходе в текстовом содержимом узлов dom-дерева тегов (то есть символов < и >) не будет, а все сущности будут закодированными. Если их нужно раскодировать, можно вызвать html_entity_decode($a, ENT_HTML5);.

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

Поделиться

Отладка запросов к FastCGI из консоли Ctrl Зарисовка для объяснения понятия формата

Читайте также

Латех и веб-технологии
В прошлый раз я рассказал о своем сервисе, который генерирует для веба картинки с математическими формулами на латехе.
2014
AMP
2017

Комментарии

#1. 22 января 2023 года, 20:03. Евгений Степанищев пишет:
Странно, что не tidy, я бы сделал именно им, заодно им же можно кривой HTML почистить.
#2. 22 января 2023 года, 20:29. пишет:
Евгений, я с tidy никогда не работал, не знаю, что он может.

Моя задача — разбить HTML сначала по блочным элементам на абзацы, потом по знакам препинания на предложения. Попутно другие элементы обработать. Я для этого обхожу dom-дерево:
https://github.com/parpalak/rose/blob/4007 … or.php#L78

Бегло просмотрел документацию и пока не понял, как tidy может помочь. Может есть какие-нибудь примеры?
#3. 25 января 2023 года, 17:00. Евгений Степанищев пишет:
Блин, а я что-то не получил письмо, что ты мне ответил. Примеров под рукой нет, но смысл в том, что tidy исправляет и парсит даже кривой html (а это нужно делать что html ≠ xml и не всегда возможно парсить html как xml). Но проблемой может быть в том, что tidy не везде стоит — он не bundled.
#4. 30 января 2023 года, 00:22. пишет:
Ты не получил письмо, потому что в первом комментарии не подписался :)

Буду иметь в виду, что есть tidy. Пока у меня всё работает через DOMDocument и libxml даже на HTML с ошибками. На стековерфлоу по ссылке так и пишут: «DOM is capable of parsing and modifying real world (broken) HTML» (https://stackoverflow.com/questions/357764 … xml-in-php)

Кстати, надо будет добавить в юнит-тесты к Розе проверку того, что кривой HTML будет как-то индексироваться. Тесты с корректным HTML у меня есть. Тесты с поломанным тоже будут полезными, вдруг что-то изменится в будущих версиях PHP.
#5. 30 января 2023 года, 10:16. Евгений Степанищев пишет:
Век живи, век учись. Не знал, что libxml умеет разбирать поломанный HTML.

Оставьте свой комментарий


Формулы на латехе: $$f(x) = x^2-\sqrt{x}$$ превратится в $$f(x) = x^2-\sqrt{x}$$.
Выделение текста: [i]курсивом[/i] или [b]жирным[/b].
Цитату оформляйте так: [q = имя автора]цитата[/q] или [q]еще цитата[/q].
Других команд или HTML-тегов здесь нет.

Записи