Использование регулярного выражения для соответствия блоку div, имеющему определенный идентификатор

Я пытаюсь сопоставить блок div, у которого есть определенный идентификатор. Вот мой код регулярного выражения:

<div\s+[^>]*\s*id\s*=\s*["|']content["|']\s*>[^/div]+ 

Я хочу, чтобы регулярное выражение соответствовало всему блоку div. Поэтому я помещаю [^ / div] + в мое регулярное выражение, я предполагаю, что он будет соответствовать оставшимся символам, пока он не достигнет конца, но он не смог соответствовать до конца, потому что выражение [^] считало, что я не хочу для соответствия любому, что есть </ | d | i | v | >. Я хочу, чтобы все это рассматривалось как целое. Попытка [^ ()] не поможет.

Поэтому, пожалуйста, скажите мне, как я должен кодировать эту проблему?

 <div id="content"> <noscript></noscript> <a href="blabla.com"> <h1> <a href="blablac.com">Blablabla</a> </h1> </div> 

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Во-первых, я согласен с тем, что, в общем, регулярное выражение не лучший инструмент для анализа HTML. Тем не менее, в правильных руках (и с несколькими предостережениями) мощная (и, безусловно, нерегулируемая ) библиотека PCRE Филиппа Хазеля (используемая семейством функций PHP preg_*() ) позволяет разрешать нетривиальные скребли данных такие проблемы, как этот (с некоторыми ограничениями и оговорками – см. ниже). Задача, описанная выше, особенно сложна для решения только с использованием регулярного выражения, а решения регулярных выражений, такие как приведенные ниже, предназначены не для всех и никогда не должны предприниматься новичком регулярного выражения. Для правильного понимания нижеприведенного ответа требуется достаточно глубокое понимание нескольких расширенных конструкций и методов регулярных выражений.

Разве кто-нибудь не подумает о детях! Да, я прочитал легендарный ответ бобинца, и я знаю, что здесь есть острый вопрос (по меньшей мере). Но, пожалуйста, если у вас есть соблазн сразу же нажать стрелку вниз, потому что я '/(?:actual|brave|stupid)ly/' используя слова: REGEX и: HTML на одном дыхании (и на не- – тривиальная проблема, не менее), я смиренно прошу вас воздерживаться достаточно долго, чтобы прочитать весь этот пост и фактически попробовать это решение для себя.

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

РАСШИРЕННЫЙ РЕКУРСИВНЫЙ РЕЖИМ РЕШЕНИЯ: Как правильно указывает Уэс Хардакер , DIV могут (и часто) вставляться. Однако он не на 100% прав, когда говорит: «Вы не можете построить тот, который будет соответствовать до правильного </ div>» . Правда, с PHP, вы можете! (с некоторыми ограничениями – см. ниже). Как и Perl и .NET, механизм регулярных выражений PCRE в PHP предоставляет рекурсивные выражения (например, (?R) , (?1) , (?2) и т. Д.), Которые позволяют сопоставлять вложенные структуры с любой произвольной глубиной (ограничено только памятью). Например, вы можете легко сопоставить сбалансированные вложенные круглые скобки с этим выражением: '/\((?:[^()]++|(?R))*+\)/' . Запустите этот простой тест, если у вас есть какие-либо сомнения:

 $text = 'zero(one(two)one(two(three)two)one)zero'; if (preg_match('/\((?:[^()]++|(?R))*+\)/', $text, $matches)) { print_r($matches); } 

Поэтому, если мы все можем согласиться с тем, что регулярное выражение PHP может действительно соответствовать вложенным структурам, давайте перейдем к проблеме. Эта конкретная проблема осложняется тем фактом, что внешний DIV должен иметь атрибут id="content" , но любые вложенные DIV s могут или не могут. Таким образом, мы не можем использовать конструкцию (?R) рекурсивно-матч-целое-выражение , потому что подвыражение, соответствующее внешнему DIV, не совпадает с тем, которое необходимо для соответствия внутренним DIV . В этом случае нам нужна группа захвата (в данном случае группа 2), которая будет служить «рекурсивной подпрограммой» , которая соответствует внутренним, вложенным DIV . Итак, вот проверенный фрагмент кода PHP, в котором есть продвинутый, но не полностью прокомментированный, так что вы могли бы на самом деле быть способным к созданию, (в большинстве случаев – см. ниже), DIV с id="content" , который сам может содержать вложенные DIV s:

 $re = '% # Match a DIV element having id="content". <div\b # Start of outer DIV start tag. [^>]*? # Lazily match up to id attrib. \bid\s*+=\s*+ # id attribute name and = ([\'"]?+) # $1: Optional quote delimiter. \bcontent\b # specific ID to be matched. (?(1)\1) # If open quote, match same closing quote [^>]*+> # remaining outer DIV start tag. ( # $2: DIV contents. (may be called recursively!) (?: # Non-capture group for DIV contents alternatives. # DIV contents option 1: All non-DIV, non-comment stuff... [^<]++ # One or more non-tag, non-comment characters. # DIV contents option 2: Start of a non-DIV tag... | < # Match a "<", but only if it (?! # is not the beginning of either /?div\b # a DIV start or end tag, | !-- # or an HTML comment. ) # Ok, that < was not a DIV or comment. # DIV contents Option 3: an HTML comment. | <!--.*?--> # A non-SGML compliant HTML comment. # DIV contents Option 4: a nested DIV element! | <div\b[^>]*+> # Inner DIV element start tag. (?2) # Recurse group 2 as a nested subroutine. </div\s*> # Inner DIV element end tag. )*+ # Zero or more of these contents alternatives. ) # End 2$: DIV contents. </div\s*> # Outer DIV end tag. %isx'; if (preg_match($re, $text, $matches)) { printf("Match found:\n%s\n", $matches[0]); } 

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

Должен ли я использовать это? Было бы целесообразным использовать это регулярное решение в производственной среде, где сотни или тысячи документов должны обрабатываться с 100% -ной надежностью и точностью? Конечно нет. Может ли это быть полезным для ограниченного однократного запуска некоторых HTML-файлов? (например, возможно, тот, кто задал этот вопрос?) Возможно. Это зависит от того, насколько комфортно с расширенными регулярными выражениями. Если регулярное выражение выглядит похоже на то, что оно написано на иностранном языке (оно есть) и / или пугает вас от дикков, ответ, вероятно, нет.

Оно работает? Да. Например, с учетом следующих тестовых данных, правильное выражение выше правильно выбирает DIV с id="content" (или id='content' или id=content на то пошло):

 <!DOCTYPE HTML SYSTEM> <html> <head><title>Test Page</title></head> <body> <div id="non-content-div"> <h1>PCRE does recursion!</h1> <div id='content'> <h2>First level matched</h2> <!-- this comment </div> is tricky --> <div id="one-deep"> <h3>Second level matched</h3> <div id=two-deep> <h4>Third level matched</h4> <div id=three-deep> <h4>Fourth level matched</h4> </div> <p>stuff</p> </div> <!-- this comment <div> is tricky --> <p>stuff</p> </div> <p>stuff</p> </div> <p>stuff</p> </div> <p>stuff</p> </body></html> 

CAVEATS: Итак, каковы некоторые сценарии, когда это решение не работает? Ну, стартовые теги DIV могут НЕ иметь никаких угловых скобок в любом из их атрибутов (это можно устранить, но это добавляет немного больше кода). И следующие промежутки CDATA , которые содержат конкретный начальный тег DIV мы ищем (крайне маловероятный), вызовет ошибку regex:

 <style type="text/css"> p:before { content: 'Unlikely CSS string with <div id=content> in it.'; } </style> <p title="Unlikely attribute with a <div id=content> in it">stuff</p> <script type="text/javascript"> alert("evil script with <div id=content> in it">"); </script> <!-- Comment with <div id="content"> in it --> <![CDATA[ a CDATA section with <div id="content"> in it ]]> 

Я бы очень хотел узнать о других.

GO READ MRE3 Как я уже говорил, действительно понять, что здесь происходит, требует довольно глубокого понимания нескольких передовых методов. Эти методы не очевидны или интуитивно понятны. Есть только один способ, которым я знаю, чтобы получить эти навыки, а именно – сесть и изучить: Освоение регулярных выражений (3-е издание) Джеффри Фридла (MRE3). (Ты не пожалеешь об этом!)

Я могу честно сказать, что это самая полезная книга, которую я прочитал за всю свою жизнь!

Ура!

EDIT 2013-04-30 Исправлено регулярное выражение. Ранее он не разрешал тег без DIV который сразу же начинался с начального тега DIV .

[^ / Div] + остановится, когда достигнет любого из этих символов, чего вы не хотите. Поскольку это остановится, когда оно достигнет слишком из-за i .

К сожалению, вы не можете делать то, что хотите, не зная внутренней структуры HTML. Учти это:

 <div id="content"> <div id="somethingelse"> </div> </div> 

Даже если вы можете создать регулярное выражение, которое будет соответствовать до </div> , вы не сможете создать тот, который будет соответствовать вплоть до правильного </div> . Вам нужно сделать гораздо более интенсивный синтаксический анализ.

Используйте синтаксический анализатор, а не регулярное выражение.

Вот пример PHP: http://htmlparsing.com/php.html

Эта статья удивительна и является идеальным решением для моих нужд!

Он даже работает на html-коде, где simpleXML или DOMDocument терпят неудачу!

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

Я просто добавляю несколько модификаций к вашему коду и использую его с функцией PHP preg_match_all.

В следующем примере мы попытаемся правильно сопоставить содержимое div # :

 $content = <<<HTML <div id="content"> <!-- tutu --> <div id="something"> <div id="somethingElse"> <ul> <li>lorem 1</li> <li class="dfg" toto="titi">lorem 2</li> <li class="dfg">lorem 3</li> <li class="dfg">lorem 4</li> <li class="dfg">lorem 5</li> <li class="dfg">lorem 6</li> </ul> <br /> <div id="emptyStuff"></div> </div> </div> <table> <tr> <td>cell 1</td> <td>cell 2</td> <td>cell 3</td> <td>cell 4</td> <td>cell 5</td> <td>cell 6</td> </tr> <tr> <td>cell 1</td> <td>cell 2</td> <td>cell 3</td> <td>cell 4</td> <td>cell 5</td> <td>cell 6</td> </tr> </table> </div> HTML; $pattern = '@# match nested tag (?(DEFINE) (?<comment> <!--.*?-->) (?<cdata> <![CDATA[.*?]]>) (?<empty> <\w+[^>]*?/>) (?<inline> <(script|style)[^>]+>.*?</\g{-1}>) (?<nested> <(\w+)[^>]*(?<!/)>(?&innerHTML)</\g{-1}>) (?<unclosed> <\w+[^>]*(?<!/)>) (?<text> [^<]+) ) (?<outerHTML><(?<tagName>div)\s?(?<attributes>[^>]*?id\h*=\h*(?<quote>"|\')[^(?&quote)\v>]*\bcontent\b[^(?&quote)\v>]*(?&quote)[^>]*)> # opening tag (?<innerHTML> (?: (?&comment) | (?&cdata) | (?&empty) | (?&inline) | (?&nested) | (?&unclosed) | (?&text) )* ) </(?&tagName)>) # closing tag @six'; preg_match_all($pattern, $content, $matches); var_dump(array_intersect_key($matches, array( 'tagName' => 1, 'attributes' => 1, 'innerHTML' => 1, 'outerHTML' => 1 ))); 

Вот результат :

 array(4) { ["outerHTML"]=> array(1) { [0]=> string(639) "<div id="content"> <!-- tutu --> <div id="something"> <div id="somethingElse"> <ul> <li>lorem 1</li> <li class="dfg" toto="titi">lorem 2</li> <li class="dfg">lorem 3</li> <li class="dfg">lorem 4</li> <li class="dfg">lorem 5</li> <li class="dfg">lorem 6</li> </ul> <br /> <div id="emptyStuff"></div> </div> </div> <table> <tr> <td>cell 1</td> <td>cell 2</td> <td>cell 3</td> <td>cell 4</td> <td>cell 5</td> <td>cell 6</td> </tr> <tr> <td>cell 1</td> <td>cell 2</td> <td>cell 3</td> <td>cell 4</td> <td>cell 5</td> <td>cell 6</td> </tr> </table> </div>" } ["tagName"]=> array(1) { [0]=> string(3) "div" } ["attributes"]=> array(1) { [0]=> string(12) "id="content"" } ["innerHTML"]=> array(1) { [0]=> string(615) " <!-- tutu --> <div id="something"> <div id="somethingElse"> <ul> <li>lorem 1</li> <li class="dfg" toto="titi">lorem 2</li> <li class="dfg">lorem 3</li> <li class="dfg">lorem 4</li> <li class="dfg">lorem 5</li> <li class="dfg">lorem 6</li> </ul> <br /> <div id="emptyStuff"></div> </div> </div> <table> <tr> <td>cell 1</td> <td>cell 2</td> <td>cell 3</td> <td>cell 4</td> <td>cell 5</td> <td>cell 6</td> </tr> <tr> <td>cell 1</td> <td>cell 2</td> <td>cell 3</td> <td>cell 4</td> <td>cell 5</td> <td>cell 6</td> </tr> </table> " } } 

Надеюсь, это поможет !

 <div id=content>.*?</div> 

это то, что вам нужно – до тех пор, пока у вас нет вложенных div. Если у вас их есть, сдайте и используйте фактический синтаксический анализатор XML.

Включите опцию «dotall» (проверьте http://www.regular-expressions.info/dot.html и узнайте, как это сделать с вашим ароматом regex).

Незначительные детали до вас. 🙂