2009-02-26 6 views
178

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

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

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

+3

я думаю, что это боян из http://stackoverflow.com/questions/133601 – jcrossley3

+19

Потому что только Чак Norris * может * анализировать HTML с регулярным выражением (как объясняется в этой знаменитой вещи Zalgo: http://stackoverflow.com/questions/1732348/regex-match-open-tags-ex СЕРТ-XHTML-самодостаточный-теги). – takeshin

+1

Этот вопрос побудил меня задать еще один вопрос, который каким-то образом связан. Если вам интересно: [Почему невозможно использовать регулярное выражение для анализа HTML/XML: формальное объяснение в терминах непрофессионала] (http://stackoverflow.com/q/6751105/146792) – mac

ответ

187

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

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

+24

Лучший ответ до сих пор. Если он может соответствовать только регулярным грамматикам, нам понадобится бесконечно большое регулярное выражение для анализа контекстно-свободной грамматики, такой как HTML. Мне нравится, когда у этих вещей есть ясные теоретические ответы. – ntownsend

+1

Я предположил, что мы обсуждаем регулярные выражения типа Perl, где они не являются фактически регулярными выражениями. –

+0

Что делает регулярные выражения типа Perl не актуальными регулярными выражениями? – ntownsend

14

Два быстрой причина:

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

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

+0

Вау? Слайд после 2+ лет? В случае, если кто-то задавался вопросом, я не сказал: «Потому что это теоретически невозможно», потому что вопрос четко задается вопросом «быстро и грязно», а не «правильно». ОП явно уже читали ответы, которые охватывали теоретически невозможную территорию и до сих пор не удовлетворены. –

+0

Имейте upvote после 5+ лет.:) Что касается того, почему вы, возможно, получили понижение, я не имею права говорить, но лично мне хотелось бы увидеть несколько примеров или объяснений, а не заключительный риторический вопрос. –

+2

По сути, все быстрые и грязные анализы html, которые выполняются в продуктах доставки или внутренних инструментах, заканчиваются тем, что они выглядят как дыра в безопасности, или ошибка, ожидающая своего появления. Это должно быть обескуражено с удовольствием. Если можно использовать регулярное выражение, можно использовать собственный синтаксический анализатор html. –

7

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

+0

Очень верно, большинство HTML там, кажется, ужасно. Я не понимаю, как неудачное регулярное выражение может привести к серьезным нарушениям безопасности. Можете ли вы привести пример? – ntownsend

+3

ntownsend: Например, вы считаете, что вы удалили все теги скриптов из HTML, но ваше регулярное выражение не справляется с особым случаем (что, скажем, работает только на IE6): бум, у вас есть HSS vulerability! –

+0

Это был строго гипотетический пример, поскольку большинство примеров реального мира слишком сложны, чтобы вписаться в эти комментарии, но вы могли бы найти несколько быстрых поисковых запросов по этому вопросу. –

6

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

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

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

31

Для quick'n'dirty regexp будет хорошо. Но самое главное знать, что это невозможно, чтобы построить регулярное выражение, которое будет правильно проанализировать HTML.

Причина в том, что регулярные выражения не могут обрабатывать произвольные вложенные выражения. См. Can regular expressions be used to match nested patterns?

+0

@j_random_hacker: добавлена ​​ссылка на другой ответ Stackoverflow. – kmkaplan

+1

Некоторые библиотеки регулярных выражений могут выполнять рекурсивные регулярные выражения (эффективно делая их нерегулярными выражениями :) –

0

Регулярные выражения недостаточно мощны для такого языка, как HTML. Конечно, есть примеры, где вы можете использовать регулярные выражения. Но в целом это не подходит для синтаксического анализа.

15

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

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

5

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

4

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

+0

Я действительно прочитал эту книгу. Мне просто не приходило в голову, что HTML - это контекстно-свободный язык. – ntownsend

2

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

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

В этом случае неправильное использование некоторых документов не будет большим делом. Никто, кроме вас, не увидит ошибок, и если вам повезет, их будет недостаточно, чтобы вы могли следить за ними индивидуально.

Думаю, я говорю, что это компромисс. Иногда реализация или использование правильного парсера - так просто, как может быть - может не стоить того, если точность не является критичной.

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

2

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

Консенсус выше заключается в том, что в целом это плохая идея. Однако, если структура HTML известна (и вряд ли изменится), то она по-прежнему является действительным подходом.

1

Имейте в виду, что, хотя сам HTML не является регулярным, части страницы, на которую вы смотрите , могут быть регулярными.

Например, для <form> является темой, которая должна быть вложенной; если веб-страница работает правильно, то использование регулярного выражения для захвата <form> было бы вполне разумным.

Я недавно сделал некоторые веб-скребки, используя только селен и регулярные выражения. Мне это удалось, потому что данные, которые я хотел, были помещены в <form> и помещены в простой формат таблицы (поэтому я мог даже рассчитывать на <table>, <tr> и <td>, чтобы быть не-вложенными - что на самом деле очень необычно). В некоторой степени регулярные выражения были даже почти необходимы, потому что некоторые структуры, которые мне нужны для доступа, были ограничены комментариями. (Красивый суп может дать вам комментарии, но было бы трудно захватить <!-- BEGIN --> и <!-- END --> блоками с использованием Beautiful Soup.)

Если бы мне пришлось беспокоиться о вложенных таблицах, мой подход просто не сработал бы! Мне пришлось бы отступить на Beautiful Soup. Однако даже тогда, иногда, вы можете использовать регулярное выражение, чтобы захватить требуемый фрагмент, а затем развернуться оттуда.

1

На самом деле, HTML-синтаксический анализ с регулярным выражением вполне возможен в PHP. Вам просто нужно проанализировать всю строку назад, используя strrpos, чтобы найти < и повторять регулярное выражение оттуда с помощью спецификаторов неразглашения каждый раз, чтобы перебирать вложенные теги. Не очень и очень медленно на больших вещах, но я использовал его для своего собственного редактора шаблонов для своего сайта. Я на самом деле не разбирал HTML, но несколько специальных тегов, которые я сделал для запросов к записям базы данных для отображения таблиц данных (мой тег <#if()> мог выделять специальные записи таким образом). Я не был готов пойти на синтаксический анализатор XML только на пару самоподготовленных тегов (с очень не-XML-данными внутри них) здесь и там.

Итак, несмотря на то, что этот вопрос значительно мертв, он все еще отображается в поиске Google. Я прочитал его и подумал, что «вызов принят», и закончил исправление моего простого кода без необходимости замены всего. Решил предложить другое мнение любому, кто ищет подобную причину. Также последний ответ был отправлен 4 часа назад, так что это по-прежнему горячая тема.

+1

-1 за предложение идеи TERRIBLE. Вы считали пробел между тегом и скобой закрытия? (Например, '') Вы считали закрытые теги с комментариями? (Например, '

+3

Это похоже на реальный ответ - хотя, возможно, можно проанализировать произвольный HTML с регулярным выражением, поскольку сегодняшние регулярные выражения - это больше, чем просто конечные автоматы, для того чтобы разобрать произвольный html, а не только конкретную страницу, вам нужно переопределить парсер HTML в regexp и regexes, безусловно, становятся 1000-кратно нечитаемыми. –

+0

Эй, Энди, я нашел время, чтобы выступить с выражением, которое поддерживает ваши упомянутые случаи. http://stackoverflow.com/a/40095824/1204332 Сообщите мне, что вы думаете! :) –

+0

Обоснование в этом ответе * * устарело, и применяется сегодня даже меньше, чем было изначально (что, я думаю, это не так). (Цитата OP: «Если вы просто делаете что-то простое, быстрое или грязное ...».) –

0

Вы, знаете ... есть много менталитета из вас НЕ МОЖЕТСЯ сделать это, и я думаю, что все по обеим сторонам забора правильные и неправильные. Вы CAN сделайте это, но это займет немного больше обработки, чем просто запустить одно регулярное выражение против него. Возьмите this (я написал это через час). Предполагается, что HTML полностью действителен, но в зависимости от того, какой язык вы используете для применения вышеупомянутого регулярного выражения, вы можете сделать некоторые исправления HTML, чтобы убедиться, что он будет успешным. Например, удаление закрывающих тегов, которые не должны быть там: </img> например. Затем добавьте закрытие одной косой черты в HTML к элементам, которые их не хватает, и т. Д.

Я бы использовал это в контексте написания библиотеки, которая позволила бы мне выполнять поиск элементов HTML, аналогичный извлечению HTML [x].getElementsByTagName(), например. Я просто объединил функциональность, которую я написал в разделе DEFINE регулярного выражения, и использовал его для входа в дерево элементов, по одному в раз.

Итак, будет ли это окончательный 100% -ый ответ для проверки HTML? Нет. Но это начало и с немного большей работой, это можно сделать. Однако попытка сделать это внутри одного выполнения регулярного выражения непрактична и неэффективна.

2

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

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

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

Check it out. Он лучше работает с флагами «gisx», как в демо.

+0

Это очень интересно. Не читается, возможно, трудно отлаживать, но все же: впечатляющая работа! –

1

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

Используйте параметры 'sx'. «Г» тоже, если вы чувствуете себя счастливым:

(?P<content>.*?)    # Content up to next tag 
(?P<markup>      # Entire tag 
    <!\[CDATA\[(?P<cdata>.+?)]]>| # <![CDATA[ ... ]]> 
    <!--(?P<comment>.+?)-->|  # <!-- Comment --> 
    </\s*(?P<close_tag>\w+)\s*>| # </tag> 
    <(?P<tag>\w+)     # <tag ... 
    (?P<attributes> 
     (?P<attribute>\s+ 
# <snip>: Use this part to get the attributes out of 'attributes' group. 
     (?P<attribute_name>\w+) 
     (?:\s*=\s* 
      (?P<attribute_value> 
      [\w:/.\-]+|   # Unquoted 
      (?=(?P<_v>   # Quoted 
       (?P<_q>['\"]).*?(?<!\\)(?P=_q))) 
      (?P=_v) 
     ))? 
# </snip> 
    )* 
    )\s* 
    (?P<is_self_closing>/?) # Self-closing indicator 
    >)      # End of tag 

Это один предназначен для Python (он может работать и на других языках, не пробовал, он использует положительные lookaheads, отрицательные и Утверждения назад названные обратные ссылки) , Поддержка:

  • Open Tag - <div ...>
  • Закрыть Tag - </div>
  • Комментарий - <!-- ... -->
  • CDATA - <![CDATA[ ... ]]>
  • самозакрывающихся Tag - <div .../>
  • Дополнительные значения атрибутов - <input checked>
  • Без кавычек/котировки Attribu т.е значения - <div style='...'>
  • Одиночные/двойные кавычки - <div style="...">
  • кавычки экранированы - <a title='John\'s Story'>
    (это на самом деле не действует HTML, но я хороший парень)
  • пространства вокруг знака равенства - <a href = '...'>
  • Названы Захватывает Для Интересного Bits

Это также очень хорошо о не вызывая на некорректных тегах, например, когда вы забыли < или >.

Если ваш аромат регулярного выражения поддерживает повторяющиеся именованные снимки, тогда вы являетесь золотым, но Python re не (я знаю, что regex does, но мне нужно использовать vanilla Python). Вот что вы получаете:

  • content - Все содержимое до следующего тега. Вы можете это исключить.
  • markup - Весь тег со всем в нем.
  • comment - Если это комментарий, содержание комментария.
  • cdata - Если это <![CDATA[...]]>, содержимое CDATA.
  • close_tag - Если это тег close (</div>), имя тега.
  • tag - Если это открытый тег (<div>), имя тега.
  • attributes - Все атрибуты внутри тега. Используйте это, чтобы получить все атрибуты, если вы не получаете повторяющиеся группы.
  • attribute - Повторяется, каждый атрибут.
  • attribute_name - Повторяется, каждое имя атрибута.
  • attribute_value - Повторяется, каждое значение атрибута. Это включает цитаты, если они были указаны.
  • is_self_closing - Это /, если это самозакрывающийся тег, в противном случае ничего.
  • _q и _v - Игнорировать эти; они используются внутренне для обратных ссылок.

Если ваш двигатель регулярных выражений не поддерживает повторные именованные захваты, есть раздел, который вы можете использовать для получения каждого атрибута. Просто запустите это регулярное выражение в группе attributes, чтобы получить из него attribute, attribute_name и attribute_value.

Демо здесь: https://regex101.com/r/mH8jSu/11

0

HTML/XML делится на разметку и содержание.
Regex полезен только при анализе лексических тегов.
Я думаю, вы могли бы вывести содержание.
Это был бы хороший выбор для анализатора SAX.
Теги и содержание могут быть доставлены пользователем
Определенная функция, в которой гнездование/закрытие элементов
можно отслеживать.

Что касается разбора тегов, это можно сделать с помощью
regex и используется для разметки тегов из документа.

В течение многих лет тестирования я нашел секрет в методах анализа синтаксического анализа, которые хорошо и плохо сформированы.

Нормальные элементы обрабатываются с помощью этой формы:

Ядра этих тегов использовать это регулярное выражение

(?: 
     " [\S\s]*? " 
    | ' [\S\s]*? ' 
    | [^>]? 
)+ 

Вы заметите это [^>]? в качестве одного из чередований.
Это будет соответствовать неуравновешенным цитатам из плохо сформированных тегов.

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

Если используется пассивно, проблем не возникает.
Но, если вы сила что-то, чтобы соответствовать перемежая его с
разыскивают пары атрибут/значение, и не обеспечивают адекватную защиту
от возвратов, что это из-под контроля кошмара.

Это общий вид для простых старых тегов.
Обратите внимание: [\w:], обозначающий название тега?
В действительности, юридические символы, представляющие имя тега
являются невероятным списком символов Юникода.

<  
(?: 
     [\w:]+ 
     \s+ 
     (?: 
      " [\S\s]*? " 
     | ' [\S\s]*? ' 
     | [^>]? 
    )+ 
     \s* /? 
) 
> 

Двигаясь дальше, мы также видим, что вы просто не можете найти конкретный тег
без разбора ВСЕХ тегов.
Я имею в виду, что вы могли бы, но он должен был бы использовать комбинацию
глаголов вроде (* SKIP) (* FAIL), но все же все теги должны быть проанализированы.

Причина заключается в том, что синтаксис тега могут быть скрыты внутри других тегов и т.д ..

Таким образом, чтобы пассивно анализировать все теги, регулярное выражение необходимо, как показано ниже.
0 Соответствие соответствует: Активность:.

В качестве нового HTML или xml или любого другого, разрабатывающего новые конструкции, просто добавьте его как
одно из альтернатив.


Веб-страница примечание - Я никогда не видел веб-страницу (или XHTML/XML), что
имел проблемы с. Если вы его найдете, сообщите мне.

Замечание об исполнении - это быстро. Это самый быстрый тег-анализатор, который я видел
(может быть, быстрее, кто знает).
У меня есть несколько конкретных версий. Он также отлично подходит как скребок
(если вы практический тип).


Полное сырым регулярное выражение

<(?:(?:(?:(script|style|object|embed|applet|noframes|noscript|noembed)(?:\s+(?>"[\S\s]*?"|'[\S\s]*?'|(?:(?!/>)[^>])?)+)?\s*>)[\S\s]*?</\1\s*(?=>))|(?:/?[\w:]+\s*/?)|(?:[\w:]+\s+(?:"[\S\s]*?"|'[\S\s]*?'|[^>]?)+\s*/?)|\?[\S\s]*?\?|(?:!(?:(?:DOCTYPE[\S\s]*?)|(?:\[CDATA\[[\S\s]*?\]\])|(?:--[\S\s]*?--)|(?:ATTLIST[\S\s]*?)|(?:ENTITY[\S\s]*?)|(?:ELEMENT[\S\s]*?))))>

Форматированная выглядеть

< 
(?: 
     (?: 
      (?: 
       # Invisible content; end tag req'd 
       (       # (1 start) 
        script 
        | style 
        | object 
        | embed 
        | applet 
        | noframes 
        | noscript 
        | noembed 
       )        # (1 end) 
       (?: 
        \s+ 
        (?> 
          " [\S\s]*? " 
         | ' [\S\s]*? ' 
         | (?: 
           (?! />) 
           [^>] 
         )? 
        )+ 
       )? 
       \s* > 
      ) 

      [\S\s]*? </ \1 \s* 
      (?= >) 
    ) 

    | (?: /? [\w:]+ \s* /?) 
    | (?: 
      [\w:]+ 
      \s+ 
      (?: 
       " [\S\s]*? " 
      | ' [\S\s]*? ' 
      | [^>]? 
      )+ 
      \s* /? 
    ) 
    | \? [\S\s]*? \? 
    | (?: 
      ! 
      (?: 
       (?: DOCTYPE [\S\s]*?) 
      | (?: \[CDATA\[ [\S\s]*? \]\]) 
      | (?: -- [\S\s]*? --) 
      | (?: ATTLIST [\S\s]*?) 
      | (?: ENTITY [\S\s]*?) 
      | (?: ELEMENT [\S\s]*?) 
      ) 
    ) 
) 
>