RegEx соответствует открытым тегам, кроме автономных тегов XHTML
Мне нужно сопоставить все эти открывающие теги:
<p>
<a href="foo">
Но не эти
<br />
<hr class="foo" />
Я придумал это и хотел убедиться, что я правильно понял. Я только захватил a-z
,
<([a-z]+) *[^/]*?>
Я верю, что это говорит:
- Найти меньше, чем, то
- Найдите (и запишите) az один или несколько раз, затем
- Найдите ноль или более пробелов, затем
- Найти любой символ ноль или более раз, жадный, кроме
/
, затем - Найти больше, чем
У меня есть это право? И что более важно, что вы думаете?
41 ответ
Вы не можете разобрать [X]HTML с регулярным выражением. Потому что HTML не может быть проанализирован с помощью регулярных выражений. Regex не является инструментом, который можно использовать для правильного разбора HTML. Как я уже много раз отвечал на вопросы HTML и regex, использование regex не позволит вам использовать HTML. Регулярные выражения - это инструмент, который недостаточно сложен для понимания конструкций, используемых HTML. HTML не является регулярным языком и, следовательно, не может быть проанализирован с помощью регулярных выражений. Запросы Regex не оборудованы для разбиения HTML на его значимые части. так много раз, но это не до меня. Даже расширенные нерегулярные регулярные выражения, используемые в Perl, не справляются с задачей анализа HTML. Ты никогда не заставишь меня взломать. HTML - это язык достаточной сложности, который не может быть проанализирован регулярными выражениями. Даже Джон Скит не может анализировать HTML с помощью регулярных выражений. Каждый раз, когда вы пытаетесь анализировать HTML с помощью регулярных выражений, нечестивый ребенок плачет кровью девственниц, а русские хакеры набивают ваше веб-приложение. Разбор HTML с помощью регулярных выражений вызывает зараженные души в царство живых. HTML и регулярные выражения идут вместе, как любовь, брак и ритуальное детоубийство. <Центр> не может удержать это слишком поздно. Сила регулярных выражений и HTML в одном и том же концептуальном пространстве разрушит ваш разум, словно водянистая замазка. Если вы анализируете HTML с помощью регулярных выражений, вы уступаете им и их богохульным путям, которые обрекают нас всех на нечеловеческий труд для Того, чье Имя не может быть выражено на Основном Многоязычном Плане, он придет. HTML-plus-regexp будет разжижать нервы чувствующего, пока вы наблюдаете, как ваша психика увядает от ужаса. HTML-парсеры на основе регулярных выражений - это рак, убивающий Stackru. Уже слишком поздно, слишком поздно. Мы не можем спасти переход от ребенка, который гарантирует, что регулярное выражение поглотит всю живую ткань (кроме HTML, которого нет, как предсказывалось ранее), дорогой лорд Помогите нам, как кто-нибудь может выжить в этом бедствии, используя регулярное выражение для анализа HTML, обрекало человечество на вечность ужасных пыток и дыр в безопасности, используя регулярное выражение x в качестве инструмента для обработки HTML, устанавливающего разрыв между этим миром и ужасным царством неспокойных сущностей (таких как Субъекты SGML, но более испорченные), просто проблеск мира регулярных синтаксических анализаторов для HTML будет по-настоящему переносить сознание программиста в бесконечный крик, он приходит , ядовитая слабая регулярная инфекция будет л пожирать ваш HT ML парсер, применение и существование во все времена, как Visual Basic только хуже, он приходит он ком эс не фи GHT ч е пРИХОДИТ, s UNHOLY Привет Radiance де stro҉ying все enli̍ ̈Ghtenment, HTML теги Подтекание fr̶ǫm YO Ur глаз, как жика UID р айн, песня ОЧЕРЕДНОГО ехра повторно ssion разбор будет Exti nguish голоса мор тал человека от зр здесь я могу видеть, что это вы можете увидеть его это красиво, что он ж inal snuf
Ложь Человека ВСЕ ЕСТЬ ЛОЖЬ, ЧТО Я ПОЛУЧИЛСЯ, ПОЧЕМУ он приходит, когда он приходит ко мне, или он пронизывает все мое МОЕ ЛИЦО, МОЕ ЛИЦО, боже, нет НЕТ О ВКЛЮЧЕНО Θ Остановись он * ̶͑̾̾ ̅ͫ͏̙̤g͇̫͛͆̾ͫ̑͆l͖͉̗̩̳̟̍ͫͥͨ e̠̅s ͎a̧͈͖r̽̾̈́͒͑e
n ot ré̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ T O͇̹̺ͅƝ̴ȳ̳ TH̘ Ë͖́̉ ͠P̯͍̭O̚ N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S̨̥̫͎̭ͯ̿̔̀ͅ
Вы пробовали использовать вместо этого парсер XML?
Заметка модератора
Этот пост заблокирован, чтобы предотвратить несоответствующие изменения его содержания. Пост выглядит точно так, как он должен выглядеть - с его содержанием проблем нет. Пожалуйста, не отмечайте это для нашего внимания.
Хотя верно то, что запрос регулярных выражений для анализа произвольного HTML подобен просьбе новичка написать операционную систему, иногда целесообразно проанализировать ограниченный известный набор HTML.
Если у вас есть небольшой набор HTML-страниц, с которых вы хотите очистить данные и затем поместить их в базу данных, регулярные выражения могут работать нормально. Например, недавно я хотел получить имена, партии и округа федеральных представителей Австралии, которые я получил с веб-сайта парламента. Это была ограниченная разовая работа.
Regexes отлично работали для меня, и их очень быстро настраивали.
Я думаю, что недостаток в том, что HTML - это грамматика Chomsky Type 2 (контекстно-свободная грамматика), а RegEx - это грамматика Chomsky Type 3 (обычная грамматика). Поскольку грамматика типа 2 существенно сложнее, чем грамматика типа 3 (см. Иерархию Хомского), математически невозможно проанализировать XML с помощью RegEx.
Но многие попытаются, некоторые даже заявят о своем успехе - но пока другие не найдут ошибку и полностью не испортят вас.
Не слушай этих парней. Вы можете полностью разобрать грамматики без контекста с помощью регулярных выражений, если разбите задачу на более мелкие части. Вы можете создать правильный шаблон с помощью скрипта, который выполняет каждый из них по порядку:
- Решить проблему остановки.
- Квадратный круг.
- Решите задачу коммивояжера в O(log n) или меньше. Если это не так, у вас не хватит оперативной памяти, и двигатель зависнет.
- Шаблон будет довольно большим, поэтому убедитесь, что у вас есть алгоритм, который без потерь сжимает случайные данные.
- Почти там - просто делим все это на ноль. Очень просто.
Я еще не совсем закончил последнюю часть, но я знаю, что приближаюсь. Это продолжает бросать CthulhuRlyehWgahnaglFhtagnException
s по какой-то причине, поэтому я собираюсь перенести его на VB 6 и использовать On Error Resume Next
, Я обновлю код, когда буду исследовать эту странную дверь, которая только что открылась в стене. Хм.
PS Пьер де Ферма также придумал, как это сделать, но поля, на котором он писал, было недостаточно для кода.
Отказ от ответственности: используйте парсер, если у вас есть возможность. Это сказал...
Это регулярное выражение, которое я использую (!) Для соответствия тегам HTML:
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>
Возможно, он не идеален, но я пробовал этот код в большом количестве HTML. Обратите внимание, что он даже ловит странные вещи, такие как <a name="badgenerator"">
, которые появляются в Интернете.
Я полагаю, что для того, чтобы он не совпадал с самодостаточными тегами, вы можете использовать отрицательный взгляд Коби:
<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+(?<!/\s*)>
или просто объединить, если и если нет.
Для downvoters: это рабочий код из реального продукта. Я сомневаюсь, что у любого, кто читает эту страницу, сложится впечатление, что в HTML приемлемо использовать регулярные выражения.
Предостережение: я должен отметить, что это регулярное выражение все еще не работает при наличии блоков CDATA, комментариев и элементов скрипта и стиля. Хорошая новость в том, что вы можете избавиться от тех, кто использует регулярные выражения...
Есть люди, которые скажут вам, что Земля круглая (или, возможно, Земля - сжатый сфероид, если они хотят использовать странные слова). Они лгут.
Есть люди, которые скажут вам, что регулярные выражения не должны быть рекурсивными. Они ограничивают вас. Им нужно подчинить вас, и они делают это, удерживая вас в невежестве.
Вы можете жить в своей реальности или принять красную таблетку.
Как и лорд-маршал (он родственник класса маршала.NET?), Я видел Regex-Verse, основанный на обратном стэке, и вернулся со знанием способностей, которое вы не можете себе представить. Да, я думаю, что один или два старика защищали их, но они смотрели футбол по телевизору, так что это было несложно.
Я думаю, что случай XML довольно прост. RegEx (в синтаксисе.NET), дефлированный и закодированный в base64 для облегчения понимания вашим слабым умом, должен выглядеть примерно так:
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
+r/vD34mUADO1P4/AQAA//8=
Варианты для установки RegexOptions.ExplicitCapture
, Группа захвата, которую вы ищете ELEMENTNAME
, Если группа захвата ERROR
не пусто, тогда произошла ошибка синтаксического анализа, и Regex остановился.
Если у вас есть проблемы с преобразованием его в удобочитаемое регулярное выражение, это должно помочь:
static string FromBase64(string str)
{
byte[] byteArray = Convert.FromBase64String(str);
using (var msIn = new MemoryStream(byteArray))
using (var msOut = new MemoryStream()) {
using (var ds = new DeflateStream(msIn, CompressionMode.Decompress)) {
ds.CopyTo(msOut);
}
return Encoding.UTF8.GetString(msOut.ToArray());
}
}
Если вы не уверены, нет, я НЕ шучу (но, возможно, я лгу). Это будет работать. Я построил тонны модульных тестов, чтобы протестировать его, и я даже использовал (часть) тесты на соответствие. Это токенизатор, а не полноценный парсер, поэтому он будет только разбивать XML на его компонентные токены. Он не будет анализировать / интегрировать DTD.
Ох... если вы хотите исходный код регулярного выражения, с некоторыми вспомогательными методами:
регулярное выражение для токенизации XML или полный обычный регулярное выражение
В оболочке вы можете анализировать HTML, используя:
Сед хотя:
- Turing.sed
- Написать HTML-парсер (домашнее задание)
- ???
- Прибыль!
hxselect
отhtml-xml-utils
пакетvim
/ex
(который может легко переходить между HTML-тегами), например:удаление тега стиля с внутренним кодом:
$ curl -s http://example.com/ | ex -s +'/<style.*/norm nvatd' +%p -cq! /dev/stdin
grep
, например:извлечение внешнего HTML H1:
$ curl -s http://example.com/ | grep -o '<h1>.*</h1>' <h1>Example Domain</h1>
извлечение тела:
$ curl -s http://example.com/ | tr '\n' ' ' | grep -o '<body>.*</body>' <body> <div> <h1>Example Domain</h1> ...
html2text
для простого анализа текста:как парсинг таблиц:
$ html2text foo.txt | column -ts'|'
с помощью
xpath
(XML::XPath
модуль Perl), см. пример здесьPerl или Python (см. пример @Gilles)
для разбора нескольких файлов одновременно см.: Как проанализировать сто файлов html исходного кода в оболочке?
Связанный (почему вы не должны использовать регулярное выражение):
Я согласен, что правильный инструмент для анализа XML и особенно HTML - это анализатор, а не механизм регулярных выражений. Однако, как отмечали другие, иногда использование регулярных выражений происходит быстрее, проще и выполняет работу, если вы знаете формат данных.
На самом деле у Microsoft есть раздел Best Practices для регулярных выражений в.NET Framework, и он конкретно говорит о рассмотрении источника ввода.
Регулярные выражения имеют ограничения, но рассматривали ли вы следующее?
Платформа.NET уникальна в том, что касается регулярных выражений, поскольку она поддерживает определения балансирующих групп.
- См. Сопоставление сбалансированных конструкций с регулярными выражениями.NET
- См. Регулярные выражения.NET: регулярные выражения и сбалансированное сопоставление.
- См. Документы Microsoft по определениям балансирующих групп.
По этой причине я считаю, что вы МОЖЕТЕ проанализировать XML с помощью регулярных выражений. Однако обратите внимание, что это должен быть действительный XML (браузеры очень просты для HTML и допускают неверный синтаксис XML внутри HTML). Это возможно, поскольку "Определение балансирующей группы" позволит механизму регулярных выражений выступать в качестве КПК.
Цитата из статьи 1, приведенной выше:
Механизм регулярных выражений.NET
Как описано выше, правильно сбалансированные конструкции не могут быть описаны с помощью регулярного выражения. Однако механизм регулярных выражений.NET предоставляет несколько конструкций, которые позволяют распознавать сбалансированные конструкции.
(?<group>)
- помещает захваченный результат в стек захвата вместе с группой имен.(?<-group>)
- выскакивает самый верхний захват с группой имен из стека захвата.(?(group)yes|no)
- соответствует части "да", если существует группа с именем группы, в противном случае не соответствует ни одной части.Эти конструкции позволяют регулярному выражению.NET эмулировать ограниченный КПК, по существу позволяя простые версии операций стека: push, pop и empty. Простые операции в значительной степени эквивалентны увеличению, уменьшению и сравнению с нулем соответственно. Это позволяет механизму регулярных выражений.NET распознавать подмножество контекстно-свободных языков, в частности те, которые требуют только простого счетчика. Это, в свою очередь, позволяет нетрадиционным регулярным выражениям.NET распознавать отдельные правильно сбалансированные конструкции.
Рассмотрим следующее регулярное выражение:
(?=<ul\s+id="matchMe"\s+type="square"\s*>)
(?>
<!-- .*? --> |
<[^>]*/> |
(?<opentag><(?!/)[^>]*[^/]>) |
(?<-opentag></[^>]*[^/]>) |
[^<>]*
)*
(?(opentag)(?!))
Используйте флаги:
- Одна линия
- IgnorePatternWhitespace (необязательно, если вы сверните регулярное выражение и удалите все пробелы)
- IgnoreCase (не обязательно)
Объясненное регулярное выражение (встроенный)
(?=<ul\s+id="matchMe"\s+type="square"\s*>) # match start with <ul id="matchMe"...
(?> # atomic group / don't backtrack (faster)
<!-- .*? --> | # match xml / html comment
<[^>]*/> | # self closing tag
(?<opentag><(?!/)[^>]*[^/]>) | # push opening xml tag
(?<-opentag></[^>]*[^/]>) | # pop closing xml tag
[^<>]* # something between tags
)* # match as many xml tags as possible
(?(opentag)(?!)) # ensure no 'opentag' groups are on stack
Вы можете попробовать это в A Better.NET Regular Expression Tester.
Я использовал образец источника:
<html>
<body>
<div>
<br />
<ul id="matchMe" type="square">
<li>stuff...</li>
<li>more stuff</li>
<li>
<div>
<span>still more</span>
<ul>
<li>Another >ul<, oh my!</li>
<li>...</li>
</ul>
</div>
</li>
</ul>
</div>
</body>
</html>
Это нашло совпадение:
<ul id="matchMe" type="square">
<li>stuff...</li>
<li>more stuff</li>
<li>
<div>
<span>still more</span>
<ul>
<li>Another >ul<, oh my!</li>
<li>...</li>
</ul>
</div>
</li>
</ul>
хотя на самом деле получилось так:
<ul id="matchMe" type="square"> <li>stuff...</li> <li>more stuff</li> <li> <div> <span>still more</span> <ul> <li>Another >ul<, oh my!</li> <li>...</li> </ul> </div> </li> </ul>
Наконец, мне очень понравилась статья Джеффа Этвуда: Parsing Html The Cthulhu Way. Достаточно забавно, он цитирует ответ на этот вопрос, который в настоящее время имеет более 4 тысяч голосов.
Я предлагаю использовать QueryPath для разбора XML и HTML в PHP. По сути, это тот же синтаксис, что и в jQuery, только на стороне сервера.
Хотя ответы, которые вы не можете проанализировать с помощью регулярных выражений, верны, они здесь не применимы. OP просто хочет проанализировать один HTML-тег с регулярными выражениями, и это можно сделать с помощью регулярного выражения.
Предлагаемое регулярное выражение неверно, хотя:
<([a-z]+) *[^/]*?>
Если вы добавите что-то к регулярному выражению, при возврате его можно будет сопоставить глупые вещи, такие как <a >>
, [^/]
слишком разрешительный. Также обратите внимание, что <space>*[^/]*
избыточно, потому что [^/]*
также может соответствовать пробелам.
Мое предложение будет
<([a-z]+)[^>]*(?<!/)>
куда (?<! ... )
является (в регулярных выражениях Perl) отрицательным взглядом. Он читает "a <, затем слово, затем все, что не является>, последнее из которых не может быть /, а затем>".
Обратите внимание, что это позволяет такие вещи, как <a/ >
(точно так же, как исходное регулярное выражение), поэтому, если вы хотите что-то более ограничительное, вам нужно создать регулярное выражение, чтобы соответствовать парам атрибутов, разделенных пробелами.
Сунь Цзы, древний китайский стратег, генерал и философ, сказал:
Говорят, что если вы знаете своих врагов и знаете себя, вы можете выиграть сотню сражений без единой потери. Если вы знаете только себя, но не своего оппонента, вы можете выиграть или проиграть. Если вы не знаете ни себя, ни своего врага, вы всегда будете подвергать опасности себя.
В этом случае ваш враг - это HTML, а вы - либо вы, либо regex. Вы можете даже быть Perl с нерегулярным регулярным выражением. Знать HTML. Знать себя.
Я написал хайку, описывающую природу HTML.
HTML has
complexity exceeding
regular language.
Я также написал хайку, описывающую природу регулярных выражений в Perl.
The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>
Пытаться:
<([^\s]+)(\s[^>]*?)?(?<!/)>
Он похож на ваш, но последний >
не должно быть после косой черты, а также принимает h1
,
<?php
$selfClosing = explode(',', 'area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed');
$html = '
<p><a href="#">foo</a></p>
<hr/>
<br/>
<div>name</div>';
$dom = new DOMDocument();
$dom->loadHTML($html);
$els = $dom->getElementsByTagName('*');
foreach ( $els as $el ) {
$nodeName = strtolower($el->nodeName);
if ( !in_array( $nodeName, $selfClosing ) ) {
var_dump( $nodeName );
}
}
Выход:
string(4) "html"
string(4) "body"
string(1) "p"
string(1) "a"
string(3) "div"
По сути, просто определите имена узлов элементов, которые являются самозакрывающимися, загрузите всю строку html в библиотеку DOM, захватите все элементы, переберите и отфильтруйте те, которые не являются самозакрывающимися, и работайте с ними.
Я уверен, что вы уже знаете, что вы не должны использовать регулярные выражения для этой цели.
Я не знаю, в чем именно вы нуждаетесь, но если вы также используете.NET, не могли бы вы использовать Html Agility Pack?
Выдержка:
Это библиотека кода.NET, которая позволяет анализировать HTML-файлы "вне сети". Синтаксический анализатор очень терпим с искаженным HTML "реального мира".
Вы хотите первый >
не предшествует /
, Посмотрите здесь для деталей о том, как это сделать. Это называется негативным взглядом сзади.
Однако наивная реализация этого приведет к <bar/></foo>
в этом примере документа
<foo><bar/></foo>
Можете ли вы предоставить немного больше информации о проблеме, которую вы пытаетесь решить? Вы просматриваете теги программно?
W3C объясняет анализ в форме псевдо-регулярного выражения:
W3C Link
Перейдите по ссылкам var для QName
, S
, а также Attribute
чтобы получить более четкую картину.
Основываясь на этом, вы можете создать довольно хорошее регулярное выражение для обработки таких вещей, как удаление тегов.
Если вам это нужно для PHP:
Функции PHP DOM не будут работать должным образом, если не будет правильно отформатирован XML. Неважно, насколько лучше их использование для остального человечества.
simplehtmldom- это хорошо, но я обнаружил, что он немного глючит, и это довольно много памяти [вылетает на больших страницах.]
Я никогда не использовал http://querypath.org/, поэтому не могу комментировать его полезность.
Еще один пример- мой DOMParser, который очень лёгок на ресурсах, и какое-то время я с удовольствием его использовал. Простой в освоении и мощный.
Для Python и Java были размещены похожие ссылки.
Для downvoters - я написал свой класс только тогда, когда парсеры XML оказались не в состоянии противостоять реальному использованию. Религиозное опровержение просто предотвращает публикацию полезных ответов - держите вещи в поле зрения вопроса, пожалуйста.
Вот решение:
<?php
// here's the pattern:
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*(\/>|>)/';
// a string to parse:
$string = 'Hello, try clicking <a href="#paragraph">here</a>
<br/>and check out.<hr />
<h2>title</h2>
<a name ="paragraph" rel= "I\'m an anchor"></a>
Fine, <span title=\'highlight the "punch"\'>thanks<span>.
<div class = "clear"></div>
<br>';
// let's get the occurrences:
preg_match_all($pattern, $string, $matches, PREG_PATTERN_ORDER);
// print the result:
print_r($matches[0]);
?>
Чтобы проверить это глубоко, я ввел в строку теги автоматического закрытия, например:
- <час />
-
-
Я также ввел теги с:
- один атрибут
- более одного атрибута
- атрибуты, значение которых связано либо в одинарные кавычки, либо в двойные кавычки
- атрибуты, содержащие одинарные кавычки, когда разделитель является двойной кавычкой, и наоборот
- "unpretty" атрибуты с пробелом перед символом "=", после него, а также до и после него.
Если вы найдете что-то, что не работает в доказательстве концепции выше, я могу проанализировать код, чтобы улучшить мои навыки.
$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';
Пользователь @ridgerunner заметил, что шаблон не допускает атрибутов без кавычек или атрибутов без значения. В этом случае тонкая настройка приносит нам следующую схему:
$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';
EDIT>
Понимание картины
Если кому-то интересно узнать больше о шаблоне, я приведу следующую строку:
- первое подвыражение (\w+) соответствует имени тега
- второе подвыражение содержит шаблон атрибута. Он состоит из:
- один или несколько пробелов \s+
- имя атрибута (\w+)
- ноль или более пробелов \s* (возможно или нет, оставляя здесь пробелы)
- символ "="
- опять же, ноль или более пробелов
- разделитель значения атрибута, одинарная или двойная кавычка ('|"). В шаблоне одиночная кавычка экранируется, поскольку она совпадает с разделителем строк PHP. Это подвыражение захватывается скобками, поэтому на него можно ссылаться еще раз проанализировать закрытие атрибута, вот почему это очень важно.
- значение атрибута, сопоставляемого практически с чем угодно: (.*?); в этом специфическом синтаксисе, используя жадное совпадение (знак вопроса после звездочки), механизм RegExp включает оператор, похожий на "упреждающий", который сопоставляет что угодно, кроме того, что следует за этим подвыражением
- тут самое интересное: часть \4 является оператором обратной ссылки, который ссылается на подвыражение, определенное ранее в шаблоне, в данном случае я имею в виду четвертое подвыражение, которое является первым найденным разделителем атрибута
- ноль или более пробелов \ s *
- здесь под-выражение атрибута заканчивается указанием нуля или более возможных вхождений, указанных звездочкой.
- Затем, поскольку тег может заканчиваться пробелом перед символом ">", ноль или более пробелов сопоставляются с подшаблоном \ s *.
- Соответствующий тег может заканчиваться простым символом ">" или возможным закрытием XHTML, которое использует косую черту перед ним: (/>|>). Косая черта, конечно, экранирована, поскольку она совпадает с разделителем регулярных выражений.
Небольшой совет: чтобы лучше проанализировать этот код, необходимо взглянуть на сгенерированный исходный код, так как я не предоставлял никаких специальных символов HTML для экранирования.
Всякий раз, когда мне нужно быстро извлечь что-то из документа HTML, я использую Tidy, чтобы преобразовать его в XML, а затем использую XPath или XSLT, чтобы получить то, что мне нужно. В вашем случае что-то вроде этого:
//p/a[@href='foo']
Я использовал инструмент с открытым исходным кодом под названием HTMLParser раньше. Он предназначен для анализа HTML различными способами и служит для этой цели достаточно хорошо. Он может анализировать HTML как другой treenode, и вы можете легко использовать его API для получения атрибутов из узла. Проверьте это и посмотрите, может ли это помочь вам.
Мне нравится разбирать HTML с помощью регулярных выражений. Я не пытаюсь разобрать идиота HTML, который намеренно нарушен. Этот код мой основной парсер (Perl-издание):
$_ = join "",<STDIN>; tr/\n\r \t/ /s; s/</\n</g; s/>/>\n/g; s/\n ?\n/\n/g;
s/^ ?\n//s; s/ $//s; print
Он называется htmlsplit, разбивает HTML на строки с одним тегом или фрагментом текста в каждой строке. Затем строки могут быть обработаны другими текстовыми инструментами и скриптами, такими как grep, sed, Perl и т. Д. Я даже не шучу:) Наслаждайтесь.
Достаточно просто перенастроить мой Perl-скрипт slurp-everything-first в хорошую потоковую вещь, если вы хотите обрабатывать огромные веб-страницы. Но это не совсем необходимо.
Могу поспорить, что за это проголосуют.
Вопреки моим ожиданиям это вызвало некоторые положительные отзывы, поэтому я предложу несколько лучших регулярных выражений:
/(<.*?>|[^<]+)\s*/g # get tags and text
/(\w+)="(.*?)"/g # get attibutes
Они хороши для XML / XHTML.
С небольшими изменениями, он может справиться с грязным HTML... или сначала преобразовать HTML -> XHTML.
Лучший способ написания регулярных выражений - в стиле Lex / Yacc, а не в виде непрозрачных однострочных или прокомментированных многострочных уродств. Я не делал этого здесь, пока; этим едва нужно.
Вот синтаксический анализатор, основанный на PHP, который анализирует HTML, используя какое-то нечестивое регулярное выражение. Как автор этого проекта, я могу вам сказать, что можно анализировать HTML с помощью регулярных выражений, но это не эффективно. Если вам нужно решение на стороне сервера (как я сделал для моего плагина wp-Typography WordPress), это работает.
На вопрос о методах RegExp для синтаксического анализа (x)HTML ответ на все те, кто говорил о некоторых ограничениях, таков: вы недостаточно обучены, чтобы управлять силой этого мощного оружия, поскольку здесь никто не говорил о рекурсии.
Коллега по RegExp-агностику уведомил меня об этом обсуждении, которое, безусловно, не является первым в Интернете по этой старой и горячей теме.
После прочтения некоторых постов первым делом я искал строку "?R" в этой теме. Вторым был поиск по поводу "рекурсии".
Нет, святая корова, совпадений не найдено.
Поскольку никто не упомянул основной механизм, на котором построен синтаксический анализатор, я вскоре понял, что никто не понял, в чем дело.
Если парсер (x)HTML нуждается в рекурсии, то для этой цели недостаточно парсера RegExp без рекурсии. Это простая конструкция.
Черное искусство RegExp сложно освоить, поэтому, возможно, есть и другие возможности, которые мы упустили, пытаясь и тестируя наше личное решение, чтобы захватить всю сеть в одной руке... Ну, я в этом уверен:)
Вот волшебный узор:
$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";
Просто попробуйте.
Он написан в виде строки PHP, поэтому модификатор "s" делает классы включающими символы новой строки.
Вот пример примечания к руководству по PHP, которое я написал в январе:
(Будьте осторожны, в этой заметке я неправильно использовал модификатор "m"; его следует стереть, несмотря на то, что он сбрасывается механизмом RegExp, так как не использовался ^ или $ anchorage).
Теперь мы можем говорить об ограничениях этого метода с более осознанной точки зрения:
- в соответствии с конкретной реализацией механизма RegExp, рекурсия может иметь ограничение на количество анализируемых вложенных шаблонов, но это зависит от используемого языка.
- Хотя поврежденный (x)HTML не приводит к серьезным ошибкам, он не очищается.
В любом случае это всего лишь шаблон RegExp, но он раскрывает возможность разработки множества мощных реализаций.
Я написал этот шаблон для поддержки анализатора рекурсивного спуска механизма шаблонов, который я встроил в мою среду, и производительность действительно велика, как во время выполнения, так и в использовании памяти (ничего общего с другими механизмами шаблонов, которые используют тот же синтаксис).
Здесь есть несколько хороших регулярных выражений для замены HTML на BBCode. Обратите внимание, что для всех вас, кто говорит "нет", он не пытается полностью разобрать HTML-код, а просто очищает его. Вероятно, он может позволить себе убивать теги, которые его простой "парсер" не может понять.
Например:
$store =~ s/http:/http:\/\//gi;
$store =~ s/https:/https:\/\//gi;
$baseurl = $store;
if (!$query->param("ascii")) {
$html =~ s/\s\s+/\n/gi;
$html =~ s/<pre(.*?)>(.*?)<\/pre>/\[code]$2\[\/code]/sgmi;
}
$html =~ s/\n//gi;
$html =~ s/\r\r//gi;
$html =~ s/$baseurl//gi;
$html =~ s/<h[1-7](.*?)>(.*?)<\/h[1-7]>/\n\[b]$2\[\/b]\n/sgmi;
$html =~ s/<p>/\n\n/gi;
$html =~ s/<br(.*?)>/\n/gi;
$html =~ s/<textarea(.*?)>(.*?)<\/textarea>/\[code]$2\[\/code]/sgmi;
$html =~ s/<b>(.*?)<\/b>/\[b]$1\[\/b]/gi;
$html =~ s/<i>(.*?)<\/i>/\[i]$1\[\/i]/gi;
$html =~ s/<u>(.*?)<\/u>/\[u]$1\[\/u]/gi;
$html =~ s/<em>(.*?)<\/em>/\[i]$1\[\/i]/gi;
$html =~ s/<strong>(.*?)<\/strong>/\[b]$1\[\/b]/gi;
$html =~ s/<cite>(.*?)<\/cite>/\[i]$1\[\/i]/gi;
$html =~ s/<font color="(.*?)">(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<font color=(.*?)>(.*?)<\/font>/\[color=$1]$2\[\/color]/sgmi;
$html =~ s/<link(.*?)>//gi;
$html =~ s/<li(.*?)>(.*?)<\/li>/\[\*]$2/gi;
$html =~ s/<ul(.*?)>/\[list]/gi;
$html =~ s/<\/ul>/\[\/list]/gi;
$html =~ s/<div>/\n/gi;
$html =~ s/<\/div>/\n/gi;
$html =~ s/<td(.*?)>/ /gi;
$html =~ s/<tr(.*?)>/\n/gi;
$html =~ s/<img(.*?)src="(.*?)"(.*?)>/\[img]$baseurl\/$2\[\/img]/gi;
$html =~ s/<a(.*?)href="(.*?)"(.*?)>(.*?)<\/a>/\[url=$baseurl\/$2]$4\[\/url]/gi;
$html =~ s/\[url=$baseurl\/http:\/\/(.*?)](.*?)\[\/url]/\[url=http:\/\/$1]$2\[\/url]/gi;
$html =~ s/\[img]$baseurl\/http:\/\/(.*?)\[\/img]/\[img]http:\/\/$1\[\/img]/gi;
$html =~ s/<head>(.*?)<\/head>//sgmi;
$html =~ s/<object>(.*?)<\/object>//sgmi;
$html =~ s/<script(.*?)>(.*?)<\/script>//sgmi;
$html =~ s/<style(.*?)>(.*?)<\/style>//sgmi;
$html =~ s/<title>(.*?)<\/title>//sgmi;
$html =~ s/<!--(.*?)-->/\n/sgmi;
$html =~ s/\/\//\//gi;
$html =~ s/http:\//http:\/\//gi;
$html =~ s/https:\//https:\/\//gi;
$html =~ s/<(?:[^>'"]*|(['"]).*?\1)*>//gsi;
$html =~ s/\r\r//gi;
$html =~ s/\[img]\//\[img]/gi;
$html =~ s/\[url=\//\[url=/gi;
<\s*(\w+)[^/>]*>
Части объяснили:
<
: начальный персонаж
\s*
: может иметь пробелы перед именем тега (некрасиво, но возможно).
(\w+)
: теги могут содержать буквы и цифры (h1). Что ж, \w
также соответствует '_', но это не больно, я думаю. Если любопытно, используйте ([a-zA-Z0-9]+) вместо этого.
[^/>]*
: все, кроме >
а также /
до закрытия >
>
закрытие >
несвязанный
И тем, кто недооценивает регулярные выражения, говоря, что они настолько же сильны, как и обычные языки:
an ban ban, который не является регулярным и даже не контекстным, может быть сопоставлен с ^(a+)b\1b\1$
Обратные ссылки FTW!
Как уже указывалось многими людьми, HTML не является обычным языком, который может сильно затруднить анализ. Мое решение для этого состоит в том, чтобы превратить его в обычный язык с помощью аккуратной программы, а затем использовать синтаксический анализатор XML для получения результатов. Есть много хороших вариантов для этого. Моя программа написана с использованием Java с библиотекой jtidy, чтобы превратить HTML в XML, а затем Jaxen в xpath в результат.
Если вы просто пытаетесь найти эти теги (без разбора), попробуйте следующее регулярное выражение:
/<[^/]*?>/g
Я написал это за 30 секунд и протестировал здесь: http://gskinner.com/RegExr/
Он соответствует типам тегов, которые вы упомянули, игнорируя типы, о которых вы сказали, что хотите игнорировать.
Недавно я написал дезинфицирующее средство HTML на Java. Он основан на смешанном подходе регулярных выражений и кода Java. Лично я ненавижу регулярные выражения и их глупость (удобочитаемость, удобство обслуживания и т. Д.), Но если вы сократите область применения этих приложений, они могут соответствовать вашим потребностям. В любом случае, мое дезинфицирующее средство использует белый список для тегов HTML и черный список для некоторых атрибутов стиля.
Для вашего удобства я настроил игровую площадку, чтобы вы могли проверить, соответствует ли код вашим требованиям: игровая площадка и код Java. Ваше мнение будет оценено.
В моем блоге есть небольшая статья с описанием этой работы: http://roberto.open-lab.com/
Это правда, что при программировании обычно лучше использовать выделенные парсеры и API вместо регулярных выражений при работе с HTML, особенно если точность имеет первостепенное значение (например, если ваша обработка может иметь последствия для безопасности). Однако я не приписываю догматической точке зрения, что разметка в стиле XML никогда не должна обрабатываться с помощью регулярных выражений. Бывают случаи, когда регулярные выражения являются отличным инструментом для работы, например, при одноразовом редактировании в текстовом редакторе, исправлении поврежденных файлов XML или работе с форматами файлов, которые выглядят, но не совсем как XML. Есть некоторые проблемы, о которых нужно знать, но они не являются непреодолимыми или даже необязательно актуальными.
Простое регулярное выражение типа <([^>"']|"[^"]*"|'[^']*')*>
обычно достаточно хорош, в тех случаях, которые я только что упомянул. Это наивное решение, учитывая все обстоятельства, но оно действительно позволяет незакодировать >
символы в значениях атрибутов. Если вы ищете, например, table
тег, вы можете адаптировать его как </?table\b([^>"']|"[^"]*"|'[^']*')*>
,
Просто чтобы дать представление о том, как будет выглядеть более "продвинутое" регулярное выражение HTML, следующее делает довольно респектабельную работу по эмуляции реального поведения браузера и алгоритма синтаксического анализа HTML5:
</?([A-Za-z][^\s>/]*)(?:=\s*(?:"[^"]*"|'[^']*'|[^\s>]+)|[^>])*(?:>|$)
Следующее соответствует довольно строгому определению тегов XML (хотя оно не учитывает полный набор символов Unicode, разрешенных в именах XML):
<(?:([_:A-Z][-.:\w]*)(?:\s+[_:A-Z][-.:\w]*\s*=\s*(?:"[^"]*"|'[^']*'))*\s*/?|/([_:A-Z][-.:\w]*)\s*)>
Конечно, они не учитывают окружающий контекст и несколько крайних случаев, но даже с такими вещами можно разобраться, если вы действительно хотите (например, путем поиска совпадений другого регулярного выражения).
В конце концов, используйте наиболее подходящий инструмент для работы, даже в тех случаях, когда этот инструмент является регулярным выражением.
Мне кажется, вы пытаетесь сопоставить теги без "/" в конце. Попробуй это:
<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>