2011-08-28 13 views
5

i używa DOM doc załadować html z bazy danych tak:PHP Dom Dokumenty: coraz textContent ignorując tagów skryptu i komentarze

$doc = new DOMDocument(); 
@$doc->loadHTML($data); 
$doc->encoding = 'utf-8'; 
$doc->saveHTML(); 

potem uzyskać tekst ciała, wykonując te:

$bodyNodes = $doc->getElementsByTagName("body"); 
$words = htmlspecialchars($bodyNodes->item(0)->textContent); 

Słowa, które dostałem, obejmują wszystko w <body>. Rzeczy takie jak <scripts> również zostały uwzględnione. Jak mogę je usunąć i zachować tylko prawdziwą zawartość tekstową?

+0

znaczy rekurencyjną zawartość tekstową ekstrakt z każdego elementu w '' ? –

+0

tylko treść tekstowa, która ma znaczenie, z wyłączeniem javascripts lub innych komentarzy html lub itp., Które nie są przydatnymi danymi. – nuttynibbles

Odpowiedz

5

Musisz odwiedzić wszystkie węzły i zwrócić tekst. Jeśli niektóre zawierają inny węzeł, odwiedź je też.

Można to zrobić z tego podstawowego algorytmu rekurencyjnego:

extractNode: 
    if node is a text node or a cdata node, return its text 
    if is an element node or a document node or a document fragment node: 
     if it’s a script node, return an empty string 
     return a concatenation of the result of calling extractNode on all the child nodes 
    for everything else return nothing 

Realizacja:

function extractText($node) {  
    if (XML_TEXT_NODE === $node->nodeType || XML_CDATA_SECTION_NODE === $node->nodeType) { 
     return $node->nodeValue; 
    } else if (XML_ELEMENT_NODE === $node->nodeType || XML_DOCUMENT_NODE === $node->nodeType || XML_DOCUMENT_FRAG_NODE === $node->nodeType) { 
     if ('script' === $node->nodeName) return ''; 

     $text = ''; 
     foreach($node->childNodes as $childNode) { 
      $text .= extractText($childNode); 
     } 
     return $text; 
    } 
} 

ta zwróci textContent danego węzła $, ignorując tagów skryptu i komentarze.

$words = htmlspecialchars(extractText($bodyNodes->item(0))); 

Wypróbuj go tutaj: http://codepad.org/CS3nMp7U

+0

to działa. jeśli nie masz nic przeciwko, czy możesz wyjaśnić kod? – nuttynibbles

+0

Dodałem krótki opis algorytmu. – arnaud576875

5

Można użyć XPath do tego.

Pożyczanie HTML Arnaud wykorzystywane do jego przykładu powyżej:

$html = <<< HTML 
<p> 
    test<span>foo<b>bar</b> 
</p> 
<script> 
    ignored 
</script> 
<!-- comment is ignored --> 
<p>test</p> 
HTML; 

po prostu query wszystko text nodes, że nie są not children of a script tag i do not evaluate to an empty string. Upewnisz się również, że nie musisz używać, aby używać białych znaków do formatowania.

$dom = new DOMDocument; 
$dom->preserveWhiteSpace = false; 
$dom->loadHtml($html); 

$xp = new DOMXPath($dom); 
$nodes = $xp->query('/html/body//text()[ 
    not(ancestor::script) and 
    not(normalize-space(.) = "") 
]'); 

foreach($nodes as $node) { 
    var_dump($node->textContent); 
} 

wyjście wola (demo)

string(10) " 
    test" 
string(3) "foo" 
string(3) "bar" 
string(4) "test" 
+0

Ta funkcja pomaga. faktycznie jest w stanie rozpoznać i podzielić zdanie słów jako jeden ciąg. – nuttynibbles

+0

@nuttynibbles nie rozpoznaje słów, ani zdań. XPath jest językiem zapytań dla XML. Nie ma pojęcia o zawartości dokumentu XML, tylko o strukturze. Zobacz [moja odpowiedź tutaj na wprowadzenie do koncepcji DOM] (http://stackoverflow.com/questions/4979836/noob-question-about-domdocument-in-php/4983721#4983721) – Gordon