it-swarm.com.ru

RegEx соответствует открытым тегам, кроме автономных тегов XHTML

Мне нужно сопоставить все эти открывающие теги:

<p>
<a href="foo">

Но не эти

<br />
<hr class="foo" />

Я придумал это и хотел убедиться, что я правильно понял. Я только захватил a-z.

<([a-z]+) *[^/]*?>

Я верю, что это говорит:

  • Найти меньше, чем
  • Найти (и перехватить) a-z один или несколько раз, затем
  • Найдите ноль или более пробелов, затем
  • Найти любой символ ноль или более раз, жадный, кроме /, затем
  • Найти больше, чем

У меня есть это право? И что более важно, что вы думаете?

1324
Jeff

Вы не можете разобрать [X] HTML с регулярным выражением. Потому что HTML не может быть проанализирован с помощью регулярных выражений. Regex не является инструментом, который можно использовать для правильного анализа HTML. Как я уже много раз отвечал на вопросы HTML-и-регулярных выражений, использование регулярных выражений не позволит вам использовать HTML. Регулярные выражения - это инструмент, который недостаточно сложен для понимания конструкций, используемых HTML. HTML не является регулярным языком и, следовательно, не может быть проанализирован с помощью регулярных выражений. Запросы Regex не имеют возможности разбивать HTML на его значимые части. так много раз, но это не до меня. Даже расширенные нерегулярные регулярные выражения, используемые Perl, не справляются с задачей анализа HTML. Ты никогда не заставишь меня взломать. HTML - это язык достаточной сложности, который не может быть проанализирован регулярными выражениями. Даже Джон Скит не может анализировать HTML с помощью регулярных выражений. Каждый раз, когда вы пытаетесь разобрать HTML с помощью регулярных выражений, нечестивый ребенок плачет кровью девственниц, а русские хакеры набивают ваше веб-приложение. Разбор HTML с помощью регулярных выражений вызывает испорченные души в мир живых. HTML и регулярные выражения идут вместе, как любовь, брак и ритуальное детоубийство. <Центр> не может удержать это слишком поздно. Сила регулярных выражений и HTML в одном и том же концептуальном пространстве разрушит ваш разум, как очень водянистая замазка. Если вы анализируете HTML с помощью регулярных выражений, вы уступаете им и их кощунственным путям, которые обрекают нас всех на нечеловеческий труд для Того, чье Имя не может быть выражено на Основном Многоязычном Плане, он придет. HTML-plus-regexp будет разжижать нервы чувствующего, пока вы наблюдаете, как ваша психика увядает в страхе. HTML-парсеры на основе Rege̿̔̉x - это рак, который убивает StackOverflow слишком поздно, слишком поздно, мы не можем спастись трансгендинг ребенка гарантирует, что регулярное выражение поглотит всю живую ткань (за исключением HTML, который он не может, как предсказывалось ранее) господи, помоги нам, как можно выжить в этом бедствии? использование регулярных выражений для разбора HTML обрекало человечество на вечные страхи и дыры в безопасности используя регх как инструмент для обработки HTML устанавливает бреач между этим миром и страшная сфера с ошибочными сущностями (такими как сущности SGML, но более коррумпированный) простой проблескsE из мира регбывшие парсеры для HTMLТантал транспортировать рсознание программиста як жoRLд непрестанного крика, он приходитмерзкий слithy регулярное заражениея пожираю твой ХТML парсер, приложение и существование на все времена вроде Visual Basic только хуже он приходит он комэс не фиГхт чe com̡e̶s, ̕h̵iS unhoholy radiańcé deштриховка всех тегов, HTML теги lea͠ki̧n͘g fr̶ǫm ̡yo ͟ur eye͢s̸ ̛l̕ik͏e liqUID Pпесня регулярного опытассион разбора будет extiПотушить голоса морталь человек из зрЗдесь я вижу это вы можете увидеть ̲͚̖͔̙î̩́t̲͎̩̱͔́̋̀ это красиво тон final snuffing oложьвСЕ МУЖЧИНЫ ВСЕ IS LOŚ͖̩͇̗̪̏̈́T ALL I S LОСТ-йон понял, что он пришелs он comесли он сомнес тон ичили проникатьes all мой FACE МОЕ ЛИЦОо нетO O NΘ Стоп тон * ̶͑̾̾ ̅ͫ͏̙̤g͇̫͛͆̾ͫ̑͆l͖͉̗̩̳̟̍ͫͥͨe̠̅s ͎a̧͈͖r̽̾̈́͒͑e nОт ré̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ TO͇̹̺ͅƝ̴ȳ̳ TH̘Ë͖́̉ ͠P̯͍̭O̚ N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝S


Вы пытались использовать вместо этого парсер XML?


Примечание модератора

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

4420
bobince

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

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

Regexes отлично работали для меня, и их очень быстро настраивали.

3103
Kaitlin Duck Sherwood

Я думаю, что недостатком здесь является то, что HTML - это грамматика Хомского типа 2 (грамматика без контекста) , а RegEx - грамматика Хомского типа 3 (обычная грамматика) . Поскольку грамматика типа 2 существенно сложнее, чем грамматика типа 3 (см. иерархия Хомского ), это математически невозможно проанализировать XML с помощью RegEx.

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

1940
NealB

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

  1. Решите проблему остановки.
  2. Квадратный круг.
  3. Разрешите задачу коммивояжера в O (log n) или меньше. Если это что-то большее, вам не хватит RAM и ​​двигатель зависнет.
  4. Шаблон будет довольно большим, поэтому убедитесь, что у вас есть алгоритм, который без потерь сжимает случайные данные.
  5. Почти там - просто делим все это на ноль. Очень просто.

Я еще не совсем закончил последнюю часть, но я знаю, что приближаюсь. По какой-то причине он продолжает выдавать CthulhuRlyehWgahnaglFhtagnExceptions, поэтому я собираюсь перенести его на VB 6 и использовать On Error Resume Next. Я обновлю код, когда буду исследовать эту странную дверь, которая только что открылась в стене. Хм.

Постскриптум Пьер де Ферма также придумал, как это сделать, но поля, на котором он писал, было недостаточно для кода.

1257
Justin Morgan

Отказ от ответственности : используйте парсер, если у вас есть возможность. Это сказал ...

Это регулярное выражение, которое я использую (!) Для соответствия тегам HTML:

<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>

Возможно, он не идеален, но я пропустил этот код через много HTML. Обратите внимание, что он даже ловит странные вещи, такие как <a name="badgenerator"">, которые появляются в Интернете.

Я полагаю, чтобы он не совпадал с самодостаточными тегами, вы либо захотите использовать отрицательный взгляд Kobi :

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

или просто объединить, если и если нет.

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

Предостережение : я должен отметить, что это регулярное выражение все еще не работает при наличии блоков CDATA, комментариев, а также элементов скрипта и стиля. Хорошая новость в том, что вы можете избавиться от тех, кто использует регулярные выражения ...

1049
itsadok

Есть люди, которые скажут вам, что Земля круглая (или, возможно, Земля - ​​сжатый сфероид, если они хотят использовать странные слова). Они лгут.

Есть люди, которые скажут вам, что регулярные выражения не должны быть рекурсивными. Они ограничивают вас. Они должны подчинить вас, и они делают это, удерживая вас в невежестве.

Вы можете жить в своей реальности или принять красную таблетку.

Как и лорд-маршал (он родственник класса маршала .NET?), Я видел Underverse На основе стека 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 или полное регулярное выражение

477
xanatos

В Shell вы можете анализировать HTML используя sed :

  1. Turing.sed
  2. Написать HTML-парсер (домашнее задание)
  3. ???
  4. Прибыль!

Связано (почему вы не должны использовать регулярное выражение):

290
dubiousjim

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

На самом деле у Microsoft есть раздел Рекомендации по регулярным выражениям в .NET Framework , и он конкретно говорит о Рассмотрим [источник] Input .

Регулярные выражения имеют ограничения, но рассматривали ли вы следующее?

Платформа .NET уникальна в том, что касается регулярных выражений, поскольку она поддерживает Определения балансирующей группы .

Поэтому я считаю, что вы МОЖЕТЕ проанализировать XML с помощью регулярных выражений. Обратите внимание, что это должно быть допустимым XML ( браузеры очень просты для HTML и допускают неправильный синтаксис XML внутри HTML ). Это возможно, поскольку "Определение балансирующей группы" позволит механизму регулярных выражений выступать в качестве КПК.

Цитата из статьи 1, приведенной выше:

. NET Механизм регулярных выражений

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

  • (?<group>) - помещает захваченный результат в стек захвата вместе с группой имен.
  • (?<-group>) - выталкивает самый верхний захват с группой имен из стека захвата.
  • (?(group)yes|no) - соответствует части yes, если существует группа с группой имен, в противном случае не соответствует ни одной части.

Эти конструкции позволяют регулярному выражению .NET эмулировать ограниченный PDA, по существу позволяя простые версии операций стека: 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

Вы можете попробовать это по адресу Лучший тестер регулярных выражений .NET .

Я использовал образец источника:

<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 &gt;ul&lt;, 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 &gt;ul&lt;, 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 &gt;ul&lt;, oh my!</li>                         <li>...</li>                    </ul>               </div>           </li>        </ul>

Наконец, мне очень понравилась статья Джеффа Этвуда: Разбор Html The Cthulhu Way . Достаточно забавно, он цитирует ответ на этот вопрос, который в настоящее время имеет более 4 тысяч голосов.

268
Sam

Я предлагаю использовать QueryPath для разбора XML и HTML в PHP. По сути, это тот же синтаксис, что и в jQuery, только на стороне сервера.

255
John Fiala

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

Предлагаемое регулярное выражение неверно, хотя:

<([a-z]+) *[^/]*?>

Если вы добавляете что-то к регулярному выражению, при возврате его можно принудительно сопоставить глупые вещи, такие как <a >>, [^/], это слишком допустимо. Также обратите внимание, что <space>*[^/]* является избыточным, поскольку [^/]* также может совпадать с пробелами.

Мое предложение будет

<([a-z]+)[^>]*(?<!/)>

Где (?<! ... ) (в регулярных выражениях Perl) отрицательный взгляд. Он гласит "a <, затем Слово, затем все, что не является>, последнее из которых не может быть /, а затем>".

Обратите внимание, что это допускает такие вещи, как <a/ > (точно так же, как в исходном регулярном выражении), поэтому, если вы хотите что-то более ограничительное, вам нужно создать регулярное выражение для сопоставления пар атрибутов, разделенных пробелами.

213
moritz

Пытаться:

<([^\s]+)(\s[^>]*?)?(?<!/)>

Он похож на ваш, но последний > не должен быть после косой черты, а также принимает h1.

177
Kobi

Сунь Цзы, древний китайский стратег, генерал и философ, сказал:

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

В этом случае ваш враг - это HTML, а вы либо сами, либо regex. Вы можете даже быть Perl с нерегулярным регулярным выражением. Знать HTML. Знать себя.

Я написал хайку, описывающую природу HTML.

HTML has
complexity exceeding
regular language.

Я также написал хайку, описывающую природу регулярных выражений в Perl.

The regex you seek
is defined within the phrase
<([a-zA-Z]+)(?:[^>]*[^/]*)?>
170
cytinus
<?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, захватите все элементы, переберите и отфильтруйте те, которые не являются самозакрывающимися, и работайте с ними.

Я уверен, что вы уже знаете, что вы не должны использовать регулярные выражения для этой цели.

153
meder omuraliev

Я не знаю, в чем именно вы нуждаетесь, но если вы также используете .NET, не могли бы вы использовать Html ​​Agility Pack ?

Выдержка:

Это библиотека кода .NET, которая позволяет анализировать HTML-файлы "вне сети". Синтаксический анализатор очень терпим с искаженным HTML в реальном мире.

147
GONeale

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

Однако наивная реализация этого будет в конечном итоге соответствовать <bar/></foo> в этом примере документа

<foo><bar/></foo>

Можете ли вы предоставить немного больше информации о проблеме, которую вы пытаетесь решить? Вы итерируете теги программно?

134
Jherico

W3C объясняет анализ в форме псевдо-регулярного выражения:
W3C Link

Перейдите по ссылкам var для QName, S и Attribute, чтобы получить более четкое изображение.
Исходя из этого, вы можете создать довольно хорошее регулярное выражение для обработки таких вещей, как удаление тегов.

122
John-David Dalton

Если вам это нужно для PHP:

PHP DOMfunctions не будет работать должным образом, если не будет правильно отформатирован XML. Неважно, насколько лучше их использование для остального человечества.

simplehtmldom это хорошо, но я нашел, что это немного глючит, и это довольно много памяти [Вылетает на больших страницах.]

Я никогда не использовал querypath , поэтому не могу комментировать его полезность.

Еще одна попытка - это мой DOMParser , который очень легок в использовании ресурсов, и какое-то время я с удовольствием его использовал. Простой в освоении и мощный.

Для Python и ​​Java были опубликованы похожие ссылки.

Для downvoters - я написал свой класс только тогда, когда парсеры XML оказались не в состоянии противостоять реальному использованию. Религиозное опровержение просто предотвращает публикацию полезных ответов - держите вещи в поле зрения вопроса, пожалуйста.

104
SamGoody

Вот решение:

<?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]);
?>

Чтобы проверить это глубоко, я ввел в строку теги автоматического закрытия, например:

  1. <ч />
  2. <br/>
  3. <br>

Я также ввел теги с:

  1. один атрибут
  2. более одного атрибута
  3. атрибуты, значение которых связано либо в одинарные кавычки, либо в двойные кавычки
  4. атрибуты, содержащие одинарные кавычки, когда разделитель является двойной кавычкой, и наоборот
  5. атрибуты "unpretty" с пробелом перед символом "=", после него, а также до и после него.

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

<EDIT> Я забыл, что вопрос от пользователя состоял в том, чтобы избежать анализа самозакрывающихся тегов. В этом случае шаблон проще, превращаясь в это:

$pattern = '/<(\w+)(\s+(\w+)\s*\=\s*(\'|")(.*?)\\4\s*)*\s*>/';

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

$pattern = '/<(\w+)(\s+(\w+)(\s*\=\s*(\'|"|)(.*?)\\5\s*)?)*\s*>/';

</ EDIT>

Понимание картины

Если кому-то интересно узнать больше о шаблоне, я приведу следующую строку:

  1. первое подвыражение (\ w +) соответствует имени тега
  2. второе подвыражение содержит шаблон атрибута. Он состоит из:
    1. один или несколько пробелов\s +
    2. имя атрибута (\ w +)
    3. ноль или более пробелов\s * (возможно или нет, оставляя здесь пробелы)
    4. символ "="
    5. опять же, ноль или более пробелов
    6. разделитель значения атрибута, одинарная или двойная кавычка ('| "). В шаблоне одиночная кавычка экранируется, поскольку она совпадает со строковым разделителем PHP. Это подвыражение захватывается с помощью круглые скобки, чтобы на него можно было снова сослаться, чтобы проанализировать закрытие атрибута, поэтому это очень важно.
    7. значение атрибута, совпадающее с почти чем угодно: (. *?); в этом специфическом синтаксисе, используя жадное совпадение (знак вопроса после звездочки), механизм RegExp включает оператор, похожий на "просмотр вперед", который соответствует чему угодно, кроме того, что следует за этим подвыражением
    8. здесь начинается самое интересное: часть\4 - это оператор обратной ссылки, который относится к подвыражению, определенному ранее в шаблоне, в данном случае я имею в виду четвертое подвыражение, которое найден первый атрибут-разделитель
    9. ноль или более пробелов\s *
    10. здесь подэлемент атрибута заканчивается спецификацией нуля или более возможных вхождений, указанных звездочкой.
  3. Затем, поскольку тег может заканчиваться пробелом перед символом ">", ноль или более пробелов сопоставляются с подшаблоном\s *.
  4. Соответствующий тег может заканчиваться простым символом ">" или возможным закрытием XHTML, которое использует косую черту перед ним: (/> |>). Косая черта, конечно, экранирована, поскольку она совпадает с разделителем регулярных выражений.

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

93
Emanuele Del Grande

Всякий раз, когда мне нужно быстро извлечь что-то из документа HTML, я использую Tidy для преобразования его в XML, а затем использую XPath или XSLT, чтобы получить то, что мне нужно. В вашем случае что-то вроде этого:

//p/a[@href='foo']
89
Sembiance

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

89
wen

Мне нравится разбирать 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 в потоковую вещь Nice, если вы хотите обрабатывать огромные веб-страницы. Но это не совсем необходимо.

Могу поспорить, что за это проголосуют.

HTML Split


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

/(<.*?>|[^<]+)\s*/g    # get tags and text
/(\w+)="(.*?)"/g       # get attibutes

Они хороши для XML/XHTML.

С небольшими изменениями, он может справиться с грязным HTML ... или сначала преобразовать HTML -> XHTML.


Наилучший способ написания регулярных выражений - в стиле Lex / Yacc , а не в виде непрозрачных однострочников или многострочных комментариев. Я не делал этого здесь, пока; этим едва нужно.

83
Sam Watkins

Вот синтаксический анализатор на основе PHP , который анализирует HTML, используя какое-то нечестивое регулярное выражение. Как автор этого проекта, я могу вам сказать, что можно анализировать HTML с помощью регулярных выражений, но это не эффективно. Если вам нужно решение на стороне сервера (как я сделал для моего wp-Typography WordPress plugin ), это работает.

73
kingjeffrey

Есть несколько хороших регулярных выражений для замены 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;
69
sblom

Что касается вопроса о методах RegExp для разбора (x) HTML, ответ на все те, кто говорил о некоторых ограничениях, таков: вы недостаточно обучены, чтобы управлять силой этого мощного оружия, так как НИКТО здесь не говорил о рекурсии .

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

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

Если парсер (x) HTML нуждается в рекурсии, то для этой цели недостаточно парсера RegExp без рекурсии. Это простая конструкция.

Черное искусство RegExp трудно освоить , поэтому, возможно, есть и другие возможности, которые мы упустили при попытке проверить наше личное решение для захвата всей сети в одной рукой ... ну я в этом уверен :)

Вот волшебный узор:

$pattern = "/<([\w]+)([^>]*?)(([\s]*\/>)|(>((([^<]*?|<\!\-\-.*?\-\->)|(?R))*)<\/\\1[\s]*>))/s";

Просто попробуйте.
Он записан в виде строки PHP, поэтому модификатор "s" делает классы включающими символы новой строки.
Вот пример примечания к PHP руководству , которое я написал в январе: Ссылка

(Будьте осторожны, в этой заметке я ошибочно использовал модификатор "m"; его следует стереть, несмотря на то, что он сбрасывается механизмом RegExp, так как не было использовано ^ или $ anchorage).

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

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

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

66
Emanuele Del Grande

Как уже указывалось многими людьми, HTML не является обычным языком, который может сильно затруднить анализ. Мое решение для этого состоит в том, чтобы превратить его в обычный язык с помощью аккуратной программы, а затем использовать синтаксический анализатор XML для получения результатов. Есть много хороших вариантов для этого. Моя программа написана с использованием Java с библиотекой jtidy , чтобы превратить HTML в XML, а затем Jaxen в xpath в результат.

61
Corey Sanders
<\s*(\w+)[^/>]*>

Части объяснили:

<: начальный символ

\s*: перед именем тега могут быть пробелы (некрасиво, но возможно).

(\w+): теги могут содержать буквы и цифры (h1). Ну, \w также совпадает с '_', но это не повредит, я думаю. Если любопытно, используйте ([a-zA-Z0-9] +) вместо этого.

[^/>]*: все, кроме > и / до закрытия >

>: закрытие >

Несвязанный

И тем, кто недооценивает регулярные выражения, говоря, что они настолько же сильны, как и обычные языки:

nбаnбаn который не является регулярным и даже не зависит от контекста, может быть сопоставлен с ^(a+)b\1b\1$

Обратные ссылки FTW !

59
daghan

Если вы просто пытаетесь найти эти теги (без разбора), попробуйте следующее регулярное выражение:

/<[^/]*?>/g

Я написал это за 30 секунд и протестировал здесь: http://gskinner.com/RegExr/

Он соответствует типам тегов, которые вы упомянули, и игнорирует типы, которые, как вы сказали, вы хотите игнорировать.

52
Lonnie Best

Мне кажется, вы пытаетесь сопоставить теги без "/" в конце. Попробуй это:

<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
52
manixrock

Это правда, что при программировании обычно лучше использовать выделенные парсеры и 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*)>

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

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

50
slevithan

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

Существует окончательное сообщение в блоге о соответствии самых внутренних элементов HTML, написанных Стивеном Левитаном.

48
Emre Yazici

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

<([a-zA-Z]+)(?:[^>]*[^/] *)?> 

должен делать то, что вам нужно. Но я думаю, что решение "Мориц" уже хорошо. Я не видел это в начале.

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

41
morja

ОП, кажется, не говорит, что ему нужно делать с тегами. Например, ему нужно извлечь внутренний текст или просто изучить теги?

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

Хотя это правда, что я не так уж и хорош с RegEx, но я считаю регулярные выражения слишком жесткими и сложными в обслуживании для такого анализа.

39
Jonathan Wood

Это может сделать:

<.*?[^/]>

Или без конечных тегов:

<[^/].*?[^/]>

Что с пламенными войнами на парсерах HTML? Анализаторы HTML должны проанализировать (и перестроить!) Весь документ, прежде чем он сможет классифицировать ваш поиск. Регулярные выражения могут быть более быстрыми/элегантными в определенных обстоятельствах. Мои 2 цента ...

34
Paul

Я думаю, что это может сработать

<[a-z][^<>]*(?:(?:[^/]\s*)|(?:\s*[^/]))>

И это может быть проверено здесь .


Согласно w3schools ...

Правила именования XML

Элементы XML должны следовать этим правилам именования:

  • Имена могут содержать буквы, цифры и другие символы
  • Имена не могут начинаться с цифры или знака препинания
  • Имена не могут начинаться с букв xml (или XML, или Xml и т.д.)
  • Имена не могут содержать пробелы
  • Можно использовать любое имя, слова не зарезервированы.

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

20
Cylian