I am trying to extract the headers table from this url: https://www4.bcb.gov.br/pec/poupanca/poupanca.asp
Unfortunately, they use a "non-standard" html table where instead of rowspan they used a (bizarre) table inside the first row 😩...
So I would like to extract dynamically this header in an array of 8 items like this (merging the the headers with two rows size):
$columns_extracted_result = [
'Data',
'DataFim',
'Depósitos até 03.05.2012 - Remuneração básica ',
'Depósitos até 03.05.2012 - Remuneração adicional',
'Depósitos até 03.05.2012 - Remuneração total',
'Depósitos a partir de 04.05.2012 (*) -Remuneração básica',
'Depósitos a partir de 04.05.2012 (*) - Remuneração adicional',
'Depósitos a partir de 04.05.2012 (*) - Remuneração total'
];
And after that, create a array where the keys will be the $columns_extracted_result like:
$table = [
[
'Data' => '26/04/2022',
'DataFim' => '26/05/2022',
'Depósitos até 03.05.2012 - Remuneração básica' => '0,1538'
//...
],
[
'Data' => '27/04/2022',
'DataFim' => '27/05/2022',
'Depósitos até 03.05.2012 - Remuneração básica' => '0,1568'
//...
]
];
How Can I achieve this using DomXpath ?
The get_columns
function returns a list of columns as defined in the table headers:
array(8) {
[0]=>
string(4) "Data"
[1]=>
string(8) "Data fim"
[2]=>
string(50) "Depósitos até 03.05.2012 - Remuneração básica"
[3]=>
string(52) "Depósitos até 03.05.2012 - Remuneração adicional"
[4]=>
string(48) "Depósitos até 03.05.2012 - Remuneração total"
[5]=>
string(61) "Depósitos a partir de 04.05.2012 (*) - Remuneração básica"
[6]=>
string(63) "Depósitos a partir de 04.05.2012 (*) - Remuneração adicional"
[7]=>
string(59) "Depósitos a partir de 04.05.2012 (*) - Remuneração total"
}
The get_rows
function returns the complete data set, see code and output:
<?php
$doc = new \DOMDocument();
@$doc->loadHTMLFile("https://www4.bcb.gov.br/pec/poupanca/poupanca.asp");
$xpath = new DOMXpath($doc);
$data = get_rows($xpath, get_columns($xpath));
var_dump($data);
function get_inner_html(DOMElement $node) : string
{
$innerHTML= '';
$children = $node->childNodes;
foreach ($children as $child) {
$innerHTML .= $child->ownerDocument->saveXML( $child );
}
return $innerHTML;
}
function get_clean_value(DomElement $text) : string
{
$html = get_inner_html($text);
$decode = html_entity_decode($html);
$replace = str_replace(["<br/>", "\t", PHP_EOL, " ", "\xc2\xa0"], " ", $decode);
$stripped = strip_tags($replace);
return trim($stripped);
}
function get_columns(DOMXPath $xpath) : array
{
$tds = $xpath->query("(/html/body/table/tbody/tr)[1]/td");
$columns = [];
foreach($tds as $td) {
$tdFirst = $xpath->query('(table)[1]/tr/td', $td);
$tdSecond = $xpath->query("(table)[2]/tr/td", $td);
if($tdFirst->count() > 0 && $tdSecond->count() > 0) {
$columnPrefix = get_clean_value($tdFirst[0]);
foreach($tdSecond as $item) {
$columns[] = $columnPrefix . " - " . get_clean_value($item);
}
} else {
$columns[] = get_clean_value($td);
}
}
return $columns;
}
function get_rows(DOMXPath $xpath, array $columns) : array
{
$result = [];
$trs = $xpath->query("/html/body/table/tbody/tr");
foreach($trs as $count=>$tr) {
if($count === 0) continue;
$tds = $xpath->query("td", $tr);
$row = [];
foreach($tds as $td) {
$row[] = get_clean_value($td);
}
$result[] = array_combine($columns, $row);
}
return $result;
}
array(45) {
[0]=>
array(8) {
["Data"]=>
string(10) "27/04/2022"
["Data fim"]=>
string(10) "27/05/2022"
["Depósitos até 03.05.2012 - Remuneração básica"]=>
string(6) "0,1568"
["Depósitos até 03.05.2012 - Remuneração adicional"]=>
string(6) "0,5000"
["Depósitos até 03.05.2012 - Remuneração total"]=>
string(6) "0,6576"
["Depósitos a partir de 04.05.2012 (*) - Remuneração básica"]=>
string(6) "0,1568"
["Depósitos a partir de 04.05.2012 (*) - Remuneração adicional"]=>
string(6) "0,5000"
["Depósitos a partir de 04.05.2012 (*) - Remuneração total"]=>
string(6) "0,6576"
}
...