Кажется, что у каждого вопроса в stackoverflow, когда спрашивающий использует регулярное выражение для получения некоторой информации из HTML, неизбежно будет «ответ», который говорит, что не следует использовать регулярное выражение для анализа HTML.
Почему нет? Я знаю, что существуют «настоящие» парсеры HTML без кавычек, такие как Beautiful Soup , и я уверен, что они мощные и полезные, но если вы просто делаете что-то простое, быстрое или грязное, то почему использовать что-то настолько сложное, когда несколько операторов регулярных выражений будут работать нормально?
Кроме того, есть ли что-то фундаментальное, чего я не понимаю в регулярных выражениях, что делает их плохим выбором для синтаксического анализа вообще?
regex
html-parsing
ntownsend
источник
источник
Ответы:
С помощью регулярных выражений синтаксический анализ всего HTML невозможен, поскольку он зависит от соответствия открывающего и закрывающего тегов, что невозможно при регулярных выражениях.
Регулярные выражения могут соответствовать только обычным языкам, но HTML является языком без контекста, а не с обычным языком (как указывал @StefanPochmann, обычные языки также являются контекстно-свободными, поэтому контекстно-свободный не обязательно означает не регулярный). Единственное, что вы можете сделать с помощью регулярных выражений в HTML, - это эвристика, но это не сработает при любых условиях. Должна быть возможность представить HTML-файл, который будет совпадать с любым регулярным выражением.
источник
Для быстрого и грязного регулярного выражения все будет хорошо. Но фундаментальная вещь, которую нужно знать, это то, что невозможно создать регулярное выражение, которое будет правильно анализировать HTML.
Причина в том, что регулярные выражения не могут обрабатывать произвольно вложенные выражения. См. Можно ли использовать регулярные выражения для сопоставления с вложенными шаблонами?
источник
(С http://htmlparsing.com/regexes )
Допустим, у вас есть файл HTML, в котором вы пытаетесь извлечь URL из тегов <img>.
Итак, вы пишете регулярное выражение в Perl:
В этом случае
$url
действительно будет содержатьhttp://example.com/whatever.jpg
. Но что происходит, когда вы начинаете получать HTML, как это:или
или
или
или вы начинаете получать ложные срабатывания от
Это выглядит так просто, и это может быть просто для одного неизменного файла, но для всего, что вы собираетесь делать с произвольными данными HTML, регулярные выражения - всего лишь рецепт будущей душевной боли.
источник
Две быстрые причины:
Относительно пригодности регулярных выражений для синтаксического анализа в целом: они не подходят. Вы когда-нибудь видели виды регулярных выражений, которые вам понадобятся для анализа большинства языков?
источник
Что касается синтаксического анализа, регулярные выражения могут быть полезны на этапе «лексического анализа» (lexer), когда входные данные разбиваются на токены. Это менее полезно на стадии «построения дерева разбора».
Для синтаксического анализатора HTML я ожидал бы, что он будет принимать только правильно сформированный HTML, и для этого требуются возможности, выходящие за рамки возможностей регулярного выражения (они не могут «подсчитать» и убедиться, что заданное количество открываемых элементов сбалансировано одним и тем же числом закрывающих элементов).
источник
Потому что есть много способов «испортить» HTML, который браузеры будут воспринимать довольно либерально, но потребуется довольно много усилий, чтобы воспроизвести либеральное поведение браузера, чтобы покрыть все случаи регулярными выражениями, поэтому ваше регулярное выражение неизбежно потерпит неудачу в некоторых особых случаев, и это может привести к серьезным пробелам в безопасности вашей системы.
источник
Проблема в том, что большинство пользователей, которые задают вопрос, связанный с HTML и регулярным выражением, делают это, потому что они не могут найти собственное регулярное выражение, которое работает. Тогда нужно подумать, будет ли все проще при использовании парсера DOM или SAX или чего-то подобного. Они оптимизированы и сконструированы для работы с XML-подобными структурами документов.
Конечно, есть проблемы, которые можно легко решить с помощью регулярных выражений. Но акцент делается на легко .
Если вы просто хотите найти все URL, которые выглядят так, как будто
http://.../
вы в порядке с регулярными выражениями. Но если вы хотите найти все URL-адреса в a-элементе, который имеет класс «mylink», вам, вероятно, лучше использовать соответствующий синтаксический анализатор.источник
Регулярные выражения не предназначены для обработки структуры вложенных тегов, и в лучшем случае сложно (в худшем случае невозможно) обрабатывать все возможные крайние случаи, которые вы получаете с реальным HTML.
источник
Я считаю, что ответ лежит в теории вычислений. Для анализа языка с помощью регулярных выражений он должен быть по определению «обычный» ( ссылка ). HTML не является обычным языком, так как он не соответствует ряду критериев для обычного языка (во многом благодаря множеству уровней вложенности, присущих HTML-коду). Если вас интересует теория вычислений, я бы порекомендовал эту книгу.
источник
Это выражение извлекает атрибуты из элементов HTML. Поддерживает:
(?:\<\!\-\-(?:(?!\-\-\>)\r\n?|\n|.)*?-\-\>)|(?:<(\S+)\s+(?=.*>)|(?<=[=\s])\G)(?:((?:(?!\s|=).)*)\s*?=\s*?[\"']?((?:(?<=\")(?:(?<=\\)\"|[^\"])*|(?<=')(?:(?<=\\)'|[^'])*)|(?:(?!\"|')(?:(?!\/>|>|\s).)+))[\"']?\s*)
Проверьте это . Лучше работает с флагами "gisx", как в демо.
источник
<script>
тега.HTML / XML делится на разметку и контент. Regex полезен только для анализа лексических тегов. Я думаю, вы могли бы вывести содержание. Это был бы хороший выбор для парсера SAX. Теги и контент могут быть доставлены в пользовательскую функцию, где можно отслеживать вложение / закрытие элементов.
Что касается простого анализа тегов, это можно сделать с помощью регулярного выражения и использовать для удаления тегов из документа.
За годы тестирования я обнаружил секрет того, как браузеры анализируют теги, как хорошо, так и плохо сформированные.
Нормальные элементы анализируются с помощью этой формы:
Ядро этих тегов использует это регулярное выражение
Вы заметите это
[^>]?
как одно из чередований. Это будет соответствовать несбалансированным цитатам из плохо сформированных тегов.Это также, самый корень всего зла в регулярных выражениях. То, как он используется, вызовет удар, чтобы удовлетворить его жадный, должен соответствовать количественный контейнер.
При пассивном использовании проблемы никогда не возникает. Но если вы заставляете что-либо совпадать, перемежая это с требуемой парой атрибут / значение, и не предоставляете адекватную защиту от обратного отслеживания, это неуправляемый кошмар.
Это общая форма для простых старых тегов. Обратите внимание на
[\w:]
представление имени тега? На самом деле, допустимые символы, представляющие имя тега, представляют собой невероятный список символов Юникода.Продвигаясь дальше, мы также видим, что вы просто не можете искать определенный тег без разбора ВСЕХ тегов. Я имею в виду, что вы могли бы, но он должен был бы использовать комбинацию глаголов, таких как (* 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]*?))))>
Отформатированный вид
источник
"Это зависит", хотя. Это правда, что регулярные выражения не делают и не могут анализировать HTML с истинной точностью по всем причинам, приведенным здесь. Однако, если последствия неправильного понимания (например, не обработка вложенных тегов) незначительны, и если регулярные выражения очень удобны в вашей среде (например, когда вы взламываете Perl), продолжайте.
Предположим, что вы, возможно, анализируете веб-страницы, которые ссылаются на ваш сайт - возможно, вы нашли их с помощью поиска ссылок в Google - и вы хотите получить быстрый способ получить общее представление о контексте вокруг вашей ссылки. Вы пытаетесь запустить небольшой отчет, который может предупредить вас о ссылках на спам, что-то вроде этого.
В этом случае неправильный анализ некоторых документов не будет иметь большого значения. Никто, кроме вас, не увидит ошибок, и если вам очень повезет, их будет достаточно, чтобы вы могли отслеживать их индивидуально.
Я думаю, я говорю, что это компромисс. Иногда реализация или использование правильного синтаксического анализатора - настолько легкого, насколько это возможно - может не стоить проблем, если точность не критична.
Просто будьте осторожны с вашими предположениями. Я могу придумать несколько способов, которыми ярлык регулярного выражения может иметь неприятные последствия, если вы пытаетесь проанализировать что-то, что будет показано, например, публично.
источник
Определенно есть случаи, когда использование регулярного выражения для синтаксического анализа некоторой информации из HTML - правильный путь - это во многом зависит от конкретной ситуации.
Консенсус выше, что в целом это плохая идея. Тем не менее, если структура HTML известна (и вряд ли изменится), то это все еще допустимый подход.
источник
Имейте в виду, что, хотя сам HTML не является регулярным, части просматриваемой страницы могут быть регулярными.
Например, это ошибка для
<form>
вложенных тегов; если веб-страница работает правильно, то использование регулярного выражения для захвата<form>
будет вполне разумным.Недавно я сделал несколько операций с использованием только Selenium и регулярных выражений. Я ушел с ним , потому что данные , которые я хотел было положить в
<form>
, и поставить в простом формате таблицы (так что я мог рассчитывать даже на<table>
,<tr>
и<td>
быть невложенных - что на самом деле очень необычно). В некоторой степени регулярные выражения были даже почти необходимы, потому что некоторые структуры, к которым мне нужно было получить доступ, были ограничены комментариями. (Beautiful Soup может дать вам комментарии, но было бы трудно захватить<!-- BEGIN -->
и<!-- END -->
заблокировать, используя Beautiful Soup.)Однако если бы мне пришлось беспокоиться о вложенных таблицах, мой подход просто не сработал бы! Мне бы пришлось вернуться на Beautiful Soup. Однако даже в этом случае иногда вы можете использовать регулярное выражение, чтобы получить нужный вам фрагмент, а затем перейти к следующему этапу.
источник
На самом деле, разбор HTML с помощью регулярных выражений вполне возможен в PHP. Вам просто нужно проанализировать всю строку в обратном направлении, используя,
strrpos
чтобы найти<
и повторить оттуда регулярное выражение, используя несвязные спецификаторы каждый раз, чтобы преодолеть вложенные теги. Не причудливый и ужасно медленный на больших вещах, но я использовал его для своего личного редактора шаблонов для моего сайта. Я на самом деле не разбирал HTML, но сделал несколько пользовательских тегов для запроса записей в базе данных для отображения таблиц данных (мой<#if()>
тег может выделять специальные записи таким образом). Я не был готов пойти на парсер XML только на пару самостоятельно созданных тегов (с очень не XML-данными внутри них) здесь и там.Таким образом, хотя этот вопрос значительно мертв, он все равно обнаруживается в поиске Google. Я прочитал его и подумал, что «вызов принят», и закончил исправление моего простого кода, не заменяя все. Решил предложить другое мнение любому, кто ищет подобную причину. Также последний ответ был опубликован 4 часа назад, так что это все еще горячая тема.
источник
<tag >
) Рассматривали ли вы закомментированные закрывающие теги? (Например,<tag> <!-- </tag> -->
) Вы рассматривали CDATA? Вы рассматривали теги с непоследовательным регистром? (Например,<Tag> </tAG>
) Вы это тоже учли?Я попробовал свои силы в регулярных выражениях для этого тоже. Он в основном полезен для поиска порций контента в паре со следующим HTML-тегом, и он не ищет подходящих тегов, но подбирает закрытые теги. Сверните стопку на вашем родном языке, чтобы проверить это.
Используйте с опциями 'sx'. «g» тоже, если вам повезет:
Этот предназначен для Python (он может работать для других языков, еще не пробовал, он использует положительные взгляды, отрицательные взгляды и именованные обратные ссылки). Поддержка:
<div ...>
</div>
<!-- ... -->
<![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
источник
Регулярные выражения недостаточно мощны для такого языка, как HTML. Конечно, есть несколько примеров, где вы можете использовать регулярные выражения. Но в целом это не подходит для разбора.
источник
Вы, знаете ... у вас много менталитета, вы НЕ МОЖЕТЕ это сделать, и я думаю, что все по обе стороны забора правы и неправы. Вы МОЖЕТЕ сделать это, но это требует немного больше обработки, чем просто выполнение одного регулярного выражения против него. Возьмите это (я написал это в течение часа) в качестве примера. Предполагается, что HTML-код полностью допустим, но в зависимости от того, какой язык вы используете для применения вышеупомянутого регулярного выражения, вы можете внести некоторые исправления в HTML-код, чтобы убедиться в его успешности. Например, удаление закрывающих тегов, которых там не должно быть:
</img>
например. Затем добавьте закрывающий одиночный слеш HTML к элементам, в которых они отсутствуют, и т. Д.Я бы использовал это в контексте написания библиотеки, которая позволила бы мне выполнять поиск элементов HTML
[x].getElementsByTagName()
, например, JavaScript . Я бы просто разделил функциональность, которую я написал в разделе DEFINE регулярного выражения, и использовал бы ее для перехода внутрь дерева элементов, по одному за раз.Итак, это будет окончательный 100% ответ для проверки HTML? Нет. Но это начало, и немного больше работы можно сделать. Однако попытка сделать это внутри одного выполнения регулярного выражения не практична и не эффективна.
источник