如何解析HTML/XML并从中提取信息?
当前回答
注意,这个答案推荐了那些已经废弃了10多年的图书馆。
phpQuery和QueryPath在复制流畅的jQuery API方面非常相似。这也是为什么它们是在PHP中正确解析HTML的两种最简单的方法。
QueryPath示例
基本上,您首先从HTML字符串创建一个可查询的DOM树:
$qp = qp("<html><body><h1>title</h1>..."); // or give filename or URL
结果对象包含HTML文档的完整树表示。可以使用DOM方法遍历它。但常见的方法是使用jQuery中的CSS选择器:
$qp->find("div.classname")->children()->...;
foreach ($qp->find("p img") as $img) {
print qp($img)->attr("src");
}
大多数情况下,您希望为->find()使用简单的#id和.class或DIV标记选择器。但您也可以使用XPath语句,这有时会更快。另外,典型的jQuery方法,如->children()和->text(),特别是->attr(),简化了提取正确的HTML片段。(并且已经解码了它们的SGML实体。)
$qp->xpath("//div/p[1]"); // get first paragraph in a div
QueryPath还允许将新标记注入流(->追加),然后输出并美化更新的文档(->写入HTML)。它不仅可以解析格式错误的HTML,还可以解析各种XML方言(带有名称空间),甚至可以从HTML微格式(XFN、vCard)中提取数据。
$qp->find("a[target=_blank]")->toggleClass("usability-blunder");
.
phpQuery还是QueryPath?
一般来说,QueryPath更适合处理文档。而phpQuery还实现了一些伪AJAX方法(仅HTTP请求),以更接近于jQuery。据称,phpQuery通常比QueryPath更快(因为总体特性较少)。
有关差异的更多信息,请参阅tagbyte.org上的wayback机器上的比较
优势
简单可靠易于使用的替代方案->查找(“a img,a object,div a”)正确的数据取消捕获(与正则表达式grepping相比)
其他回答
我创建了一个名为HTML5DOMDocument的库,可以在https://github.com/ivopetkov/html5-dom-document-php
它还支持查询选择器,我认为这在您的情况下非常有用。下面是一些示例代码:
$dom = new IvoPetkov\HTML5DOMDocument();
$dom->loadHTML('<!DOCTYPE html><html><body><h1>Hello</h1><div class="content">This is some text</div></body></html>');
echo $dom->querySelector('h1')->innerHTML;
处理HTML/XML DOM的方法有很多,其中大多数已经提到。因此,我不会亲自列出这些。
我只想补充一点,我个人更喜欢使用DOM扩展,以及为什么:
iit充分利用了底层C代码的性能优势它是OO PHP(并允许我对其进行子类化)它的级别相当低(这允许我将其用作更高级行为的非臃肿基础)它提供对DOM的每个部分的访问(不像SimpleXml,它忽略了一些鲜为人知的XML特性)它具有用于DOM爬行的语法,与原生Javascript中使用的语法类似。
虽然我怀念为DOMDocument使用CSS选择器的能力,但有一种非常简单和方便的方法可以添加此功能:将DOMDocument子类化,并将类似于querySelectorAll和querySelector的JS方法添加到子类中。
为了解析选择器,我建议使用Symfony框架中的非常简约的CsSelector组件。该组件只是将CSS选择器转换为XPath选择器,然后可以将其输入到DOMX路径中以检索相应的Nodelist。
然后,您可以使用这个(仍然是非常低级的)子类作为更高级类的基础,例如解析非常特定的XML类型或添加更多类似jQuery的行为。
下面的代码直接来自我的DOM查询库,并使用了我描述的技术。
对于HTML分析:
namespace PowerTools;
use \Symfony\Component\CssSelector\CssSelector as CssSelector;
class DOM_Document extends \DOMDocument {
public function __construct($data = false, $doctype = 'html', $encoding = 'UTF-8', $version = '1.0') {
parent::__construct($version, $encoding);
if ($doctype && $doctype === 'html') {
@$this->loadHTML($data);
} else {
@$this->loadXML($data);
}
}
public function querySelectorAll($selector, $contextnode = null) {
if (isset($this->doctype->name) && $this->doctype->name == 'html') {
CssSelector::enableHtmlExtension();
} else {
CssSelector::disableHtmlExtension();
}
$xpath = new \DOMXpath($this);
return $xpath->query(CssSelector::toXPath($selector, 'descendant::'), $contextnode);
}
[...]
public function loadHTMLFile($filename, $options = 0) {
$this->loadHTML(file_get_contents($filename), $options);
}
public function loadHTML($source, $options = 0) {
if ($source && $source != '') {
$data = trim($source);
$html5 = new HTML5(array('targetDocument' => $this, 'disableHtmlNsInDom' => true));
$data_start = mb_substr($data, 0, 10);
if (strpos($data_start, '<!DOCTYPE ') === 0 || strpos($data_start, '<html>') === 0) {
$html5->loadHTML($data);
} else {
@$this->loadHTML('<!DOCTYPE html><html><head><meta charset="' . $encoding . '" /></head><body></body></html>');
$t = $html5->loadHTMLFragment($data);
$docbody = $this->getElementsByTagName('body')->item(0);
while ($t->hasChildNodes()) {
$docbody->appendChild($t->firstChild);
}
}
}
}
[...]
}
另请参见Symfony的创建者Fabien Potencier关于为Symfony创建CsSelector组件的决定以及如何使用它的CSS选择器解析XML文档。
不使用正则表达式解析HTML有几个原因。但是,如果您完全控制将生成什么HTML,那么可以使用简单的正则表达式。
上面是一个通过正则表达式解析HTML的函数。请注意,此函数非常敏感,要求HTML遵守某些规则,但它在许多场景中都非常有效。如果您想要一个简单的解析器,并且不想安装库,请尝试一下:
function array_combine_($keys, $values) {
$result = array();
foreach ($keys as $i => $k) {
$result[$k][] = $values[$i];
}
array_walk($result, create_function('&$v', '$v = (count($v) == 1)? array_pop($v): $v;'));
return $result;
}
function extract_data($str) {
return (is_array($str))
? array_map('extract_data', $str)
: ((!preg_match_all('#<([A-Za-z0-9_]*)[^>]*>(.*?)</\1>#s', $str, $matches))
? $str
: array_map(('extract_data'), array_combine_($matches[1], $matches[2])));
}
print_r(extract_data(file_get_contents("http://www.google.com/")));
您可以尝试使用类似HTMLTidy的东西来清理任何“损坏”的HTML,并将HTML转换为XHTML,然后可以使用XML解析器解析。
JSON和来自XML的数组分为三行:
$xml = simplexml_load_string($xml_string);
$json = json_encode($xml);
$array = json_decode($json,TRUE);
大大!