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

Nov 14 2009

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

<p>
<a href="foo">

Но не эти:

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

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

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

Я считаю, что там сказано:

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

Я имею это право? И что еще более важно, что вы думаете?

Ответы

4413 bobince Nov 14 2009 at 06:04

Вы не можете разобрать [X] HTML с помощью регулярного выражения. Потому что HTML не может быть проанализирован с помощью регулярного выражения. Regex не является инструментом, который можно использовать для правильного синтаксического анализа HTML. Как я уже много раз отвечал на вопросы HTML и регулярных выражений, использование регулярных выражений не позволит вам использовать HTML. Регулярные выражения - это инструмент, который недостаточно сложен для понимания конструкций, используемых HTML. HTML не является обычным языком и поэтому не может быть проанализирован с помощью регулярных выражений. Запросы с регулярными выражениями не способны разбивать HTML на его значимые части. столько раз, но меня это не касается. Даже усовершенствованные нерегулярные регулярные выражения, используемые Perl, не подходят для анализа HTML. Ты никогда не заставишь меня сломаться. HTML - это язык с достаточной сложностью, поэтому его нельзя проанализировать с помощью регулярных выражений. Даже Джон Скит не может анализировать HTML с помощью регулярных выражений. Каждый раз, когда вы пытаетесь разобрать HTML с помощью регулярных выражений, нечестивый ребенок плачет кровью девственниц, а русские хакеры взламывают ваше веб-приложение. Анализ HTML с помощью регулярного выражения вызывает испорченные души в царство живых. HTML и регулярное выражение идут вместе, как любовь, брак и ритуальное детоубийство. <center> не может удержать это слишком поздно. Сила регулярного выражения и HTML вместе в одном концептуальном пространстве разрушит ваш разум, как жидкая замазка. Если вы разбираете HTML с помощью регулярных выражений, вы поддаетесь Им и их кощунственным способам, обрекающим всех нас на бесчеловечный труд для Того, чье Имя не может быть выражено в Основном Многоязычном Плане, - он приходит. HTML-plus-regexp сжижает нервы разумного, пока вы наблюдаете, как ваша психика увядает от натиска ужаса. Парсеры HTML на основе Rege̿̔̉x - это рак, убивающий StackOverflowслишком поздно, слишком поздно, мы не можем спасти преступление ребенка гарантирует, что регулярное выражение будет потреблять всю живую ткань (кроме HTML, который не может, как предсказывалось ранее), дорогой господин, помоги нам, как можно выжить в этой беде, используя регулярное выражение для синтаксического анализа HTML обрекли человечество к вечности страшных пыток и безопасности отверстий с помощью REGE х в качестве инструмента для процесса HTML устанавливает Brea ч между этим миром и страшным царству коррумпированных лиц (как субъекты SGML, но более коррумпированный) а glimp просто ЮВА мир рег бывшие анализаторы для HTML будет ины tantly транспортной ап сознание rogrammer, я Nto ав ORL d непрекращающихся кричать, он приходит , то надоедливый сл ithy регулярного выражение-инфекция Виля л пожирает ваш HT ML парсера, применение и существование для все время , как Visual Basic только хуже , он приходит он ком эс делать не фи GHT ч е пРИХОДИТ, привет с нечестивой Radiance де stro҉ying все просветление, HTML теги Подтеканием fr̶ǫm YO Ur глаза , как жидк UID р айн, песня ОЧЕРЕДНОЙ ехр повторно ssion разбор будет Exti nguish голоса мор тал человека от зр здесь я могу видеть , что вы можете видеть , что это красив т он е inal snufFing о й лжи s Мэн все потерянно A LL Я SL ОСТ й е PONY он пришел сек он CoM эс он совместно мне ул он ич или permeat эс ал л MY FAC E MY FACE ᵒh бог п о NO Ныо O ON Θ стоп т он * ̶͑̾̾ Gl éS n ot rè̑ͧ̌aͨl̘̝̙̃ͤ͂̾̆ ZA̡͊͠͝LGΌ ISͮ̂҉̯͈͕̹̘̱ T O͇̹̺ͅƝ̴ȳ̳ TH̘ Ë͖́̉ ͠P̯͍̭O̚ N̐Y̡ H̸̡̪̯ͨ͊̽̅̾̎Ȩ̬̩̾͛ͪ̈́̀́͘ ̶̧̨̱̹̭̯ͧ̾ͬC̷̙̲̝͖ͭ̏ͥͮ͟Oͮ͏̮̪̝͍M̲̖͊̒ͪͩͬ̚̚͜Ȇ̴̟̟͙̞ͩ͌͝ S̨̥̫͎̭ͯ̿̔̀ͅ ͎a̧͈͖r̽̾̈́͒͑e


Вы пробовали вместо этого использовать синтаксический анализатор XML?


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

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

3294 KaitlinDuckSherwood Nov 14 2009 at 13:27

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

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

Регулярные выражения у меня отлично работали, и их очень быстро настраивали.

2082 VladGudim Nov 19 2009 at 01:42

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

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

1363 JustinMorgan Mar 09 2011 at 00:49

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

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

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

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

1087 itsadok Nov 15 2009 at 13:37

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

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

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

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

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

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

или просто комбинируйте, если и если нет.

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

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

509 xanatos Mar 08 2011 at 20:30

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

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

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

Как и лорд Маршал (является ли он родственником класса Marshal .NET?), Я видел Regex-Verse Underverse Stack Based Regex-Verse и вернулся со знанием сил, которое вы не можете себе представить. Да, я думаю, их защищали один или двое стариков, но они смотрели футбол по телевизору, так что это было несложно.

Я думаю, что случай XML довольно прост. Регулярное выражение (в синтаксисе .NET), сжатое и закодированное в base64, чтобы облегчить понимание вашим слабым умом, должно быть примерно таким:

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
+r/vD34mUADO1P4/AQAA//8=

Вариантов множество RegexOptions.ExplicitCapture. Группа захвата, которую вы ищете ELEMENTNAME. Если группа захвата ERRORне пуста, значит произошла ошибка синтаксического анализа и регулярное выражение остановлено.

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

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 или полного простого регулярного выражения

304 kenorb Feb 15 2010 at 07:55

В оболочке вы можете анализировать HTML с помощью sed :

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

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

  • Если вам так нравятся регулярные выражения, почему бы вам не выйти за них замуж?
  • Регулярные выражения: теперь у вас две проблемы
  • Взлом обработчика HTML stackoverflow.com
278 Sam Sep 27 2011 at 11:01

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

На самом деле у Microsoft есть раздел « Лучшие практики для регулярных выражений в .NET Framework», в котором конкретно говорится о рассмотрении входного источника .

У регулярных выражений есть ограничения, но учли ли вы следующее?

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

  • См. Сопоставление сбалансированных конструкций с регулярными выражениями .NET.
  • См. Регулярные выражения .NET: регулярное выражение и сбалансированное сопоставление
  • См. Документацию Microsoft по определениям балансировки групп.

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

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

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

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

  • (?<group>) - помещает захваченный результат в стек захвата с именем 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 &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>

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

261 JohnFiala Nov 14 2009 at 06:44

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

222 moritz Jan 27 2010 at 19:54

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

Однако предлагаемое регулярное выражение неверно:

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

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

Мое предложение было бы

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

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

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

185 Kobi Nov 14 2009 at 05:50

Пытаться:

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

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

181 cytinus May 17 2012 at 17:13

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

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

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

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

HTML has
complexity exceeding
regular language.

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

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

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

149 GONeale Nov 17 2009 at 06:15

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

Отрывок:

Это библиотека кода .NET, которая позволяет анализировать файлы HTML «вне Интернета». Парсер очень терпимо относится к искаженному HTML "реального мира".

138 Jherico Nov 14 2009 at 05:47

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

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

<foo><bar/></foo>

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

123 John-DavidDalton Nov 15 2009 at 13:18

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

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

107 SamGoody Nov 17 2009 at 02:02

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

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

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

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

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

Для Python и Java были размещены аналогичные ссылки.

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

96 EmanueleDelGrande Jul 25 2011 at 21:35

Вот решение:

<?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.

92 AmalMurali Nov 18 2009 at 21:50

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

//p/a[@href='foo']
90 wen Nov 17 2009 at 01:34

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

84 SamWatkins Jul 12 2011 at 00:13

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

HTML-разделение


Некоторые лучшие регулярные выражения:

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

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

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


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

74 kingjeffrey Jul 18 2010 at 09:52

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

70 kenorb Apr 25 2010 at 23:38

Есть некоторые хорошие регулярные выражения для замены 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;
70 EmanueleDelGrande Jul 05 2010 at 21:16

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

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

Прочитав несколько сообщений, первое, что я сделал, - это поискал в этой теме строку «? R». Второй - искать на предмет «рекурсии».

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

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

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

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

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

Просто попробуйте. Он написан как строка PHP, поэтому модификатор «s» заставляет классы включать символы новой строки.

Вот образец примечания к руководству по PHP, которое я написал в январе: Ссылка

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

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

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

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

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

62 CoreySanders Feb 04 2010 at 23:22

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

60 daghan May 10 2012 at 20:53
<\s*(\w+)[^/>]*>

Объяснение частей:

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

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

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

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

>: Закрытие >

НЕ СВЯЗАННЫЙ

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

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

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

55 LonnieBest Jun 01 2012 at 12:13

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

/<[^/]*?>/g

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

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

53 manixrock Nov 16 2009 at 00:13

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

<([a-zA-Z][a-zA-Z0-9]*)[^>]*(?<!/)>
52 slevithan May 29 2012 at 06:27

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

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

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

49 EmreYazici Feb 09 2010 at 10:59

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

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