Использование регулярных выражений для разбора HTML: почему бы и нет? - PullRequest
199 голосов
/ 26 февраля 2009

Кажется, что каждый вопрос о стековом потоке, когда запрашивающий использует регулярное выражение для получения некоторой информации из HTML, неизбежно будет иметь «ответ», который говорит, что не следует использовать регулярное выражение для анализа HTML.

Почему бы и нет? Я знаю, что существуют «настоящие» парсеры HTML без кавычек, такие как Beautiful Soup , и я уверен, что они мощные и полезные, но если вы просто делаете что-то простое, быстрое или грязный, тогда зачем использовать что-то настолько сложное, когда несколько операторов регулярных выражений будут работать нормально?

Кроме того, есть ли что-то фундаментальное, чего я не понимаю в регулярных выражениях, что делает их плохим выбором для синтаксического анализа вообще?

Ответы [ 18 ]

202 голосов
/ 26 февраля 2009

Полный синтаксический анализ HTML невозможен с регулярными выражениями, поскольку он зависит от соответствия открывающего и закрывающего тегов, что невозможно при регулярных выражениях.

Регулярные выражения могут соответствовать только обычным языкам , но HTML является контекстно-свободным языком и не обычным языком (как указывал @StefanPochmann, обычные языки также не зависят от контекста, поэтому отсутствие контекста не обязательно означает нерегулярность). Единственное, что вы можете сделать с помощью регулярных выражений в HTML - это эвристика, но это не сработает при любых условиях. Должна быть возможность представить HTML-файл, который будет некорректно сопоставляться любым регулярным выражением.

34 голосов
/ 26 февраля 2009

Для быстрых регулярных выражений все будет хорошо. Но фундаментальная вещь, которую нужно знать, это то, что невозможно построить регулярное выражение, которое будет правильно разобрать HTML.

Причина в том, что регулярные выражения не могут обрабатывать произвольно вложенные выражения. См. Можно ли использовать регулярные выражения для сопоставления с вложенными шаблонами?

19 голосов
/ 10 сентября 2013

http://htmlparsing.com/regexes)

Скажем, у вас есть файл HTML, из которого вы пытаетесь извлечь URL из image метки.

<img src="http://example.com/whatever.jpg">

Итак, вы пишете регулярное выражение в Perl:

if ( $html =~ /<img src="(.+)"/ ) {
    $url = $1;
}

В этом случае $url действительно будет содержать http://example.com/whatever.jpg. Но что происходит, когда вы начинаете получать HTML, как это:

<img src='http://example.com/whatever.jpg'>

или

<img src=http://example.com/whatever.jpg>

или

<img border=0 src="http://example.com/whatever.jpg">

или

<img
    src="http://example.com/whatever.jpg">

или вы начинаете получать ложные срабатывания от

<!-- // commented out
<img src="http://example.com/outdated.png">
-->

Это выглядит так просто, и это может быть просто для одного неизменного файла, но для всего, что вы собираетесь делать с произвольными данными HTML, регулярные выражения - всего лишь рецепт будущей душевной боли.

16 голосов
/ 26 февраля 2009

Что касается синтаксического анализа, регулярные выражения могут быть полезны на этапе «лексического анализа» (lexer), когда входные данные разбиваются на токены. Это менее полезно на этапе создания дерева анализа.

Для синтаксического анализатора HTML я ожидаю, что он будет принимать только правильно сформированный HTML, и это требует возможностей, выходящих за пределы возможностей регулярного выражения (они не могут «считать» и убедиться, что заданное количество открываемых элементов сбалансировано с помощью столько же закрывающих элементов).

16 голосов
/ 26 февраля 2009

Две быстрые причины:

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

Относительно пригодности регулярных выражений для синтаксического анализа в целом: они не подходят. Вы когда-нибудь видели виды регулярных выражений, которые вам понадобятся для анализа большинства языков?

8 голосов
/ 26 февраля 2009

Поскольку существует много способов «испортить» HTML, который браузеры будут воспринимать довольно либерально, но потребуется довольно много усилий, чтобы воспроизвести либеральное поведение браузера, чтобы покрыть все случаи с помощью регулярных выражений, поэтому ваше регулярное выражение неизбежно потерпит неудачу в некоторых особых случаях, и это может привести к серьезным пробелам в безопасности вашей системы.

7 голосов
/ 26 февраля 2009

Проблема в том, что большинство пользователей, которые задают вопрос, связанный с HTML и регулярным выражением, делают это, потому что они не могут найти собственное регулярное выражение, которое работает. Тогда нужно подумать, будет ли все проще при использовании парсера DOM или SAX или чего-то подобного. Они оптимизированы и сконструированы для работы с XML-подобными структурами документов.

Конечно, есть проблемы, которые можно легко решить с помощью регулярных выражений. Но акцент делается на легко .

Если вы просто хотите найти все URL, которые выглядят как http://.../, вы можете использовать регулярные выражения. Но если вы хотите найти все URL-адреса в a-элементе, который имеет класс «mylink», вам, вероятно, лучше использовать соответствующий анализатор.

6 голосов
/ 26 февраля 2009

Я считаю, что ответ лежит в теории вычислений. Для анализа языка с использованием регулярных выражений он должен быть по определению «обычный» ( ссылка ). HTML не является обычным языком, так как он не соответствует ряду критериев для обычного языка (во многом благодаря множеству уровней вложенности, свойственных HTML-коду) Если вас интересует теория вычислений, я бы порекомендовал эту книгу.

6 голосов
/ 26 февраля 2009

Регулярные выражения не были предназначены для обработки структуры вложенных тегов, и в лучшем случае сложно (в худшем случае невозможно) обрабатывать все возможные крайние случаи, которые вы получаете с реальным HTML.

4 голосов
/ 18 октября 2016

Это выражение извлекает атрибуты из элементов HTML. Поддерживает:

  • атрибуты без кавычек / кавычки,
  • одинарные / двойные кавычки,
  • экранированные кавычки внутри атрибутов,
  • пробелы вокруг знаков равенства,
  • любое количество атрибутов,
  • проверять только атрибуты внутри тегов,
  • экранирующих комментариев и
  • управлять различными кавычками в пределах значения атрибута.

(?:\<\!\-\-(?:(?!\-\-\>)\r\n?|\n|.)*?-\-\>)|(?:<(\S+)\s+(?=.*>)|(?<=[=\s])\G)(?:((?:(?!\s|=).)*)\s*?=\s*?[\"']?((?:(?<=\")(?:(?<=\\)\"|[^\"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!\"|')(?:(?!\/>|>|\s).)+))[\"']?\s*)

Проверьте это . Лучше работает с флагами "gisx", как в демо.

...