Domdocument
The domdocument class of Php is a very handy one that can be used for a number of tasks like parsing xml, html and creating xml. It is documented here.
In this tutorial we are going to see how to use this class to parse html content. The need to parse html happens when are you are for example writing scrapers, or similar data extraction scripts.
Sample html
The following is the sample html file that we are going to use with DomDocument.
<html> <body> <div id="mango"> This is the mango div. It has some text and a form too. <form> <input type="text" name="first_name" value="Yahoo" /> <input type="text" name="last_name" value="Bingo" /> </form> <table class="inner"> <tr><td>Happy</td><td>Sky</td></tr> </table> </div> <table id="data" class="outer"> <tr><td>Happy</td><td>Sky</td></tr> <tr><td>Happy</td><td>Sky</td></tr> <tr><td>Happy</td><td>Sky</td></tr> <tr><td>Happy</td><td>Sky</td></tr> <tr><td>Happy</td><td>Sky</td></tr> </table> </body> </html>
1. Loading the html
So the first thing to do would be to construct a domdocument object and load the html content in it. Lets see how to do that.
// a new dom object $dom = new domDocument; // load the html into the object $dom->loadHTML($html); // discard white space $dom->preserveWhiteSpace = false;
Done. The $dom object has loaded the html content and can be used to extract contents from the whole html structure just like its done inside javascript. Most common functions are getElementsByTagName and getElementById.
Now that the html is loaded, its time to see how nodes and child elements can be accessed.
2. Get an element by its html id
This will get hold of a node/element by using its ID.
//get element by id $mango_div = $dom->getElementById('mango'); if(!mango_div) { die("Element not found"); } echo "element found";
Getting the value/html of a node
The "nodeValue" attribute of an node shall give its value but strip all html inside it. For example
echo $mango_div->nodeValue;
The second method is to use the saveHTML function, that gets out the exact html inside that particular node.
echo $dom->saveHTML($mango_div);
Note that the function saveHTML is called on the dom object and the node object is passed as a parameter. The saveHTML function will provide the whole html (outer html) of the node including the node's own html tags as well.
Another function called C14N does the same thing more quickly
//echo the contents of mango_div element echo $mango_div->C14N();
inner html
To get just the inner html take the following approach. It adds up the html of all of the child nodes.
$tables = $dom->getElementsByTagName('table'); echo get_inner_html($tables->item(0)); function get_inner_html( $node ) { $innerHTML= ''; $children = $node->childNodes; foreach ($children as $child) { $innerHTML .= $child->ownerDocument->saveXML( $child ); } return $innerHTML; }
The function get_inner_html gets the inner html of the html element. Note that we used the saveXML function instead of the saveHTML function. The property "childNodes" provides the child nodes of an element. These are the direct children.
3. Getting elements by tagname
This will get elements by tag name.
$tables = $dom->getElementsByTagName('table'); foreach($tables as $table) { echo $dom->saveHTML($table); }
The function getElementsByTagName returns an object of type DomNodeList that can be read as an array of objects of type DomNode. Another way to fetch the nodes of the NodeList is by using the item function.
$tables = $dom->getElementsByTagName('table'); echo "Found : ".$tables->length. " items"; $i = 0; while($table = $tables->item($i++)) { echo $dom->saveHTML($table); }
The item function takes the index of the item to be fetched. The length attribute of the DomNodeList gives the number of objects found.
4. Get the attributes of an element
Every DomNode has an attribute called "attributes" that is a collection of all the html attributes of that node.
Here is a quick example
$tables = $dom->getElementsByTagName('table'); $i = 0; while($table = $tables->item($i++)) { foreach($table->attributes as $attr) { echo $attr->name . " " . $attr->value . "<br />"; } }
To get a particular attribute using its name, use the "getNamedItem" function on the attributes object.
$tables = $dom->getElementsByTagName('table'); $i = 0; while($table = $tables->item($i++)) { $class_node = $table->attributes->getNamedItem('class'); if($class_node) { echo "Class is : " . $table->attributes->getNamedItem('class')->value . PHP_EOL; } }
5. Children of a node
A DomNode has the following properties that provide access to its children
1. childNodes
2. firstChild
3. lastChild
$tables = $dom->getElementsByTagName('table'); $table = $tables->item(1); //get the number of rows in the 2nd table echo $table->childNodes->length; //content of each child foreach($table->childNodes as $child) { echo $child->ownerDocument->saveHTML($child); }
Checking if child nodes exist
The hasChildNodes function can be used to check if a node has any children at all.
Quick example
if( $table->hasChildNodes() ) { //print content of children foreach($table->childNodes as $child) { echo $child->ownerDocument->saveHTML($child); } }
6. Comparing 2 elements for equality
It might be needed to check if the element in 1 variable is the same as the element in another variable. The function "isSameNode" is used for this. The function is called on one node, and the other node is passed as the parameter. If the nodes are same, then boolean true is returned.
$tables = $dom->getElementsByTagName('table'); $table = $tables->item(1); $table2 = $dom->getElementById('data'); var_dump($table->isSameNode($table2));
The var_dump would show true , indicating that the tables in both $table and $table2 are the same.
Conclusion
The above examples showed how Domdocument can be used to access elements in an html document in an object oriented manner. Domdocument can not only parse html but also create/modify html and xml. In later articles we shall see how to do that.
Thank you for the post
Here is a working php script that adds some enhancements to this useful article:
<?php
$html = file_get_contents("sample html document.html");
echo "0 this is the input HTML:”;
echo “”;
echo htmlentities($html);
echo “”;
// a new dom object
$dom = new domDocument;
// load the html into the object
$dom->loadHTML($html);
// discard white space
removeEmptyTextNodes($dom);
// preserveWhiteSpace does not help us much
$dom->preserveWhiteSpace = false;
//get element by id
$mango_div = $dom->getElementById(‘mango’);
if(!$mango_div)
{
die(“Element not found”);
}
echo “1 ‘mango’ element found”;
echo “2 the node value of the ‘mango’ element:”;
echo $mango_div->nodeValue;
echo “3 saveHTML on the mango div:”;
echo $dom->saveHTML($mango_div);
echo “4 Another way to do this using canonicalization (C14N) of the node itself:”;
//echo the contents of mango_div element
echo $mango_div->C14N();
echo “5 retrieving the inner HTML of the ‘.inner’ table:”;
$tables = $dom->getElementsByTagName(‘table’);
echo get_inner_html($tables->item(0));
echo “6 get elements by tag name ‘table’:”;
$tables = $dom->getElementsByTagName(‘table’);
foreach($tables as $table)
{
echo $dom->saveHTML($table);
}
echo “7 alternative way to use getElementsByTagName, using the item() method:”;
$tables = $dom->getElementsByTagName(‘table’);
echo “Found : “.$tables->length. ” items”;
$i = 0;
while($table = $tables->item($i++))
{
echo $dom->saveHTML($table);
}
echo “8 Finding the attributes of an element:”;
$tables = $dom->getElementsByTagName(‘table’);
$i = 0;
while($table = $tables->item($i++))
{
foreach($table->attributes as $attr)
{
echo $attr->name . ” ” . $attr->value . “”;
}
}
echo “9 Finding named attributes (using class as example):”;
$tables = $dom->getElementsByTagName(‘table’);
$i = 0;
while($table = $tables->item($i++))
{
$class_node = $table->attributes->getNamedItem(‘class’);
if($class_node)
{
echo “Class is : ” . $table->attributes->getNamedItem(‘class’)->value . PHP_EOL;
}
}
echo “10 Looping over child nodes:”;
$tables = $dom->getElementsByTagName(‘table’);
$table = $tables->item(1);
//get the number of rows in the 2nd table
// when empty text nodes are not removed, there will be 11 child nodes.
// when empty text nodes are removed there are 5 nodes
echo $table->childNodes->length;
//content of each child
foreach($table->childNodes as $child)
{
echo $child->ownerDocument->saveHTML($child);
}
echo “11 Check for existence of child nodes:”;
if( $table->hasChildNodes() )
{
//print content of children
foreach($table->childNodes as $child)
{
echo $child->ownerDocument->saveHTML($child);
}
}
echo “12 Compare two nodes for equality:”;
$tables = $dom->getElementsByTagName(‘table’);
$table = $tables->item(1);
$table2 = $dom->getElementById(‘data’);
var_dump($table->isSameNode($table2));
exit(0);
function get_inner_html( $node )
{
$innerHTML= ”;
$children = $node->childNodes;
foreach ($children as $child)
{
$innerHTML .= $child->ownerDocument->saveXML( $child );
}
return $innerHTML;
}
// recursive white space remover: DOMText nodes containing only white space
// this is taken from the loadHTML documentation page on php.net (April 2022)
function removeEmptyTextNodes(DOMNode $node) {
if ($node->hasChildNodes()) {
// depth-first, right-to-left
for ($i = $node->childNodes->length – 1; $i >= 0; –$i) {
removeEmptyTextNodes($node->childNodes->item($i));
}
}
if ($node->nodeType === XML_TEXT_NODE && // this is a text node
!$node->hasChildNodes() && // this is a leaf node
!$node->hasAttributes() && // it has no attributes
empty(trim($node->textContent))) { // and there is only white space in there
$node->parentNode->removeChild($node);
}
}
?>
Great article! I would be very interested to read about create/modify html nodes.
Hi, I have a question. As I’m not sure the HTML standards are always respected when I parse a page, I’d like to retrieve a tag with a specific id and classes.
Something like:
Hello there
in other words : span#myId.class1.class2
is there a way to perform this with DOMElement ?
thanks
Thanks, great tutorial. It helped me.
The variable $html in the examples above was not defined! Or was it? Where or how? Please
Thanks. Great Tutorial. But can you please add the outputs of the code as well.
Many thanks, got i finally working because of your example
This is an old article, but I’m just getting started… In any case I think you are missing a “$” in front of the variable in your code above if(!mango_div).. Atleast I did. Thanks
Thanks Silver, your get_inner_html function has saved me a lot of time. Really great article.
$mango_div = $dom->getElementById(‘mango’);
above text not working in windows 7. it occurring this error
Catchable fatal error: Object of class DOMElement could not be converted to string in /var/www/websites/shabbonacreekrv/shabbonacreekrv.com/www/html/vehicle.php on line 65
to test it…
Great article! I would be very interested to read about create/modify html nodes.
Hello I am getting Following error :
Warning: domdocument::domdocument() expects parameter 2….
Fatal error: Call to undefined method domdocument::loadHTML()
I am using php 5.2.x + WAMP
php_domxml extension is enebled.
What is wrong in my code ?? $html has html content.. i did not paste it here. its the same as your example.
$dom = new DOMDocument(‘1.0′,’utf-8’);
$dom->loadHTML($html);
$dom->preserveWhiteSpace = false;
$tables = $dom->getElementsByTagName(‘table’);
print_r($tables);
Thanks, great tutorial!
Thanks for the tut. Would be helpful if you included the code (as you have) + the return values of your echo statements!
thanks for the feedback, shall update the post soon.
Thanks, this helped me a lot (was just looking for a nice DOCDocument example)!