PHP Conference Japan 2024

DOMDocument::getElementsByTagName

(PHP 5, PHP 7, PHP 8)

DOMDocument::getElementsByTagName搜索所有具有给定本地标签名称的元素

描述

public DOMDocument::getElementsByTagName(string $qualifiedName): DOMNodeList

此函数返回一个新的 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

参见

添加注释

用户贡献的注释 14 个注释

James L
16 年前
如果没有匹配项,则返回一个空的 DOMNodeList。使用 length 属性进行检查,例如

<?php
$nodes
=$domDocument->getElementsByTagName('book') ;
if (
$nodes->length==0) {
// 没有结果
}
?>
Philip N
14 年前
请注意,使用 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) 是下一个“未更改”的标签。
calvin at g mail
14 年前
我的第一篇帖子!
这就是我如何通过属性及其值获取元素。
例如,如果我想获取所有具有类名“className”的 DIV 标签,则...

<?php
$some_link
= 'some website';
$tagName = 'div';
$attrName = 'class';
$attrValue = 'className';

$dom = new DOMDocument;
$dom->preserveWhiteSpace = false;
@
$dom->loadHTMLFile($some_link);

$html = getTags( $dom, $tagName, $attrName, $attrValue );
echo
$html;

function
getTags( $dom, $tagName, $attrName, $attrValue ){
$html = '';
$domxpath = new DOMXPath($dom);
$newDom = new DOMDocument;
$newDom->formatOutput = true;

$filtered = $domxpath->query("//$tagName" . '[@' . $attrName . "='$attrValue']");
// $filtered = $domxpath->query('//div[@class="className"]');
// '//' 当您不知道“绝对”路径时

// 由于上面返回 DomNodeList 对象
// 我使用以下例程将其转换为字符串(html);从该站点中某人的帖子中复制。谢谢。
$i = 0;
while(
$myItem = $filtered->item($i++) ){
$node = $newDom->importNode( $myItem, true ); // 导入节点
$newDom->appendChild($node); // 追加节点
}
$html = $newDom->saveHTML();
return
$html;
}

?>

请改进它并分享。
gem at rellim dot com
20 年前
这是一个 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
[email protected]
15年前
以下示例包含多个属性和多个子节点。这被用于为文章批量上传创建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'); // 查找 Sections
$k=0;
foreach (
$params as $param) //逐个访问每个 section
{
echo
"Section 属性 :-> ".$params->item($k)->getAttribute('name')."<br>"; //获取 section 属性
$params2 = $params->item($k)->getElementsByTagName('category'); //在 Section 内查找 categories
$i=0; // values 用于迭代 categories
foreach ($params2 as $p) {
echo
"&nbsp;&nbsp;- Category 属性名称 :-> ".$params2->item($i)->getAttribute('name')."<br>"; //获取 Category 属性
$params3 = $params2->item($i)->getElementsByTagName('arti'); //在 Categories 内查找 Arti
$j=0;//values 用于迭代 Arti
foreach ($params3 as $p2)
{
echo
"&nbsp;&nbsp;&nbsp;- Article 属性名称 : ".$params3->item($j)->getAttribute('name').""; //获取 arti 属性
echo "&nbsp;&nbsp; 值 : ".$params3->item($j)->nodeValue."<br>"; //获取节点值;
$j++;
}
$i++;
}
$k++;

}
?>

输出
Section 属性 :-> Section1
- Category 属性名称 :-> google
- Article 属性名称 : article1 值 : any html code heremy name is so so
- Article 属性名称 : article2 值 : value2
- Article 属性名称 : article3 值 : value3
- Article 属性名称 : article4 值 : value4
- Category 属性名称 :-> yahoo
- Article 属性名称 : articleSection2 值 : Test value
Section 属性 :-> Section2
- Category 属性名称 :-> msn
- Article 属性名称 : article2 值 : value1
- Article 属性名称 : article3 值 : value2
- Category 属性名称 :-> webcare
- Article 属性名称 : param3 值 : value4
[email protected]
10年前
我需要一个 $dom->getElementsByTagName,它可以在 contextNode 内发挥作用。

我为什么需要 getElementsByTagName 而不是简单地使用 xPath->query,是因为在循环遍历返回的节点列表时,创建了更多具有我正在查找的 tagName 的节点。

当使用 getElementsByTagName 时,新节点会被“添加到”我已在循环遍历的节点列表中。

当使用 xpath 查询时,你只会循环遍历原始节点列表,新创建的元素不会出现在该节点列表中。

我之前已经在 domDocument 上使用了一个扩展类,因此创建一种可以接受 contextNode 的 getElementsByTagName 非常简单。

<?php
class SmartDocument extends DOMDocument {
private
$localDom;
public
$xpath;
private
$serialize = array('localDom');

private
$elemName;
private
$elemCounter;

/**
* 构造函数
*/
function __construct() {
parent::__construct ( '1.0', 'UTF-8' );
$this->preserveWhiteSpace = false;
$this->recover = TRUE;
$this->xpath = new DOMXpath ( $this );
}

/**
* 在 contextNode 内获取 GetElementsByTagname
*
* @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)) {
//现在,当你在这个循环中创建新的节点作为此节点的子节点或后续同级节点时
//它们会出现在此循环中
}

}

?>
Marco Maranao
14 年前
以下代码从 XML 文件(或 RSS 订阅源)中获取新闻列表,首先将其分配给一个名称值对数组,然后生成一个 HTML 列表。

<?php

$xml
=<<<EOT
<?xml version="1.0" encoding="ISO-8859-1"?>
<news>
<item>
<title>新闻 1</title>
<created>2010年4月2日 08:00 EST</created>
<url>http://news.example.com/news.pdf</url>
</item>
<item>
<title>新闻 2</title>
<created>2010年4月25日 08:00 EST</created>
<url>http://news.example.com/news.pdf</url>
</item>
<item>
<title>新闻 3</title>
<created>2010年4月27日 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>';
}
}

?>
[email protected]
12年前
这是一种使用 DOMDocument 类遍历 XML 节点和子节点的非常简单的方法。

<?php
$xml
='<?xml version="1.0" encoding="utf-8"?>
<root>
<Parent>
<child>子节点 1</child>
<child>子节点 2</child>
<child>子节点 3</child>
<subParent>
<Grandchild>孙节点 1</Grandchild>
<Grandchild>孙节点 2</Grandchild>
<Grandchild>孙节点 3</Grandchild>
</subParent>
</Parent>
<Parent>
<child>子节点 4</child>
<child>子节点 5</child>
<child>子节点 6</child>
<subParent>
<Grandchild>孙节点 4</Grandchild>
<Grandchild>孙节点 5</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++;
}
?>
查找节点的值
17年前
我不知道这是否显而易见,但对我来说并非如此,因此除了 [email protected] 的帖子外
添加

<?php
echo $param -> nodeValue.'<br>';
?>

到循环中将输出
值1
值2
值3
[email protected]
11年前
这是一个将填充的 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);
}
?>
[email protected]
14 年前
我第一次尝试使用此函数获得稳定的解决方案,但遇到了以下异常:

"致命错误:调用未定义的方法 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 版本。
james
14 年前
问题
您有一个 XML 文档,其中包含对例如图像的文件名引用。每个文件名引用由 <file>filename.ext</file> 标签定义。您希望执行其他验证,例如,在通过 XSD 验证运行 XML 文档后。其他验证可以是您选择的任何内容,在本例中,将其转换为 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->prevservWhiteSpace = 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 代码可以轻松地转换为一个函数,该函数返回一个图像文件名数组、找到的图像数量的整数值等。

希望这有帮助。
jason at shaped dot ca
16 年前
回复 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;
}
}
Mateusz K
4 年前
如果您想将所有节点移动到另一个标签,您可以这样做
例如:用节点替换 <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');
To Top