domnodelist - Extraer datos de HTML usando PHP y xPath
php dom node (2)
Intento extraer datos de una página web para insertarlos en una base de datos. Los datos que me interesan están en los div''s que tienen una clase = "compañía". En una página web hay 15 o menos div''s así, y hay muchas páginas de las que intento extraer estos datos. Por esta razón, estoy tratando de encontrar una solución automática para la extracción de datos.
El div con una clase = "compañía" es el siguiente (hay 15 o menos divs como este en una página con datos diferentes):
<div class="company" id="company-6666"> <!-- EXTRACT ''company-6666'' from id="company-6666" -->
<div class="top clearfix">
<div class="name clearfix">
<h2>
<a href="/company-name">Company Name</a> <!-- EXTRACT ''Company Name'' from contents of A element and EXTRACT ''/company-name'' from href attribute -->
<a href="/branches-list-link?parent_id=6666" class="branches">Branches <span>(5)</span></a> <!-- EXTRACT ''/branches-list-link?parent_id=6666'' from href attribute -->
</h2>
</div>
</div>
<div class="inner clearfix has-logo">
<div class="clearfix">
<div class="logo">
<a href="/company-name">
<img src="/graphics/company/logo/listing/123456.jpg?_ts=1365390237" border="0" alt="" /> <!-- EXTRACT ''/graphics/company/logo/listing/123456.jpg?_ts=1365390237'' from src attribute -->
</a>
</div>
<div class="info">
<div class="address">StreetName 500, 7777 City, County</div> <!-- EXTRACT ''StreetName 500, 7777 City, County'' from contents of class="address" div -->
<div class="clearfix">
<div class="slogan">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac condimentum mi.</div> <!-- EXTRACT ''Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi ac condimentum mi.'' from contents of class="slogan" div -->
</div>
</div>
</div>
<div class="actions-bar clearfix">
<ul>
<li><span class="phone-number">6666666</span></li> <!-- EXTRACT ''6666666'' from contents of class="phone-number" div -->
<li><a href="mailto:[email protected]" target="_blank" title="[email protected]" class="email">[email protected]</a></li> <!-- EXTRACT ''[email protected]'' from contents of class="email" div -->
<li><a href="http://www.webpage.com" target="_blank" title="www.webpage.com" class="redirect url">www.webpage.com</a></li> <!-- EXTRACT ''www.webpage.com'' from contents of class="redirect url" div -->
</ul>
</div>
</div>
</div>
Hasta ahora tengo el siguiente código PHP (el $ output tiene el código HTML de la página web):
<?php
$doc = new DomDocument();
@$doc->loadHTML($output);
$doc->preserveWhiteSpace = false;
$xpath = new DomXPath($doc);
$elements = $xpath->query("//*[@class=''company'']");
if (!is_null($elements)) {
foreach ($elements as $element) {
echo $element->nodeValue;
}
}
?>
Parece que obtiene todos los 15 div con clase = "compañía", pero no tengo idea de cómo extraer los valores individuales mencionados anteriormente (en comentarios del código HTML).
Cada div (estoy hablando del div con class = "company") no tiene todos los valores escritos en el bloque HTML. Así que de alguna manera tengo que hacer una consulta si un div específico dentro de la empresa div, donde están los datos que me interesan, existe y, si existe, debo verificar si no está vacío (contiene texto entre etiquetas o no). Si existe y no está vacío, lo agrego a una variable.
Una vez que se extraen los valores, me gustaría asignarlos a las variables de PHP que luego me permiten trabajar con ellos. Sería aún mejor si los valores extraídos se ponen en una matriz así:
$result = array(
// 1''st div''s data
[0] =>
''company name'' => ''company name'',
''company link'' => ''company link'',
''company id'' => ''company id'',
''company branches'' => ''branches link'',
''company logo'' => ''logo'',
''company address'' => ''address'',
''company slogan'' => ''slogan'',
''company webpage'' => ''webpage'',
''company email'' => ''email'',
''company phone'' => ''phone''
// 2''nd div''s data
[1] =>
''company name'' => ''company name'',
''company link'' => ''company link'',
''company id'' => ''company id'',
''company branches'' => ''branches link'',
''company logo'' => ''logo'',
''company address'' => ''address'',
''company slogan'' => ''slogan'',
''company webpage'' => ''webpage'',
''company email'' => ''email'',
''company phone'' => ''phone''
...
)
Cada compañía puede ser representada por un nodo contextual al tener cada propiedad representada por una expresión xpath relativa a ella:
Company company-6666:
->id ....... = "company-6666" -- string(@id)
->name ..... = "Company Name" -- .//a[1]/text()
->href ..... = "/company-name" -- .//a[1]/@href
->img ...... = "/graphics/company/logo/listing/123456.jpg?_ts=1365390237" -- .//img[1]/@src
->address .. = "StreetName 500, 7777 City, County" -- .//*[@class="address"]/text()
...
Si envuelve eso en objetos, esto es bastante ingenioso para usar:
$doc = new DOMDocument();
$doc->loadHTML($html);
/* @var $companies DOMValueObject[] */
$companies = new Companies($doc);
foreach ($companies as $company) {
printf("Company %s:/n", $company->id);
foreach ($company->getObjectProperties() as $name => $value) {
$expression = $company->getPropertyExpression($name);
printf(" ->%''.-10s = /"%s/" -- %s/n", $name.'' '', $value, $expression);
}
}
Esto funciona con DOMObjectCollection y DOMValueObject , definiendo su propio tipo:
class Companies extends DOMValueCollection
{
public function __construct(DOMDocument $doc) {
parent::__construct($doc, ''//*[@class="company"]'');
}
/**
* @return DOMValueObject
*/
public function current() {
$object = parent::current();
$object->defineProperty(''id'', ''string(@id)'');
$object->defineProperty(''name'', ''.//a[1]/text()'');
$object->defineProperty(''href'', ''.//a[1]/@href'');
$object->defineProperty(''img'', ''.//img[1]/@src'');
$object->defineProperty(''address'', ''.//*[@class="address"]/text()'');
# ... add your definitions
return $object;
}
}
Y para sus requisitos de matriz, hay un método getArrayCopy()
:
echo "/nGet Array Copy:/n/n";
print_r($companies->getArrayCopy());
Salida:
Get Array Copy:
Array
(
[0] => Array
(
[id] => company-6666
[name] => Company Name
[href] => /company-name
[img] => /graphics/company/logo/listing/123456.jpg?_ts=1365390237
[address] => StreetName 500, 7777 City, County
)
)
Para verificar si existe un nodo, verifique que la propiedad length sea igual a 1 en el resultado de la consulta devuelta:
if ($company_name->length == 1) {
$object->company_name = trim($company_name->item(0)->nodeValue);
}