DOMDocument::loadHTML

(PHP 5, PHP 7, PHP 8)

DOMDocument::loadHTML从字符串加载 HTML

说明

public DOMDocument::loadHTML(string $source, int $options = 0): bool

该函数解析字符串 source 中包含的 HTML。与加载 XML 不同,HTML 不必格式良好才能加载。

警告

此函数使用 HTML 4 解析器解析输入。现代 Web 浏览器使用的 HTML 5 的解析规则不同。根据输入,这可能会导致不同的 DOM 结构。因此,此函数不能安全地用于清理 HTML。

解析 HTML 时的行为可能取决于所使用的 libxml 版本,尤其是在边缘情况和错误处理方面。对于符合 HTML5 规范的解析,请使用 PHP 8.4 中添加的 Dom\HTMLDocument::createFromString()Dom\HTMLDocument::createFromFile()

例如,某些 HTML 元素在遇到时会隐式关闭父元素。自动关闭父元素的规则在 HTML 4 和 HTML 5 之间有所不同,因此 DOMDocument 所看到的 DOM 结构可能与 Web 浏览器所看到的 DOM 结构不同,这可能使攻击者能够破坏生成的 HTML。

参数

source

HTML 字符串。

options

按位 ORlibxml 选项常量

返回值

成功时返回 true,失败时返回 false

错误/异常

如果将空字符串作为 source 传递,则会生成警告。此警告不是由 libxml 生成的,无法使用 libxml 的错误处理函数处理。

虽然格式错误的 HTML 应该成功加载,但此函数在遇到错误标记时可能会生成 E_WARNING 错误。可以使用 libxml 的错误处理函数 来处理这些错误。

变更日志

版本 说明
8.3.0 此函数现在具有暂定的 bool 返回类型。
8.0.0 静态调用此函数现在将抛出 Error。以前,会引发 E_DEPRECATED

范例

示例 #1 创建文档

<?php
$doc
= new DOMDocument();
$doc->loadHTML("<html><body>Test<br></body></html>");
echo
$doc->saveHTML();
?>

参见

添加笔记

用户贡献的笔记 19 个笔记

138
mdmitry at gmail dot com
14 年前
您也可以使用以下简单技巧将 HTML 加载为 UTF-8

<?php

$doc
= new DOMDocument();
$doc->loadHTML('<?xml encoding="UTF-8">' . $html);

// 脏修复
foreach ($doc->childNodes as $item)
if (
$item->nodeType == XML_PI_NODE)
$doc->removeChild($item); // 删除修复
$doc->encoding = 'UTF-8'; // 插入正确

?>
65
Shane Harter
14 年前
DOMDocument 非常擅长处理不完美的标记,但它在处理时会到处抛出警告。

这里没有很好地记录这一点。解决这个问题的方法是实现一个单独的装置来处理这些错误。

在调用 loadHTML 之前设置 libxml_use_internal_errors(true)。这将阻止错误冒泡到您的默认错误处理程序。然后您可以使用其他 libxml 错误函数获取它们(如果您需要)。

您可以在此处找到更多信息 https://php.net/manual/en/ref.libxml.php
62
hanhvansu at yahoo dot com
17 年前
在使用 loadHTML() 处理 UTF-8 页面时,您可能会遇到 DOM 函数的输出与输入不一致的问题。例如,如果您想获取“Cạnh tranh”,您将收到“Cạnh tranh”。我建议我们在加载 UTF-8 页面之前使用 mb_convert_encoding
<?php
$pageDom
= new DomDocument();
$searchPage = mb_convert_encoding($htmlUTF8Page, 'HTML-ENTITIES', "UTF-8");
@
$pageDom->loadHTML($searchPage);

?>
5
obayed dot opu at gmail dot com
2 年前
要支持 HTML5,您必须通过将 `LIBXML_NOERROR` 作为 loadHTML 方法的选项来禁用 xml 错误处理。

示例

<?php
$doc
= new DOMDocument();
$doc->loadHTML("<html><body>Test<br><section>I'M UNSUPPORTED</section></body></html>", LIBXML_NOERROR);
echo
$doc->saveHTML();
?>
16
bigtree at DONTSPAM dot 29a dot nl
19 年前
注意加载字符集与 iso-8859-1 不同的 html。由于此方法不会主动尝试找出您尝试加载的 html 的编码方式(就像大多数浏览器一样),您必须在 html head 中指定它。例如,如果您的 html 是 utf-8,请确保您的 html 的 head 部分中有一个 meta 标签

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
</head>

如果您没有像这样指定字符集,所有高 ASCII 字节将被 html 编码。将您正在加载 html 的 dom 文档设置为 UTF-8 是不够的。
4
deepakrajpal dot com at gmail dot com
3 年前
如果我们加载 <section>、<svg> 等 html5 标签,则会出现以下错误

DOMDocument::loadHTML(): Tag section invalid in Entity

我们可以在 loadHTML(); 之前使用 libxml_use_internal_errors(true); 来禁用标准 libxml 错误(并启用用户错误处理)。

这在 phpunit 自定义断言中非常有用,如以下示例所示(如果使用 phpunit 测试用例)

// 创建 DOMDocument
$dom = new DOMDocument();

// 修复 html5/svg 错误
libxml_use_internal_errors(true);

// 加载 html
$dom->loadHTML("<section></section>");
$htmlNodes = $dom->getElementsByTagName('section');

if ($htmlNodes->length == 0) {
$this->assertFalse(TRUE);
} else {
$this->assertTrue(TRUE);
}
10
finkenb2 at mail dot lib dot msu dot edu
8 年前
警告:此方法对于 HTML5 元素(如 SVG)的处理效果不佳。大多数 Web 上的建议是关闭错误以使其与 HTML5 兼容。
7
fr at felix-riesterer dot de
8 年前
请记住:如果您使用 HTML5 文档类型和以下元元素

<meta charset=utf-8">

您的 HTML 代码将被解释为 ISO-8859-something,并且非 ASCII 字符将被转换为 HTML 实体。但是,HTML4 风格的版本将起作用(正如 10 年前“bigtree at 29a”所指出的那样)

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
13
cake at brothercake dot com
11 年前
请注意,此函数实际上并不理解 HTML - 它使用 SGML 的通用规则来修复标签汤输入,因此它创建了格式良好的标记,但不知道哪些元素上下文是允许的。

例如,对于以下输入,第一个元素没有关闭

<span>hello <div>world</div>

loadHTML 将将其更改为以下格式良好的但无效的格式

<span>hello <div>world</div></span>
13
Errol
15 年前
应该注意的是,当任何文本在 body 标签内提供时
在包含元素之外,DOMDocument 将封装该
文本到段落标签 (<p>) 中。

例如
<?php
$doc
= new DOMDocument();
$doc->loadHTML("<html><body>Test<br><div>Text</div></body></html>");
echo
$doc->saveHTML();
?>

将产生
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<p>Test<br></p>
<div>Text</div>
</body></html>


<?php
$doc
= new DOMDocument();
$doc->loadHTML(
"<html><body><i>Test</i><br><div>Text</div></body></html>");
echo
$doc->saveHTML();
?>

将产生
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"
"http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body>
<i>Test</i><br><div>Text</div>
</body></html>
5
romain dot lalaut at laposte dot net
17 年前
请注意,即使使用 <html xmlns="http://www.w3.org/1999/xhtml">,此类文档的元素也没有命名空间
5
kerim-yagmurcu at gmx dot de
7 年前
对于那些想要获取外部 URL 的类元素的人,我有 2 个有用的函数。在本例中,我们获取 '<h3 class="r">'
元素返回(搜索结果标题)来自谷歌搜索

1. 检查 URL(如果它可以访问,存在)
<?php
# URL 检查
function url_check($url) {
$headers = @get_headers($url);
return
is_array($headers) ? preg_match('/^HTTP\\/\\d+\\.\\d+\\s+2\\d\\d\\s+.*$/',$headers[0]) : false;
};
?>

2. 清理要获取的元素(删除所有标签、制表符、换行符等)
<?php
# 用于清理字符串的函数
function clean($text){
$clean = html_entity_decode(trim(str_replace(';','-',preg_replace('/\s+/S', " ", strip_tags($text)))));// 删除所有内容
return $clean;
echo
'\n';// 抛出一个新行
}
?>

完成之后,我们可以使用以下方法输出搜索结果标题
<?php
$searchstring
= 'djceejay';
$url = 'http://www.google.de/webhp#q='.$searchstring;
if(
url_check($url)){
$doc = new DomDocument;
$doc->validateOnParse = true;
$doc->loadHtml(file_get_contents($url));
$output = clean($doc->getElementByClass('r')->textContent);
echo
$output . '<br>';
}else{
echo
'URL 不可访问!';// 当 URL 不可调用时抛出消息
}
?>
4
jamesedwardcooke+php at gmail dot com
15 年前
使用 loadHTML() 会自动设置 DOMDocument 实例的 doctype 属性(设置为 html 中的 doctype,或默认为 4.0 Transitional)。如果您使用 DOMImplementation 设置 doctype,它将被覆盖。

我假设可以设置它,然后使用我定义的 doctype 加载 html(以便在运行时决定 doctype),并且在试图找出我的 doctype 去哪里时遇到了巨大的麻烦。希望这对其他人有所帮助。
3
Alex
14 年前
注意“陷阱”(按设计工作,但并非按预期工作):如果您使用 loadHTML,您无法验证文档。验证只适用于 XML。详细信息请参见:http://bugs.php.net/bug.php?id=43771&edit=1
4
xuanbn at yahoo dot com
16 年前
如果您使用 loadHTML() 处理 utf HTML 字符串(例如在越南语中),您可能会遇到结果为乱码文本,而某些文件则正常。即使您的 HTML 已经具有 meta charset,例如

<meta http-equiv="content-type" content="text/html; charset=utf-8">

我发现,为了帮助 loadHTML() 正确处理 utf 文件,meta 标签应该放在最前面,在任何 utf 字符串出现之前。例如,以下 HTML 文件

<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title> 越南语 - Tiếng Việt</title>
</head>
<body></body>
</html>

当 <meta> 标签出现在 <title> 标签之前时,将与 loadHTML() 兼容。

但是,以下文件将无法被 loadHTML() 识别,因为 <title> 标签包含的 utf 字符串出现在 <meta> 标签之前。

<html>
<head>
<title> 越南语 - Tiếng Việt</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
</head>
<body></body>
</html>
3
piopier
15 年前
以下是我编写的一个函数,它可以解决使用 loadHTML 和 DOM 函数时关于字符集问题(UTF-8...)的先前评论。
它在 <head> 之后添加 charset meta 标签,以改进自动编码检测,并将任何特定字符转换为 html 实体,因此 PHP DOM 函数/属性将返回正确的值。

<?php
mb_detect_order
("ASCII,UTF-8,ISO-8859-1,windows-1252,iso-8859-15");
function
loadNprepare($url,$encod='') {
$content = file_get_contents($url);
if (!empty(
$content)) {
if (empty(
$encod))
$encod = mb_detect_encoding($content);
$headpos = mb_strpos($content,'<head>');
if (
FALSE=== $headpos)
$headpos= mb_strpos($content,'<HEAD>');
if (
FALSE!== $headpos) {
$headpos+=6;
$content = mb_substr($content,0,$headpos) . '<meta http-equiv="Content-Type" content="text/html; charset='.$encod.'">' .mb_substr($content,$headpos);
}
$content=mb_convert_encoding($content, 'HTML-ENTITIES', $encod);
}
$dom = new DomDocument;
$res = $dom->loadHTML($content);
if (!
$res) return FALSE;
return
$dom;
}
?>

注意:它使用 mb_strpos/mb_substr 而不是 mb_ereg_replace,因为在处理大型 html 页面时,这似乎效率更高。
2
Anonymous
2 年前
如果 html 包含一些从 HTML5 开始就被采用的标签(例如“nav、section、footer 等”,在 PHP 8.1.6 中),则 loadHTML() 和 loadHTMLFile() 可能会始终生成警告。

尝试运行以下代码。

<?php

$file_name
= 'PHP Runtime Configuration - Manual.html'; // 事先从 "https://php.net/manual/en/session.configuration.php" 下载此文件。

$doc = new DOMDocument();
$doc->loadHTMLFile($file_name); // 如果将 "LIBXML_NOERROR" 设置为第二个参数,则不会出现错误
echo $doc->saveHTML();

// 警告:DOMDocument::loadHTMLFile(): 标签 nav 在 PHP Runtime Configuration - Manual.html 中无效,行:63 在 D:\xampp\htdocs\test\xml(dom)\loadHTML\index.php 中的第 6 行

?>
2
BychkovVV at mail dot ru
4 年前
如果您要从任何网站加载以 "utf-8" 编码的 html 内容,并且 meta width content-type 不是 HEAD 的第一个子节点,则解析器(编码)将不会识别它; 因此您可以进行此修复
function domLoadHTML($html)
{$testDOM = new DOMDocument('1.0', 'UTF-8');
$testDOM->loadHTML($html);
$charset = NULL;
$searchInElemnt = function(&$item) use (&$searchInElemnt, &$charset)
{if($item->childNodes)
{foreach($item->childNodes as $childItem)
{switch($childItem->nodeName)
{case 'html'
case 'head'
$searchInElemnt($childItem);
break;
case 'meta'
$attributes = array();
foreach ($childItem->attributes as $attr)
{$attributes[mb_strtoupper($attr->localName)] = $attr->nodeValue;
}
if(array_key_exists('HTTP-EQUIV', $attributes) && (mb_strtoupper($attributes['HTTP-EQUIV']) == 'CONTENT-TYPE') && array_key_exists('CONTENT', $attributes) && preg_match('~[\s]*;[\s]*charset[\s]*=[\s]*([^\s]+)~', $attributes['CONTENT'], $matches))
{$charset = preg_replace('~[\s\']~', '', $matches[1]);
}
}
}
}
};
$searchInElemnt($testDOM);
if(isset($charset))
{$dom = new DOMDocument('1.0', $charset);
$dom->loadHTML('<?xml encoding="'.$charset.'">'.$html);
foreach ($dom->childNodes as $item)
if($item->nodeType == XML_PI_NODE)
{$dom->removeChild($item);
}
$dom->encoding = $charset;
}
else
{$dom = $testDOM;
}
return $dom;
};
0
divinity76+spam at gmail dot com
3 年前
如果您想摆脱所有“仅包含空白字符的 DOMText 元素”,可以尝试以下方法

<?php

function loadHTML_noemptywhitespace(string $html, int $extra_flags = 0, int $exclude_flags = 0): DOMDocument
{
$flags = LIBXML_HTML_NODEFDTD | LIBXML_NOBLANKS | LIBXML_NONET;
$flags = ($flags | $extra_flags) & ~ $exclude_flags;

$domd = new DOMDocument();
$domd->preserveWhiteSpace = false;
@
$domd->loadHTML('<?xml encoding="UTF-8">' . $html, $flags);
$removeAnnoyingWhitespaceTextNodes = function (\DOMNode $node) use (&$removeAnnoyingWhitespaceTextNodes): void {
if (
$node->hasChildNodes()) {
// 警告:以相反的顺序执行很重要; 如果您正向执行,DOMNodeList 的索引可能会失效;
// 这就是我不使用 foreach() 的原因 - 不要更改它(除非您知道自己在做什么,当然)
for ($i = $node->childNodes->length - 1; $i >= 0; --$i) {
$removeAnnoyingWhitespaceTextNodes($node->childNodes->item($i));
}
}
if (
$node->nodeType === XML_TEXT_NODE && !$node->hasChildNodes() && !$node->hasAttributes() && empty(trim($node->textContent))) {
//echo "Removing annoying POS";
// var_dump($node);
$node->parentNode->removeChild($node);
}
//elseif ($node instanceof DOMText) { echo "not removed"; var_dump($node, $node->hasChildNodes(), $node->hasAttributes(), trim($node->textContent)); }
};
$removeAnnoyingWhitespaceTextNodes($domd);
return
$domd;
}
To Top