Using the following xml: http://www.bnr.ro/nbrfxrates.xml
How can I get the EUR value?
Been trying like this ... but no luck.
$xmlDoc = new DOMDocument();
$xmlDoc->load('http://www.bnr.ro/nbrfxrates.xml');
$searchNode = $xmlDoc->getElementsByTagName("Cube");
var_dump($searchNode);
foreach ($searchNode as $searchNode) {
$valueID = $searchNode->getAttribute('Rate');
echo $valueID;
}
Check this
<?php
$xmlDoc = new DOMDocument();
$xmlDoc->load('http://www.bnr.ro/nbrfxrates.xml');
foreach ($xmlDoc->getElementsByTagName('Rate') as $searchNode) {
if($searchNode->getAttribute('currency') === 'EUR') {
echo $searchNode->nodeValue;
}
}
?>
First Rate is not an attribute but an element. So you would need another getElementsByTagName('Rate') and loop over it. However the XML uses a default namespace so getElementByTagNameNS('http://www.bnr.ro/xsd', 'Rate') would be the correct way.
An easier way is to use Xpath to fetch the value directly:
$document = new DOMDocument();
$document->load('http://www.bnr.ro/nbrfxrates.xml');
$xpath = new DOMXpath($document);
$xpath->registerNamespace('r', 'http://www.bnr.ro/xsd');
var_dump(
$xpath->evaluate('number(//r:Cube/r:Rate[#currency="EUR"])')
);
Output:
float(4.4961)
Xpath does not have a default namespace, so you have to register your own alias for it (I used r in the example.).
The Xpath expression
fetch any {http://www.bnr.ro/nbrfxrates.xml}Cube
//r:Cube
fetch all {http://www.bnr.ro/nbrfxrates.xml}Rate children
//r:Cube/r:Rate
filter by the currency attribute
//r:Cube/r:Rate[#currency="EUR"]
cast the first found node into a number
number(//r:Cube/r:Rate[#currency="EUR"])
<?php
$xmlDoc = new DOMDocument();
$xmlDoc->load('http://www.bnr.ro/nbrfxrates.xml');
foreach($xmlDoc->getElementsByTagName("Rate") as $node)
{
$currency = $node->getAttribute('currency');
if($currency == 'EUR')
{
$value = $node->nodeValue;
}
}
echo 'value for EUR is - '. $value;
?>
Related
Why does not display the attribute html via xpath php
<?php
$content = '<div class="keep-me">Keep this div</div><div class="remove-me" id="test">Remove this div</div>';
$badClasses = array('');
$dom = new DOMDocument;
libxml_use_internal_errors(true);
$dom->loadHTML($content);
libxml_clear_errors();
$xPath = new DOMXpath($dom);
foreach($badClasses as $badClass){
$domNodeList = $xPath->query('//div[#class="remove-me"]/#id');
$domElemsToRemove = ''; // container of deleted elements
foreach ( $domNodeList as $domElement ) {
$domElemsToRemove .= $dom->saveHTML($domElement); // concat them
$domElement->parentNode->removeChild($domElement); // then remove
}
}
$content = $dom->saveHTML();
echo htmlentities($domElemsToRemove);
?>
Works - //div[#class="remove-me"] or //div[#class="remove-me"]/text()
Not working - //div[#class="remove-me"]/#id
Maybe there is a way easier
The XPath //div[#class="remove-me"]/#id is correct, but you need to just loop over the returned elements and add the nodeValue to a list of matching ID's...
$xPath = new DOMXpath($dom);
$domNodeList = $xPath->query('//div[#class="remove-me"]/#id');
$ids = []; // container of deleted elements
foreach ( $domNodeList as $domElement ) {
$ids[] = $domElement->nodeValue;
}
print_r($ids);
If the aim is to fetch the ID of any element with class "remove-me" as is how I interpret the question then perhaps you can try like this - untested btw...
.... other code before
$xp=new DOMXpath( $dom );
$col= $xp->query( '*[#class="remove-me"]' );
if( $col->length > 0 ){
foreach($col as $node){
$id=$node->hasAttribute('id') ? $node->getAttribute('id') : 'banana';
echo $id;
}
}
however looking at the code in the question suggests that you wish to delete nodes - in which case build an array of nodes ( nodelist ) and iterate through it from the end to the front - ie: backwards...
I am trying to load html, find a tag and add an attribute to it, before showing it.
I have tried:
libxml_use_internal_errors(true);
$domDocument->loadHTML("<html><body>Test<br></body></html>");
$domElement = $domDocument->getElementsByTagName('body');
foreach ($domElement as $formula) {
$formula->nodeValue->createAttribute('name')->value = 'attributevalue';
}
libxml_use_internal_errors(false);
But I have this error:
Call to a member function createAttribute() on string
Do you have a solution please ?
Best regards
The nodeValue returns the String type, which isn't the way to create an attribute.
And in fact, the node's type is DOMElement, so you need to set the attribute as similar as the following code:
<?php
$domDocument = new DOMDocument();
$domDocument->loadHTML("<html><body>Test<br></body></html>");
$domElement = $domDocument->getElementsByTagName('body');
foreach ($domElement as $formula) {
$formula->setAttribute("name", "attributevalue");
}
?>
Try it like this
libxml_use_internal_errors(true);
$doc = new DOMDocument();
$doc->loadHTML("<html><body>Test<br></body></html>");
$domElements = $doc->getElementsByTagName('body');
foreach ($domElements as $domElement) {
$domAttribute = $doc->createAttribute('name');
$domAttribute->value = 'attributevalue';
$domElement->appendChild($domAttribute);
print_r($domElement->getAttribute('name'));
// returns attributevalue
}
libxml_use_internal_errors(false);
Try it out #PHP-Sandbox
Here is a possible solution where setAttribute is used instead of create. Though I am not sure the purpose of the loop since there is usually only 1 body tag.
libxml_use_internal_errors(true);
$domDocument->loadHTML("<html><body>Test<br></body></html>");
$domElement = $domDocument->getElementsByTagName('body');
foreach ($domElement as $formula) {
$formula->setAttribute('name', 'thevalue');
}
libxml_use_internal_errors(false);
I'm trying to get the bing search results with XPath. Here is my code:
$html = file_get_contents("http://www.bing.com/search?q=bacon&first=11");
$doc = new DOMDocument();
libxml_use_internal_errors(true);
$doc->loadHtml($html);
$x = new DOMXpath($doc);
$output = array();
// just grab the urls for now
foreach ($x->query("//li[#class='b_algo']") as $node)
{
//$output[] = $node->getAttribute("href");
$tmpDom = new DOMDocument();
$tmpDom->loadHTML($node);
$tmpDP = new DOMXPath($tmpDom);
echo $tmpDP->query("//div[#class='b_title']//h2//a//href");
}
return $output;
This foreach iterates over all results, all I want to do is to extract the link and text from $node in foreach, but because $node itself is an object I can't create a DOMDocument from it. How can I query it?
First of all, your XPath expression tries to match non-existant href subelements, query #href for the attribute.
You don't need to create any new DOMDocuments, just pass the $node as context item:
foreach ($x->query("//li[#class='b_algo']") as $node)
{
var_dump( $x->query("./div[#class='b_title']//h2//a//#href", $node)->item(0) );
}
If you're just interested in the URLs, you could also query them directly:
foreach ($x->query("//li[#class='b_algo']/div[#class='b_title']/h2/a/#href") as $node)
{
var_dump($node);
}
How do I echo and scrape a div class? I tried this but it doesn't work. I am using cURL to establish the connection. How do I echo it? I want it just how it is on the actual page.
$document = new DOMDocument();
$document->loadHTML($html);
$selector = new DOMXPath($document);
$anchors = $selector->query("/html/body//div[#class='resultitem']");
//a URL you want to retrieve
foreach($anchors as $a) {
echo $a;
}
Neighbor,
I just made this snippet below, that uses your logic, and some tweaks to display the specified class from the webpage in the get_contents function.
Maybe you can plug in your values and try it?
(Note: I put the error checking in there to see a few bugs. It can be helpful to use that as you tweak. )
<?php
error_reporting(E_ALL);
ini_set('display_errors', '1');
$url = "http://www.tizag.com/cssT/cssid.php";
$class_to_scrape="display";
$html = file_get_contents($url);
$document = new DOMDocument();
$document->loadHTML($html);
$selector = new DOMXPath($document);
$anchors = $selector->query("/html/body//div[#class='". $class_to_scrape ."']");
echo "ok, no php syntax errors. <br>Lets see what we scraped.<br>";
foreach ($anchors as $node) {
$full_content = innerHTML($node);
echo "<br>".$full_content."<br>" ;
}
/* this function preserves the inner content of the scraped element.
** http://stackoverflow.com/questions/5349310/how-to-scrape-web-page-data-without-losing-tags
** So be sure to go and give that post an uptick too:)
**/
function innerHTML(DOMNode $node)
{
$doc = new DOMDocument();
foreach ($node->childNodes as $child) {
$doc->appendChild($doc->importNode($child, true));
}
return $doc->saveHTML();
}
?>
i'm struggling with Xpath, i have an xml list and i need to get the child data based on the parent id ...
My xml file :
<projecten>
<project id="1">
<titel>Shop 1</titel>
<siteurl>http://test.be</siteurl>
<screenshot>test.jpg</screenshot>
<omschrijving>comment 1</omschrijving>
</project>
<project id="2">
<titel>Shop 2</titel>
<siteurl>http://test2.be</siteurl>
<screenshot>test2.jpg</screenshot>
<omschrijving>comment</omschrijving>
</project>
</projecten>
the code i use to get for example the project 1 data (does not work):
$xmlDoc = new DOMDocument();
$xmlDoc->load(data.xml);
$xpath = new DOMXPath($xmlDoc);
$projectId = '1';
$query = '//projecten/project[#id='.$projectId.']';
$details = $xpath->query($query);
foreach( $details as $detail )
{
echo $detail->titel;
echo $detail->siteurl;
echo $detail->screenshot;
echo $detail->omschrijving;
}
But this does not show anything, if someone can point me out ... thanks
In addition to the solution already given you can also use:
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
echo
$projectNode->getElementsByTagName('titel')->item(0)->nodeValue,
$projectNode->getElementsByTagName('siteurl')->item(0)->nodeValue,
$projectNode->getElementsByTagName('screenshot')->item(0)->nodeValue,
$projectNode->getElementsByTagName('omschrijving')->item(0)->nodeValue;
}
or fetch the DOMText node values directly with Xpath
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
echo
$xpath->evaluate('string(titel)', $projectNode),
$xpath->evaluate('string(siteurl)', $projectNode),
$xpath->evaluate('string(screenshot)', $projectNode),
$xpath->evaluate('string(omschrijving)', $projectNode);
}
or import the node to SimpleXml
foreach ($xpath->query(sprintf('/projecten/project[#id="%d"]', $id)) as $projectNode) {
$detail = simplexml_import_dom($projectNode);
echo
$detail->titel,
$detail->siteurl,
$detail->screenshot,
$detail->omschrijving;
}
or even concatenate all the values directly in the XPath:
$xpath = new DOMXPath($dom);
echo $xpath->evaluate(
sprintf(
'concat(
/projecten/project[#id = %1$d]/titel,
/projecten/project[#id = %1$d]/siteurl,
/projecten/project[#id = %1$d]/screenshot,
/projecten/project[#id = %1$d]/omschrijving
', $id
)
);
Accessing the child nodes as you do:
echo $detail->title;
Is not valid, if you use DOM* functions. This would probably work if you were using SimpleXML.
For DOM* try this:
$dom = new DOMDocument;
$dom->loadXml('<projecten>
<project id="1">
<titel>Shop 1</titel>
<siteurl>http://test.be</siteurl>
<screenshot>test.jpg</screenshot>
<omschrijving>comment 1</omschrijving>
</project>
<project id="2">
<titel>Shop 2</titel>
<siteurl>http://test2.be</siteurl>
<screenshot>test2.jpg</screenshot>
<omschrijving>comment</omschrijving>
</project>
</projecten>
');
$id = 2;
$xpath = new DOMXPath($dom);
foreach ($xpath->query(sprintf('/projecten/project[#id="%s"]', $id)) as $projectNode) {
// repeat this for every needed node
$titleNode = $xpath->query('titel', $projectNode)->item(0);
if ($titleNode instanceof DOMElement) {
echo $titleNode->nodeValue;
}
// or us a loop for all child nodes
foreach ($projectNode->childNodes as $childNode) {
echo $childNode->nodeValue;
}
}