如果沒有相符的結果,則返回一個空的 DOMNodeList。使用 length 屬性檢查,例如:
<?php
$nodes=$domDocument->getElementsByTagName('book') ;
if ($nodes->length==0) {
// 沒有結果
}
?>
(PHP 5, PHP 7, PHP 8)
DOMDocument::getElementsByTagName — 搜尋所有具有指定本地標籤名稱的元素
此函式會傳回一個新的 DOMNodeList 類別的實例,其中包含所有具有指定本地標籤名稱的元素。
qualifiedName
要比對的本地名稱(不含命名空間)。特殊值 *
會比對所有標籤。
一個新的 DOMNodeList 物件,其中包含所有相符的元素。
範例 #1 基本用法範例
<?php
$xml = <<< XML
<?xml version="1.0" encoding="utf-8"?>
<books>
<book>Patterns of Enterprise Application Architecture</book>
<book>Design Patterns: Elements of Reusable Software Design</book>
<book>Clean Code</book>
</books>
XML;
$dom = new DOMDocument;
$dom->loadXML($xml);
$books = $dom->getElementsByTagName('book');
foreach ($books as $book) {
echo $book->nodeValue, PHP_EOL;
}
?>
以上範例會輸出:
Patterns of Enterprise Application Architecture Design Patterns: Elements of Reusable Software Design Clean Code
如果沒有相符的結果,則返回一個空的 DOMNodeList。使用 length 屬性檢查,例如:
<?php
$nodes=$domDocument->getElementsByTagName('book') ;
if ($nodes->length==0) {
// 沒有結果
}
?>
請注意,使用 getElementsByTagName 時,它是一個動態列表。因此,如果您有調整 DOM 結構的程式碼,它將會更改 getElementsByTagName 結果列表的結果。
以下程式碼會迭代遍歷一組完整的結果,並將它們全部更改為新的標籤
<?php
$nodes = $xml->getElementsByTagName("oldtag");
$nodeListLength = $nodes->length; // 這個值也會改變
for ($i = 0; $i < $nodeListLength; $i ++)
{
$node = $nodes->item(0);
// 一些程式碼將標籤名稱從 "oldtag" 改成其他名稱
// 例如加密標籤元素
}
?>
由於清單是動態更新的,`$nodes->item(0)` 是下一個「未更改的」標籤。
我的第一篇文章!
這就是我如何透過屬性及其值取得元素。
例如,如果我想抓取所有 class 名稱為 'className' 的 DIV 標籤,那麼……
<?php
$網址 = '某個網站';
$標籤名稱 = 'div';
$屬性名稱 = 'class';
$屬性值 = 'className';
$dom = new DOMDocument;
$dom->preserveWhiteSpace = false;
@$dom->loadHTMLFile($網址);
$html = 取得標籤( $dom, $標籤名稱, $屬性名稱, $屬性值 );
echo $html;
function 取得標籤( $dom, $標籤名稱, $屬性名稱, $屬性值 ) {
$html = '';
$domxpath = new DOMXPath($dom);
$newDom = new DOMDocument;
$newDom->formatOutput = true;
$filtered = $domxpath->query("//$標籤名稱" . '[@' . $屬性名稱 . "='$屬性值']");
// 透過迴圈將 DomNodeList 物件轉換為字串 (HTML)
foreach($filtered as $myItem) {
$node = $newDom->importNode($myItem, true); // 匯入節點
$newDom->appendChild($node); // 附加節點
}
$html = $newDom->saveHTML();
return $html;
}
?>
請改進它並分享。
這裡有一個 getElementsByTagName() 的例子
<?php
$xml = <<<EOT
<?xml version="1.0"?>
<config>
<section id="section1">
<param name="param1">value1</param>
<param name="param2">value2</param>
</section>
<section id="section2">
<param name="param3">value3</param>
</section>
</config>
EOT;
$dom = new DomDocument;
$dom->preserveWhiteSpace = FALSE;
$dom->loadXML($xml);
$params = $dom->getElementsByTagName('param');
foreach ($params as $param) {
echo $param -> getAttribute('name').'<br>';
}
?>
預期結果
--------------
param1
param2
param3
以下範例包含多個屬性和多個子節點。這用於製作 Joomla 的批量上傳文章外掛程式。Gurmukh Singh Bhatti
<?php
$xml =<<<EOT
<?xml version="1.0"?>
<root>
<section name="Section1">
<category id="Category1" name="google">
<arti name="article1">
<p>any html code here</p>
<b>my name is so so</b>
</arti>
<arti name="article2">value2</arti>
<arti name="article3">value3</arti>
<arti name="article4">value4</arti>
</category>
<category id="Category2" name="yahoo">
<arti name="articleSection2">Test value</arti>
</category>
</section>
<section name="Section2">
<category id="category1_of_section2" name="msn">
<arti name="article2">value1</arti>
<arti name="article3">value2</arti>
</category>
<category id="Category2_of_section2" name="webcare">
<arti name="param3">value4</arti>
</category>
</section>
</root>
EOT;
$dom = new DomDocument;
$dom->preserveWhiteSpace = FALSE;
$dom->loadXML($xml);
$params = $dom->getElementsByTagName('section'); // Find Sections
$k=0;
foreach ($params as $param) //go to each section 1 by 1
{
echo "Section Attribute :-> ".$params->item($k)->getAttribute('name')."<br>"; //get section attribute
$params2 = $params->item($k)->getElementsByTagName('category'); //digg categories with in Section
$i=0; // values is used to iterate categories
foreach ($params2 as $p) {
echo " - Category Attribute Name :-> ".$params2->item($i)->getAttribute('name')."<br>"; //get Category attributes
$params3 = $params2->item($i)->getElementsByTagName('arti'); //dig Arti into Categories
$j=0;//values used to interate Arti
foreach ($params3 as $p2)
{
echo " - Article Attribute Name : ".$params3->item($j)->getAttribute('name').""; //get arti atributes
echo " Value : ".$params3->item($j)->nodeValue."<br>"; //get Node value ;
$j++;
}
$i++;
}
$k++;
}
?>
輸出
區段屬性:-> Section1
- 類別屬性名稱:-> google
- 文章屬性名稱:article1 值:任何 html 程式碼 heremy name is so so
- 文章屬性名稱:article2 值:value2
- 文章屬性名稱:article3 值:value3
- 文章屬性名稱:article4 值:value4
- 類別屬性名稱:-> yahoo
- 文章屬性名稱:articleSection2 值:Test value
區段屬性:-> Section2
- 類別屬性名稱:-> msn
- 文章屬性名稱:article2 值:value1
- 文章屬性名稱:article3 值:value2
- 類別屬性名稱:-> webcare
- 文章屬性名稱:param3 值:value4
我需要一個能在特定節點上下文 (contextNode) 中生效的 $dom->getElementsByTagName 方法。
我需要 getElementsByTagName 而不是簡單地使用 xPath->query 的原因是,在迴圈處理返回的節點列表時,會建立更多具有我正在尋找的標籤名稱的節點。
使用 getElementsByTagName 時,新的節點會「添加」到我正在迴圈處理的節點列表中。
使用 xpath 查詢時,您只會迴圈處理原始節點列表,新建立的元素不會出現在該節點列表中。
我已經在 domDocument 上使用了一個擴展類別,因此建立一個可以接受 contextNode 的 getElementsByTagName 類似方法很簡單。
<?php
class SmartDocument extends DOMDocument {
private $localDom;
public $xpath;
private $serialize = array('localDom');
private $elemName;
private $elemCounter;
/**
* Constructor
*/
function __construct() {
parent::__construct ( '1.0', 'UTF-8' );
$this->preserveWhiteSpace = false;
$this->recover = TRUE;
$this->xpath = new DOMXpath ( $this );
}
/**
* GetElementsByTagname within an contextNode
*
* @param string $name
* @param DomNode $contextNode
* @return DOMNode|NULL
*/
public function getElementsByTagNameContext($name, $contextNode) {
if($this->elemName!=$name) {
$this->elemCounter = 0;
$this->elemName =$name;
}
$this->elemLength = $this->xpath->evaluate('count(.//*[name()="'.$this->elemName.'"])', $contextNode);
while($this->elemCounter < $this->elemLength) {
$this->elemCounter++;
$nl = $this->xpath->query('.//*[name()="'.$this->elemName.'"]['.$this->elemCounter.']', $contextNode);
if($nl->length == 1) {
return $nl->item(0);
}
}
$this->elemLength = null;
$this->elemCounter = null;
$this->elemName = null;
return null;
}
}
?>
用法
<?php
$doc = new SmartDocument();
$doc->load('book.xml');
$nl = $doc->query('//books');
foreach($nl as $node) {
while($book = $doc->getElementsByTagNameContext('book', $node)) {
//當您現在在此迴圈內建立新的節點作為此節點的子節點或後續兄弟節點時
// 它們會顯示在此迴圈內
}
}
?>
以下程式碼從 XML 檔案(或 RSS 摘要)中獲取新聞項目列表,先將其指定給一個陣列以獲得名稱/值對,然後產生 HTML 列表。
<?php
$xml =<<<EOT
<?xml version="1.0" encoding="ISO-8859-1"?>
<news>
<item>
<title>News 1</title>
<created>04/2/2010 08:00 EST</created>
<url>http://news.example.com/news.pdf</url>
</item>
<item>
<title>News 2</title>
<created>04/25/2010 08:00 EST</created>
<url>http://news.example.com/news.pdf</url>
</item>
<item>
<title>News 3</title>
<created>04/27/2010 08:00 EST</created>
<url>http://news.example.com/news.pdf</url>
</item>
</news>
EOT;
$doc = new DOMDocument();
if ($doc->loadXML($xml)) {
$items = $doc->getElementsByTagName('item');
$headlines = array();
foreach($items as $item) {
$headline = array();
if($item->childNodes->length) {
foreach($item->childNodes as $i) {
$headline[$i->nodeName] = $i->nodeValue;
}
}
$headlines[] = $headline;
}
if(!empty($headlines)) {
$hc = 0;
echo '<ul>';
foreach($headlines as $headline) {
if(++$hc <= 3) {
echo '<li>'
.'<a href="'.$headline['url'].'" target="_blank">'
.'<span>'.date('F j, Y', strtotime($headline['created'])).'</span><br />'
.$headline['title']
.'</a>'
.'</li>';
}
}
echo '</ul>';
}
}
?>
這是一個使用 DOMDocument 類別來遍歷 XML 節點和子節點的非常簡化的方式。
<?php
$xml ='<?xml version="1.0" encoding="utf-8"?>
<root>
<Parent>
<child>child 1</child>
<child>child 2</child>
<child>child 3</child>
<subParent>
<Grandchild>Grandchild 1</Grandchild>
<Grandchild>Grandchild 2</Grandchild>
<Grandchild>Grandchild 3</Grandchild>
</subParent>
</Parent>
<Parent>
<child>child 4</child>
<child>child 5</child>
<child>child 6</child>
<subParent>
<Grandchild>Grandchild 4</Grandchild>
<Grandchild>Grandchild 5</Grandchild>
<Grandchild>Grandchild 6</Grandchild>
</subParent>
</Parent>
</root>';
$doc = new DOMDocument();
$doc->preserveWhiteSpace = false;
$doc->loadXML($xml);
$i=0;
while(is_object($finance = $doc->getElementsByTagName("Parent")->item($i)))
{
foreach($finance->childNodes as $nodename)
{
if($nodename->nodeName=='subParent')
{
foreach($nodename->childNodes as $subNodes)
{
echo $subNodes->nodeName." - ".$subNodes->nodeValue."<br>";
}
}
else
{
echo $nodename->nodeName." - ".$nodename->nodeValue."<br>";
}
}
$i++;
}
?>
我不知道這是否很明顯,但對我來說並非如此,因此除了 gem at rellim dot com 的貼文之外
加上
<?php
echo $param -> nodeValue.'<br>';
?>
到迴圈將輸出
值 1
值 2
值 3
這裡有一個函式,可以將 HTML 文件中已填入的表格轉換為陣列。
<?php
// 建立一個陣列,包含 HTML 頁面中所有已填入資料的表格
// 返回的陣列:tables_to_array[表格編號][列編號][欄編號]
function tables_to_array($url) {
$htmlDocDom = new DOMDocument();
@$htmlDocDom->loadHTMLFile($url);
$htmlDocDom->preserveWhiteSpace = false;
$tableCounter = 0;
$htmlDocTableArray = array();
$htmlDocTables = $htmlDocDom->getElementsByTagName('table');
foreach ($htmlDocTables as $htmlDocTable) {
$htmlDocTableArray[$tableCounter] = array();
$htmlDocRows = $htmlDocTable->getElementsByTagName('tr');
$htmlDocRowCount = 0;
$htmlDocTableArray[$tableCounter] = array();
foreach ($htmlDocRows as $htmlDocRow) {
if (strlen($htmlDocRow->nodeValue) > 1)
{
$htmlDocColCount = 0;
$htmlDocTableArray[$tableCounter][$htmlDocRowCount] = array();
$htmlDocCols = $htmlDocRow->getElementsByTagName('td');
foreach ($htmlDocCols as $htmlDocCol) {
$htmlDocTableArray[$tableCounter][$htmlDocRowCount][] = $htmlDocCol->nodeValue;
$htmlDocColCount++;
}
$htmlDocRowCount++;
}
}
if ($htmlDocRowCount > 1) $tableCounter++;
}
return($htmlDocTableArray);
}
?>
我第一次嘗試用這個函式來取得穩定的解決方案,卻遇到以下的例外錯誤
「致命錯誤:呼叫未定義的方法 DOMNodeList::getElementsByTagName()」
這是 XML 片段
<?xml version="1.0" encoding="UTF-8"?>
<root>
<component>
<properties>
....<任何元素>
</properties>
</component>
</root>
所以用來爬取這些元素的 PHP 程式碼如下:
<?php
$src = new DOMDocument('1.0', 'utf-8');
$src->formatOutput = true;
$src->preserveWhiteSpace = false;
//載入外部檔案
$src->load('../xml/Item_component.xml');
//檢查 <component> 的第一個索引分支節點的每個子節點
//首先取得根元素之後的元素:<component>
//第一層
$component = $src->getElementsByTagName('component')->item(0);
//第二層,取得 component 之後的下一個元素,這裡會失敗!!
$properties = $component->getElementsByTagName('properties')->item(0);
...
?>
我發現,在 Apache2 上使用不同的 libxml2 版本時會有差異。這段程式碼在 libxml2 版本 2.6.23 和 PHP 版本 5.2.6 時會失敗。
--
->在 libxml2 版本 2.6.32 和 PHP 版本 5.2.6-3ubuntu4.6 時可以正常運作。
->...最後,它在 libxml2 2.7.7 和 PHP >= 5.3 時也能正常運作。
所以,如果您像我一樣厭倦了尋找 DOM 解決方案,請確保您的 www 環境在 apache2 伺服器上安裝了正確的 libxml2 / PHP 版本。
問題
您有一個 XML 文件,其中包含檔案名稱參考,例如圖片。每個檔案名稱參考都由 <file>filename.ext</file> 標籤定義。您希望在 XML 文件通過 XSD 驗證後執行額外的驗證。額外的驗證可以是您選擇的任何驗證,在本例中,理想的做法是將 PHP 程式碼轉換為函式。該函式將判斷圖片是否存在,並返回一個整數值或布林值。
<?xml version="1.0"?>
<root>
<box>
<file>example.png</file>
</box>
<content>
<item>
<image><file>example2.png</file></image>
<caption>上面的圖片是一個範例</caption>
</item>
</content>
</root>
解決方案
<?php
$dom = new DomDocument();
$dom->preserveWhiteSpace = false;
if (!@$dom->load("example.xml")) {
echo "example.xml 不存在!\n";
return;
}
$imageList = $dom->getElementsByTagName('file');
$imageCnt = $imageList->length;
for ($idx = 0; $idx < $imageCnt; $idx++) {
print $imageList->item($idx)->nodeValue . "\n";
}
?>
以上 PHP 程式碼可以輕鬆轉換成一個函式,用於返回影像檔名陣列、找到的影像數量等整數值。
希望這有幫助。
回覆 tildy at pr dot hu
我偏好的方式是(以從 iso 3166 xml 檔案收集國家資料為例)
$countries = new DOMDocument();
$countries->load("./lib/iso_3166.xml");
$countriesList = $countries->getElementsByTagName("ISO_3166-1_Entry");
foreach($countriesList as $country) {
$values = $country->getElementsByTagName("*");
foreach($values as $node) {
echo $node->nodeName."=".$node->nodeValue;
}
}
如果您想將所有節點移動到另一個標籤,您可以這樣做
例如:將帶有節點的 <div> 替換為帶有相同節點的 <p>。
function replaceDomElementTag(DOMDocument $dom, DOMElement $node, string $tagName)
{
$newElement = $dom->createElement($tagName);
while ($node->childNodes->length)
$newElement->appendChild($node->childNodes[0]);
$node->parentNode->replaceChild($newElement, $node);
}
$dom = new DOMDocument();
replaceDomElementTag($dom, $divElementToReplace, 'p');