PHP DOMDocument получает текст между двумя тегами SETS

Есть ли способ использовать Xpath для разбора текста между двумя тегами SETS ? Например, см. Пример:

<div class="par"> <p class="pp"> <span class="dv">1 </span>Blah blah blah blah. <span class="dv">2 </span> Yada yada yada yada. <span class="dv">3 </span>Foo foo foo foo. </p> </div> <div class="par"> <p class="pp"> <span class="dv">4 </span>Hmm hmm hmm hmm. </p> </div> 

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

 array[0] = "Blah blah blah blah."; array[1] = "Yada yada yada yada."; array[2] = "Foo foo foo foo."; array[3] = "Hmm hmm hmm hmm."; 

Могу ли я использовать DOMDocument для этого? Если нет, то каков наилучший способ добиться этого? Обратите внимание, что в середине предложений могут быть или теги. Такие как:

 ...<span class="dv">5 </span>Uhh uhh <a href="www.uhh.com">uhh</a> uhh. <span class="dv">6 </span>... 

ОБНОВИТЬ

Кажется, вам нужен плоский список, поэтому я добавляю этот конкретный пример, чтобы не было путаницы:

 $html = '<div class="par"> <p class="pp"> <span class="dv">1 </span>Blah blah blah blah. <span class="dv">2 </span> Yada yada yada yada. <span class="dv">3 </span>Foo foo foo foo. </p> </div> <div class="par"> <p class="pp"> <span class="dv">4 </span>Hmm hmm hmm hmm. </p> </div>'; $dom = DOMDocument::loadHTML($html); $finder = new DOMXPath($dom); // select THE TEXT NODES of all p elements with the class pp // - note that means its explictly class="pp", // not that "pp" is anywhere in the class list you may need to change this up depending... // post additional questions for specific xpath help $found = $finder->query('//p[@class="pp"]/text()'); $nodes = array(); // simply transform the resulting DOMNodeList into an array // for easier consumption/manipulation foreach($found as $textNode) { $node[] = $textNode->nodeValue; } print_r($nodes); 

Производит:

 Array ( [0] => [1] => Blah blah blah blah. [2] => Yada yada yada yada. [3] => Foo foo foo foo. [4] => [5] => Hmm hmm hmm hmm. ) 

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

 $html = '<div class="par"> <p class="pp"> <span class="dv">1 </span>Blah blah blah blah. <span class="dv">2 </span> Yada yada yada yada. <span class="dv">3 </span>Foo foo foo foo. </p> </div> <div class="par"> <p class="pp"> <span class="dv">4 </span>Hmm hmm hmm hmm. </p> </div>'; $dom = DOMDocument::loadHTML($html); $finder = new DOMXPath($dom); // select all p elements with the class pp - note that means its explictly class="pp", // not that "pp" is anywhere in the class list you may need to change this up depending... // post additional questions for specific xpath help $found = $finder->query('//p[@class="pp"]'); $nodes = array(); foreach($found as $p) { // for each p element, pull its text nodes. $textNodes = $finder->query('text()', $p); $textStr = ''; // loop over the textNodes and concat them into a single string foreach ($textNodes as $n) { $textStr .= $n->nodeValue; } // push the compiled string onto the array $nodes[] = $textStr; } print_r($nodes); 

Это даст результат, например:

 Array ( [0] => Blah blah blah blah. Yada yada yada yada. Foo foo foo foo. [1] => Hmm hmm hmm hmm. ) 

Если вам действительно нужен каждый текстовый узел отдельно, вам просто нужно изменить цикл:

 foreach($found as $p) { // for each p element, pull its text nodes. $textNodes = $finder->query('text()', $p); $textArr = array(); // loop over the textNodes and concat them into a single string foreach ($textNodes as $n) { $textArr[] = $n->nodeValue; } // push the compiled string onto the array $nodes[] = $textArr; } 

Что даст вам:

 Array ( [0] => Array ( [0] => [1] => Blah blah blah blah. [2] => Yada yada yada yada. [3] => Foo foo foo foo. ) [1] => Array ( [0] => [1] => Hmm hmm hmm hmm. ) ) 

Очевидно, что, поскольку вы можете видеть, что он схватил разрывы строк, вы можете легко отфильтровать их с помощью выбранного метода фильтрации массива, если они нежелательны. Или вы можете посмотреть в настройках XPath и DOMDocument, чтобы настроить это, IIRC есть некоторые настройки, касающиеся интерпретации пробелов (или нет), которые, вероятно, позволят вам избежать этого, но это может иметь и другие последствия, если вы выполняете другую обработку на тот же экземпляр DOMDocument .

Вы хотите, чтобы первый текстовый узел был непосредственно следующим братом после элемента span:

 //span/following-sibling::text()[1] 

Это 1: 1 в синтаксисе PHP:

 $doc = new DOMDocument(); $doc->loadHTML($buffer, LIBXML_HTML_NOIMPLIED); $xpath = new DOMXPath($doc); $expr = '//span/following-sibling::text()[1]'; $result = $xpath->evaluate($expr); 

Затем вы хотите, чтобы результирующие текстовые узлы превратились в массив строк. Я бы сказал, когда вы сделаете так, что работаете, выполните некоторую нормализацию белого пространства на нем:

 $array = array_map(function(DOMText $text) { return preg_replace(['~\s+~u', '~^ | $~'], [' ', ''], $text->nodeValue); }, iterator_to_array($result)); 

В результате получается следующее:

 [ "Blah blah blah blah.", "Yada yada yada yada.", "Foo foo foo foo.", "Hmm hmm hmm hmm." ] 

Полный пример кода:

 <?php /** * http://stackoverflow.com/questions/27674012/php-domdocument-get-text-between-two-sets-of-tags */ $buffer = <<<HTML <div class="par"> <p class="pp"> <span class="dv">1 </span>Blah blah blah blah. <span class="dv">2 </span> Yada yada yada yada. <span class="dv">3 </span>Foo foo foo foo. </p> </div> <div class="par"> <p class="pp"> <span class="dv">4 </span>Hmm hmm hmm hmm. </p> </div> HTML; $doc = new DOMDocument(); $doc->loadHTML($buffer, LIBXML_HTML_NOIMPLIED); $xpath = new DOMXPath($doc); $expr = '//span/following-sibling::text()[1]'; $result = $xpath->evaluate($expr); $array = array_map(function(DOMText $text) { return preg_replace(['~\s+~u', '~^ | $~'], [' ', ''], $text->nodeValue); }, iterator_to_array($result)); echo json_encode($array, JSON_PRETTY_PRINT);