Here is a simple web scraping example using the PHP DOM that tries to get the largest text body of a HTML document. I needed it for a spider that had to show a short description for a page. It assumes that document annotation can be the largest <div>, <td> or <p> element in the page.
In the example I show a way to prevent a bug in the DOM as it sometimes just doesn't recognize html encoding. It seems to work if you put charset meta tag right after the head tag of the document.
<?php
$ch= curl_init();
curl_setopt ($ch, CURLOPT_URL, '...put url here...' );
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch,CURLOPT_VERBOSE,1);
curl_setopt($ch, CURLOPT_USERAGENT, 'set sth...');
curl_setopt ($ch, CURLOPT_REFERER, '...set sth...'); //just a fake referer
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch,CURLOPT_POST,0);
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 20);
$html= curl_exec($ch);
$html1= curl_getinfo($ch);
//try to get page encoding as it was sent from server
if ($html1['content_type']){
$arr= explode('charset=',$html1['content_type']);
$csethdr= strtolower(trim($arr[1]));
} else {
$csethdr= false;
}
$cset= false;
$arr= array();
//This has to replace page meta tags for charset with utf-8, but it doesn't actually help(see the bug info).
if (preg_match_all(
'/(<meta\s*http-equiv="Content-Type"\s*content="[^;]*;
\s*charset=([^"]*?)(?:"|\;)[^>]*>)/' //merge this line
,$html,$arr,PREG_PATTERN_ORDER)){
$cset= strtolower(trim($arr[2][0]));
if ($cset!='utf-8'||$cset!=$csethdr){
$new= str_replace($arr[2][0],'utf-8',$arr[1][0]);
$html= str_replace($arr[1][0],$new,$html);
$cset= $csethdr;
} else {
$cset= false;
}
if ($cset=='utf-8'){
$cset= false;
}
}
unset($arr);
if ($cset){
$html= iconv($cset,'utf-8',$html);
}
unset($cset);
//solve dom bug
$html=preg_replace('/<head[^>]*>/','<head>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
',$html);
$dom= new DOMDocument();
$dom->loadHTML($html);
$dom->preserveWhiteSpace = false;
function getMaxTextBody($dom){
$content = $dom->getElementsByTagname('div');
$content2= $dom->getElementsByTagname('td');
$content3= $dom->getElementsByTagname('p');
$new= array();
foreach ($content as $value) {
$new[]= $value;
unset($value);
}
unset($content);
foreach ($content2 as $value) {
$new[]= $value;
unset($value);
}
unset($content2);
foreach ($content3 as $value) {
$new[]= $value;
unset($value);
}
unset($content3);
$maxlen= 0;
$result= '';
foreach ($new as $item)
{
$str= $item->nodeValue;
if (strlen($str)>$maxlen){
$content1= $item->getElementsByTagName('div');
$content2= $item->getElementsByTagname('td');
$content3= $item->getElementsByTagname('p');
$contentnew= array();
foreach ($content1 as $value) {
$contentnew[]= $value;
unset($value);
}
unset($content1);
foreach ($content2 as $value) {
$contentnew[]= $value;
unset($value);
}
unset($content2);
foreach ($content3 as $value) {
$contentnew[]= $value;
unset($value);
}
unset($content3);
if (count($contentnew)==0){
$result= $str;
} else {
foreach ($contentnew as $value) {
$str1= getMaxTextBody($value);
$str2= $value->nodeValue;
//let's say largest body has more than 50% of the text in its parent
if (strlen($str1)*2<strlen($str2)){
$str1= $str2;
}
if (strlen($str1)*2>strlen($str)&&strlen($str1)>$maxlen){
$result= $str1;
} elseif (strlen($str1)>$maxlen){
$result= $str1;
}
$maxlen= strlen($result);
}
}
$maxlen= strlen($result);
unset($contnentnew);
}
}
unset($new);
return $result;
}
print getMaxTextBody($dom);
?>
La classe DOMDocument
Introduction
Représente un document HTML ou XML entier ; ce sera la racine de l'arbre document.
Synopsis de la classe
Propriétés
- actualEncoding
-
Obsolète. L'encodage actuel du document, en lecture seule, équivalent àencoding.
- config
-
Obsolète. Configuration utilisée lorsque DOMDocument::normalizeDocument() est appelé.
- doctype
-
Le doctype associé au document.
- documentElement
-
C'est un attribut de convenence, qui permet un accès direct au noeud fils, qui est l'élément document du document.
- documentURI
-
La localisation du document, ou NULL si indéfini.
- encoding
-
L'encodage du document, tel que spécifié par la déclaration XML. Cet attribut n'est pas présent dans la spécification DOM Level 3 finale, mais représente la seule façon de manipuler l'encodage du document XML dans cette implémentation.
- formatOutput
-
Formate la sortie avec une jolie indentation et des espaces supplémentaires.
- implementation
-
L'objet DOMImplementation qui gère ce document.
- preserveWhiteSpace
-
Ne pas supprimer les espaces redondants. Vaut par défaut TRUE.
- recover
-
Propriétaire. Active le mode "recovery", i.e. tente d'analyser un document mal formé. Cet attribut ne fait pas parti de la spécification DOM et est spécifique à libxml.
- resolveExternals
-
Définissez-le à TRUE pour charger des entités externes depuis la déclaration doctype. C'est utile pour inclure des entités dans vos documents XML.
- standalone
-
Obsolète. Si le document est "standalone", ou non, tel que spécifié par la déclaration XML, correspondant à xmlStandalone.
- strictErrorChecking
-
Lance une DOMException en cas d'erreur. Par défaut, vaut TRUE.
- substituteEntities
-
Propriétaire. Si l'on doit ou non substituer les entités. Cet attribut ne fait pas parti de la spécification DOM et est spécifique à libxml.
- validateOnParse
-
Charge et valide la DTD. Par défaut, vaut FALSE.
- version
-
Obsolète. Version du XML, correspond à xmlVersion
- xmlEncoding
-
Un attribut spécifiant l'encodage du document. Il vaut NULL lorsque l'encodage n'est pas spécifié, ou lorsqu'il est inconnu, comme c'est le cas lorsque le document a été créé en mémoire.
- xmlStandalone
-
Un attribut spécifiant si le document est "standalone". Il vaut FALSE lorsque non spécifié.
- xmlVersion
-
Un attribut spécifiant le numéro de version du document. S'il n'y a pas de déclaration et si le document supporte la fonctionnalité "XML", la valeur sera "1.0".
Sommaire
- DOMDocument::__construct — Crée un nouvel objet DOMDocument
- DOMDocument::createAttribute — Crée un nouvel attribut
- DOMDocument::createAttributeNS — Crée un nouvel attribut avec un espace de noms associé
- DOMDocument::createCDATASection — Crée un nouveau noeud cdata
- DOMDocument::createComment — Crée un nouveau noeud de commentaire
- DOMDocument::createDocumentFragment — Crée un nouveau fragment de document
- DOMDocument::createElement — Crée un nouveau noeud
- DOMDocument::createElementNS — Crée un nouveau noeud avec un espace de noms associé
- DOMDocument::createEntityReference — Crée un nouveau noeud de référence d'entité
- DOMDocument::createProcessingInstruction — Crée un nouveau noeud PI
- DOMDocument::createTextNode — Crée un nouveau noeud de texte
- DOMDocument::getElementById — Cherche un élément avec un certain identifiant
- DOMDocument::getElementsByTagName — Cherche tous les éléments qui ont le nom de balise donné
- DOMDocument::getElementsByTagNameNS — Recherche tous les éléments avec un nom de balise donné dans un espace de noms spécifié
- DOMDocument::importNode — Importe un noeud dans le document courant
- DOMDocument::load — Charge du XML depuis un fichier
- DOMDocument::loadHTML — Charge du HTML à partir d'une chaîne de caractères
- DOMDocument::loadHTMLFile — Charge du HTML à partir d'un fichier
- DOMDocument::loadXML — Charge du XML depuis une chaîne de caractères
- DOMDocument::normalizeDocument — Normalise le document
- DOMDocument::registerNodeClass — Enregistre la classe étendue utilisée pour créer un type de base de noeud
- DOMDocument::relaxNGValidate — Effectue une validation relaxNG sur le document
- DOMDocument::relaxNGValidateSource — Effectue une validation relaxNG sur le document
- DOMDocument::save — Sauvegarde l'arbre interne XML dans un fichier
- DOMDocument::saveHTML — Sauvegarde le document interne dans une chaîne en utilisant un formatage HTML
- DOMDocument::saveHTMLFile — Sauvegarde un document interne dans un fichier en utilisant un formatage HTML
- DOMDocument::saveXML — Sauvegarde l'arbre interne XML dans une chaîne de caractères
- DOMDocument::schemaValidate — Valide un document selon un schéma
- DOMDocument::schemaValidateSource — Valide un document selon un schéma
- DOMDocument::validate — Valide un document en se basant sur sa DTD
- DOMDocument::xinclude — Remplace les XIncludes dans un objet DOMDocument
DOMDocument
30-Nov-2008 03:59
15-May-2008 09:58
To indent a XML in a pretty way I use:
<?
$sXML = '<root><element><key>a</key><value>b</value></element></root>';
$doc = new DOMDocument();
$doc->preserveWhiteSpace = false;
$doc->formatOutput = true;
$doc->loadXML($sXML);
echo $doc->saveXML();
?>
11-Apr-2008 03:48
Showing a quick example of how to use this class, just so that new users can get a quick start without having to figure it all out by themself. ( At the day of posting, this documentation just got added and is lacking examples. )
<?php
// Set the content type to be XML, so that the browser will recognise it as XML.
header( "content-type: application/xml; charset=ISO-8859-15" );
// "Create" the document.
$xml = new DOMDocument( "1.0", "ISO-8859-15" );
// Create some elements.
$xml_album = $xml->createElement( "Album" );
$xml_track = $xml->createElement( "Track", "The ninth symphony" );
// Set the attributes.
$xml_track->setAttribute( "length", "0:01:15" );
$xml_track->setAttribute( "bitrate", "64kb/s" );
$xml_track->setAttribute( "channels", "2" );
// Create another element, just to show you can add any (realistic to computer) number of sublevels.
$xml_note = $xml->createElement( "Note", "The last symphony composed by Ludwig van Beethoven." );
// Append the whole bunch.
$xml_track->appendChild( $xml_note );
$xml_album->appendChild( $xml_track );
// Repeat the above with some different values..
$xml_track = $xml->createElement( "Track", "Highway Blues" );
$xml_track->setAttribute( "length", "0:01:33" );
$xml_track->setAttribute( "bitrate", "64kb/s" );
$xml_track->setAttribute( "channels", "2" );
$xml_album->appendChild( $xml_track );
$xml->appendChild( $xml_album );
// Parse the XML.
print $xml->saveXML();
?>
Output:
<Album>
<Track length="0:01:15" bitrate="64kb/s" channels="2">
The ninth symphony
<Note>
The last symphony composed by Ludwig van Beethoven.
</Note>
</Track>
<Track length="0:01:33" bitrate="64kb/s" channels="2">Highway Blues</Track>
</Album>
If you want your PHP->DOM code to run under the .xml extension, you should set your webserver up to run the .xml extension with PHP ( Refer to the installation/configuration configuration for PHP on how to do this ).
Note that this:
<?php
$xml = new DOMDocument( "1.0", "ISO-8859-15" );
$xml_album = $xml->createElement( "Album" );
$xml_track = $xml->createElement( "Track" );
$xml_album->appendChild( $xml_track );
$xml->appendChild( $xml_album );
?>
is NOT the same as this:
<?php
// Will NOT work.
$xml = new DOMDocument( "1.0", "ISO-8859-15" );
$xml_album = new DOMElement( "Album" );
$xml_track = new DOMElement( "Track" );
$xml_album->appendChild( $xml_track );
$xml->appendChild( $xml_album );
?>
although this will work:
<?php
$xml = new DOMDocument( "1.0", "ISO-8859-15" );
$xml_album = new DOMElement( "Album" );
$xml->appendChild( $xml_album );
?>
