如果您能够获得DOMDocument
代表你的 HTML 的对象,那么你只需要递归地遍历它并构建你想要的数据结构。
将您的 HTML 文档转换为DOMDocument
应该像这样简单:
function html_to_obj($html) {
$dom = new DOMDocument();
$dom->loadHTML($html);
return element_to_obj($dom->documentElement);
}
然后简单遍历一下$dom->documentElement
这给出了您所描述的结构可能如下所示:
function element_to_obj($element) {
$obj = array( "tag" => $element->tagName );
foreach ($element->attributes as $attribute) {
$obj[$attribute->name] = $attribute->value;
}
foreach ($element->childNodes as $subElement) {
if ($subElement->nodeType == XML_TEXT_NODE) {
$obj["html"] = $subElement->wholeText;
}
else {
$obj["children"][] = element_to_obj($subElement);
}
}
return $obj;
}
测试用例
$html = <<<EOF
<!DOCTYPE html>
<html lang="en">
<head>
<title> This is a test </title>
</head>
<body>
<h1> Is this working? </h1>
<ul>
<li> Yes </li>
<li> No </li>
</ul>
</body>
</html>
EOF;
header("Content-Type: text/plain");
echo json_encode(html_to_obj($html), JSON_PRETTY_PRINT);
Output
{
"tag": "html",
"lang": "en",
"children": [
{
"tag": "head",
"children": [
{
"tag": "title",
"html": " This is a test "
}
]
},
{
"tag": "body",
"html": " \n ",
"children": [
{
"tag": "h1",
"html": " Is this working? "
},
{
"tag": "ul",
"children": [
{
"tag": "li",
"html": " Yes "
},
{
"tag": "li",
"html": " No "
}
],
"html": "\n "
}
]
}
]
}
回答更新的问题
上面提出的解决方案不适用于<script>
元素,因为它不被解析为DOMText
,但作为DOMCharacterData
目的。这是因为 PHP 中的 DOM 扩展是基于libxml2,它将您的 HTML 解析为 HTML 4.0,并且在 HTML 4.0 中,内容为<script>
属于类型CDATA
并不是#PCDATA
.
对于这个问题你有两种解决方案。
简单但不是很稳健的解决方案是添加LIBXML_NOCDATA
标记为DOMDocument::loadHTML.(我实际上并不能 100% 确定这是否适用于 HTML 解析器。)
更困难但在我看来更好的解决方案是在测试时添加额外的测试$subElement->nodeType
在递归之前。递归函数将变为:
function element_to_obj($element) {
echo $element->tagName, "\n";
$obj = array( "tag" => $element->tagName );
foreach ($element->attributes as $attribute) {
$obj[$attribute->name] = $attribute->value;
}
foreach ($element->childNodes as $subElement) {
if ($subElement->nodeType == XML_TEXT_NODE) {
$obj["html"] = $subElement->wholeText;
}
elseif ($subElement->nodeType == XML_CDATA_SECTION_NODE) {
$obj["html"] = $subElement->data;
}
else {
$obj["children"][] = element_to_obj($subElement);
}
}
return $obj;
}
如果您遇到此类错误,您应该做的第一件事就是检查节点的类型$subElement
是,因为存在许多其他可能性我的简短示例函数没有处理。
此外,您会注意到libxml2
必须修复 HTML 中的错误才能为其构建 DOM。这就是为什么一个<html>
and a <head>
即使您没有指定元素,它们也会出现。您可以通过使用来避免这种情况LIBXML_HTML_NOIMPLIED
flag.
带脚本的测试用例
$html = <<<EOF
<script type="text/javascript">
alert('hi');
</script>
EOF;
header("Content-Type: text/plain");
echo json_encode(html_to_obj($html), JSON_PRETTY_PRINT);
Output
{
"tag": "html",
"children": [
{
"tag": "head",
"children": [
{
"tag": "script",
"type": "text\/javascript",
"html": "\n alert('hi');\n "
}
]
}
]
}