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

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

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

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

Ответы [ 18 ]

3 голосов
/ 29 апреля 2011

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

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

3 голосов
/ 16 июня 2017

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

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

За годы испытаний я нашел секрет к
способ, которым браузеры анализируют теги, как хорошо, так и плохо сформированные

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

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

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

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

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

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

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

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

Продолжая, мы также видим, что вы просто не можете найти определенный тег
без разбора ALL тегов.
Я имею в виду, что вы могли бы, но это должно было бы использовать комбинацию
глаголы типа (* SKIP) (* FAIL), но все же все теги должны быть проанализированы.

Причина в том, что синтаксис тегов может быть скрыт внутри других тегов и т. Д.

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

По мере того, как новый 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]*? )
           )
      )
 )
 >
3 голосов
/ 26 февраля 2009

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

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

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

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

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

2 голосов
/ 28 декабря 2016

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

Используйте с опциями 'sx'. «g», если вам повезет:

(?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 (он может работать для других языков, не пробовал, он использует положительные взгляды, отрицательные взгляды и именованные обратные ссылки). Поддержка:

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

Также неплохо не запускать некорректные теги, например, когда вы забыли < или >.

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

  • content - Весь контент до следующего тега. Вы можете оставить это.
  • markup - Весь тег со всем в нем.
  • comment - Если это комментарий, содержание комментария.
  • cdata - Если это <![CDATA[...]]>, содержимое CDATA.
  • close_tag - Если это закрывающий тег (</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

2 голосов
/ 12 февраля 2013

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

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

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

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

2 голосов
/ 13 февраля 2013

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

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

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

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

0 голосов
/ 22 ноября 2015

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

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

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

...