如何解析HTML / XML并从中提取信息?
如何解析HTML / XML并从中提取信息?
Answers:
我更喜欢使用本机XML扩展之一,因为它们与PHP捆绑在一起,通常比所有第3方库都快,并为我提供了所需的所有标记控制权。
DOM扩展使您可以使用PHP 5通过DOM API通过XML文档进行操作。它是W3C的Document Object Model Core Level 3的实现,它是一种平台和语言无关的接口,允许程序和脚本动态访问和更新。文件的内容,结构和样式。
DOM能够解析和修改现实世界(损坏的)HTML,并且可以执行XPath查询。它基于libxml。
使用DOM需要花一些时间,但是IMO值得花时间。由于DOM是与语言无关的接口,因此您会找到许多语言的实现,因此,如果需要更改编程语言,那么您很可能已经知道如何使用该语言的DOM API。
在Grabbing A元素的href属性中可以找到一个基本的用法示例,在php的DOMDocument中可以找到一个基本的概念概述。
StackOverflow上已广泛讨论了如何使用DOM扩展,因此,如果您选择使用它,则可以确保可以通过搜索/浏览Stack Overflow来解决所遇到的大多数问题。
XMLReader扩展是XML提取解析器。阅读器充当光标,在文档流上前进并在途中的每个节点处停止。
与DOM一样,XMLReader也基于libxml。我不知道如何触发HTML解析器模块,因此使用XMLReader解析损坏的HTML的机会可能不如使用DOM健壮,因为您可以明确地告诉它使用libxml的HTML解析器模块。
在使用php从h1标签获取所有值时,可以找到一个基本的用法示例
此扩展使您可以创建XML解析器,然后为不同的XML事件定义处理程序。每个XML解析器还具有一些您可以调整的参数。
XML解析器库也基于libxml,并实现了SAX样式的XML推送解析器。与DOM或SimpleXML相比,内存管理可能是更好的选择,但与XMLReader实现的请求解析器相比,使用起来更加困难。
SimpleXML扩展提供了一个非常简单易用的工具集,可以将XML转换为可以使用常规属性选择器和数组迭代器处理的对象。
当您知道HTML是有效的XHTML时,可以选择SimpleXML。如果您需要解析损坏的HTML,甚至不用考虑SimpleXml,因为它会阻塞。
一个基本的用法示例可以在到CRUD节点和xml文件的节点值的简单程序中找到,并且在PHP手册中还有很多其他示例。
如果您更喜欢使用第三方库,则建议使用实际上在下面使用DOM / libxml的库,而不是字符串解析。
FluentDOM为PHP中的DOMDocument提供了类似jQuery的Fluent XML接口。选择器以XPath或CSS编写(使用CSS到XPath转换器)。当前版本扩展了DOM的实现标准接口,并增加了DOM Living Standard的功能。FluentDOM可以加载JSON,CSV,JsonML,RabbitFish等格式。可以通过Composer安装。
Wa72 \ HtmlPageDom是一个PHP库,可以使用它轻松处理HTML文档。它需要Symfony2组件中的DomCrawler遍历DOM树,并通过添加用于处理HTML文档DOM树的方法来扩展它。
phpQuery是服务器端可链接的CSS3选择器驱动的文档对象模型(DOM)API,基于PHP5编写的jQuery JavaScript库,并提供其他命令行界面(CLI)。
另请参阅:https : //github.com/electrolinux/phpquery
Zend_Dom提供了用于处理DOM文档和结构的工具。当前,我们提供Zend_Dom_Query,它提供了一个统一的接口,用于同时使用XPath和CSS选择器查询DOM文档。
QueryPath是一个用于处理XML和HTML的PHP库。它不仅可以与本地文件一起使用,还可以与Web服务和数据库资源一起使用。它实现了许多jQuery接口(包括CSS样式的选择器),但为服务器端使用做了很大的调整。可以通过Composer安装。
fDOMDocument扩展了标准DOM以在所有错误情况下都使用异常,而不是PHP警告或通知。他们还添加了各种自定义方法和快捷方式,以方便使用并简化DOM的使用。
sabre / xml是一个包装和扩展XMLReader和XMLWriter类的库,以创建简单的“从XML到对象/数组”的映射系统和设计模式。写入和读取XML是单次通过的,因此可以快速进行,并且在大型xml文件上需要较少的内存。
FluidXML是一个PHP库,用于使用简洁流畅的API来处理XML。它利用XPath和流畅的编程模式来使游戏变得有趣而有效。
在DOM / libxml上构建的好处是,由于您基于本机扩展,因此可以立即获得良好的性能。但是,并非所有第3方库都遵循这条路线。下面列出其中一些
- 用PHP5 +编写的HTML DOM解析器使您可以非常简单地操作HTML!
- 需要PHP 5+。
- 支持无效的HTML。
- 使用jQuery之类的选择器在HTML页面上查找标签。
- 从HTML中提取内容。
我通常不建议使用此解析器。代码库太可怕了,解析器本身也很慢并且占用大量内存。并非所有的jQuery选择器(例如子选择器)都是可行的。任何基于libxml的库都应轻而易举地胜过它。
PHPHtmlParser是一个简单,灵活的html解析器,它使您可以使用任何CSS选择器(如jQuery)来选择标签。目标是协助开发工具,这些工具需要快速,简便的方式来废弃html,无论它是否有效!该项目最初由sunra / php-simple-html-dom-parser支持,但支持似乎已停止,因此该项目是我对他先前工作的改编。
同样,我不建议使用此解析器。CPU使用率很高时,速度相当慢。也没有清除创建的DOM对象的内存的功能。这些问题在嵌套循环中尤为严重。文档本身不准确且拼写错误,自16年4月14日以来未对修复程序做出任何回应。
- 通用标记器和HTML / XML / RSS DOM解析器
- 能够操纵元素及其属性
- 支持无效的HTML和UTF8
- 可以对元素执行类似CSS3的高级查询(例如jQuery-支持名称空间)
- HTML美化工具(例如HTML Tidy)
- 缩小CSS和Javascript
- 排序属性,更改字符大小写,正确缩进等
- 可扩展的
- 使用基于当前字符/令牌的回调解析文档
- 将操作分成较小的功能,以便于覆盖
- 快速简便
没用过。无法判断是否有好处。
您可以使用以上内容来解析HTML5,但是由于HTML5允许的标记,因此可能会有一些怪癖。因此,对于HTML5,您需要考虑使用专用的解析器,例如
基于WHATWG HTML5规范的HTML解析器的Python和PHP实现,以实现与主要桌面Web浏览器的最大兼容性。
HTML5完成后,我们可能会看到更多专用的解析器。W3上还有一个博客文章,标题为How-To for html 5解析,值得一试。
如果您不想编程PHP,也可以使用Web服务。通常,我发现这些工具的实用性很少,但这只是我和我的用例。
ScraperWiki的外部界面使您可以提取想要在Web上或在自己的应用程序中使用的形式的数据。您还可以提取有关任何刮板状态的信息。
最后也是最不推荐的做法是,您可以使用正则表达式从HTML提取数据。通常,不建议在HTML上使用正则表达式。
您可以在网上找到与标记匹配的大多数代码片段都很脆弱。在大多数情况下,它们仅适用于非常特殊的HTML。微小的标记更改(例如在某处添加空格,在标签中添加或更改标签中的属性)会使RegEx在编写不正确时失败。在HTML上使用RegEx之前,您应该知道自己在做什么。
HTML解析器已经知道HTML的语法规则。必须为您编写的每个新RegEx教授正则表达式。RegEx在某些情况下还可以,但实际上取决于您的用例。
您可以编写更可靠的解析器,但是当上述库已经存在并且在此方面做得更好时,编写具有正则表达式的完整且可靠的自定义解析器会浪费时间。
另请参阅解析Cthulhu方式的HTML
如果你想花一些钱,看看
我不隶属于PHP Architect或作者。
// Create DOM from URL or file
$html = file_get_html('http://www.example.com/');
// Find all images
foreach($html->find('img') as $element)
echo $element->src . '<br>';
// Find all links
foreach($html->find('a') as $element)
echo $element->href . '<br>';
// Create DOM from string
$html = str_get_html('<div id="hello">Hello</div><div id="world">World</div>');
$html->find('div', 1)->class = 'bar';
$html->find('div[id=hello]', 0)->innertext = 'foo';
echo $html;
// Dump contents (without tags) from HTML
echo file_get_html('http://www.google.com/')->plaintext;
// Create DOM from URL
$html = file_get_html('http://slashdot.org/');
// Find all article blocks
foreach($html->find('div.article') as $article) {
$item['title'] = $article->find('div.title', 0)->plaintext;
$item['intro'] = $article->find('div.intro', 0)->plaintext;
$item['details'] = $article->find('div.details', 0)->plaintext;
$articles[] = $item;
}
print_r($articles);
只需使用DOMDocument-> loadHTML()即可完成。libxml的HTML解析算法非常好且快速,并且与流行的看法相反,它不会阻止格式错误的HTML。
为什么不应该以及何时使用正则表达式?
首先,一个常见的误称:Regexps不是用于“ 解析 ” HTML。但是,正则表达式可以“ 提取 ”数据。提取是他们的目的。与适当的SGML工具包或基准XML解析器相比,正则表达式HTML提取的主要缺点是其语法工作和可靠性不同。
考虑制作一个可靠的HTML提取正则表达式:
<a\s+class="?playbutton\d?[^>]+id="(\d+)".+? <a\s+class="[\w\s]*title
[\w\s]*"[^>]+href="(http://[^">]+)"[^>]*>([^<>]+)</a>.+?
比简单的phpQuery或QueryPath可读性更差:
$div->find(".stationcool a")->attr("title");
但是,在特定的用例中它们可以提供帮助。
<!--
,但是HTML注释有时是提取更为有用的锚点。特别是伪HTML变体<$var>
或SGML残差很容易与正则表达式配合。有时甚至建议使用正则表达式预先提取HTML片段,/<!--CONTENT-->(.+?)<!--END-->/
然后使用更简单的HTML解析器前端处理其余部分。
注意:实际上,我有这个应用程序,在这里我可以选择使用XML解析和正则表达式。就在上周,PyQuery解析中断,而正则表达式仍然有效。是的,很奇怪,我自己也无法解释。但是,它发生了。
因此,请勿仅仅因为它与regex = evil meme不匹配而否决现实世界中的考虑因素。但是,我们也不要对此投票太多。这只是该主题的旁注。
DOMComment
可以阅读评论,因此没有理由为此使用Regex。
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");
}
通常,您想为使用简单的#id
和.class
或DIV
标记选择器->find()
。但是您也可以使用XPath语句,该语句有时会更快。还典型jQuery方法等->children()
和->text()
特别->attr()
简化提取正确的HTML片段。(并且已经对其SGML实体进行了解码。)
$qp->xpath("//div/p[1]"); // get first paragraph in a div
QueryPath还允许将新标签注入流(->append
),然后输出并美化更新的文档(->writeHTML
)。它不仅可以解析格式错误的HTML,还可以解析各种XML方言(带有名称空间),甚至可以从HTML微格式(XFN,vCard)提取数据。
$qp->find("a[target=_blank]")->toggleClass("usability-blunder");
。
phpQuery或QueryPath?
通常,QueryPath更适合于文档处理。尽管phpQuery还实现了一些伪AJAX方法(仅HTTP请求),以使其更类似于jQuery。据说phpQuery通常比QueryPath更快(因为总体功能较少)。
有关差异的更多信息,请参见tagbyte.org在回溯计算机上进行的比较。(原始资料丢失了,因此这里是一个Internet存档链接。是的,您仍然可以找到丢失的页面,人们。)
优点
->find("a img, a object, div a")
简单的HTML DOM是一个很棒的开源解析器:
它以面向对象的方式处理DOM元素,并且新的迭代涵盖了很多不兼容的代码。还有一些很棒的功能,就像您在JavaScript中看到的一样,例如“ find”功能,该功能将返回该标记名称的所有元素实例。
我已经在许多工具中使用了此工具,并在许多不同类型的网页上对其进行了测试,并且我认为它的效果很好。
我这里没有提到的一种通用方法是通过Tidy运行HTML ,可以将其设置为吐出保证有效的XHTML。然后,您可以在其上使用任何旧的XML库。
但是对于您的特定问题,您应该看一下这个项目:http : //fivefilters.org/content-only/-它是Readability算法的修改版本,旨在仅提取文本内容(而不是标头)和页脚)。
对于1a和2:我将投票支持新的Symfony Componet类DOMCrawler(DomCrawler)。此类允许类似于CSS选择器的查询。看看这个演示文稿,看看其中的真实示例:news-of-the-symfony2-world。
该组件旨在独立运行,可以在不使用Symfony的情况下使用。
唯一的缺点是它仅适用于PHP 5.3或更高版本。
顺便说一句,这通常称为屏幕抓取。我用于此的库是Simple HTML Dom Parser。
我推荐PHP简单HTML DOM解析器。
它确实具有不错的功能,例如:
foreach($html->find('img') as $element)
echo $element->src . '<br>';
这听起来像是对W3C XPath技术的很好的任务描述。表达查询很容易,例如“返回嵌套在标记中的所有href
属性”。不是PHP爱好者,我不能告诉您XPath可用的形式。如果可以调用外部程序来处理HTML文件,则应该能够使用XPath的命令行版本。有关快速介绍,请参见http://en.wikipedia.org/wiki/XPath。img
<foo><bar><baz> elements
是的,您可以为此使用simple_html_dom。但是,我已经对simple_html_dom进行了大量工作,尤其是对于Web抓取,并且发现它过于脆弱。它可以完成基本工作,但我还是不会推荐它。
我从来没有使用过curl来达到目的,但是我了解到curl可以更有效地完成工作,并且更加坚固。
请查看此链接: scraping-websites-with-curl
QueryPath很好,但是请注意“跟踪状态”,因为如果您没有意识到这意味着什么,那可能意味着您浪费了大量的调试时间来试图找出发生了什么以及代码为什么不起作用。
这意味着对结果集的每次调用都会修改对象中的结果集,它不像jquery中那样可链接,在jquery中每个链接都是一个新集合,您只有一个集合,这是查询的结果,并且每个函数调用都进行修改那一套。
为了获得类似jquery的行为,您需要在执行类似filter / modify的操作之前进行分支,这意味着它将更紧密地反映jquery中发生的事情。
$results = qp("div p");
$forename = $results->find("input[name='forename']");
$results
现在包含的结果集input[name='forename']
不是原来的查询,"div p"
这让我大跌眼镜,我发现QueryPath跟踪筛选器和查找的内容以及修改结果并将其存储在对象中的所有内容。您需要这样做
$forename = $results->branch()->find("input[name='forname']")
然后$results
将不会被修改,您可以一次又一次地重用结果集,也许某个有更多知识的人可以稍微澄清一下,但是从我发现的结果来看,基本上就像这样。
Advanced Html Dom是提供相同接口的简单HTML DOM替换,但是它基于DOM,这意味着不会发生任何相关的内存问题。
它还具有完整的CSS支持,包括jQuery扩展。
我写了一个通用的XML解析器,可以轻松处理GB文件。它基于XMLReader,非常易于使用:
$source = new XmlExtractor("path/to/tag", "/path/to/file.xml");
foreach ($source as $tag) {
echo $tag->field1;
echo $tag->field2->subfield1;
}
这是github 仓库:XmlExtractor
我创建了一个名为 PHPPowertools / DOM-Query,该库使您可以像使用jQuery一样对HTML5和XML文档进行爬网。
在引擎盖下,它使用symfony / DomCrawler将CSS选择器转换为XPath选择器。即使将一个对象传递给另一个对象,它也始终使用相同的DomDocument来确保良好的性能。
namespace PowerTools;
// Get file content
$htmlcode = file_get_contents('https://github.com');
// Define your DOMCrawler based on file string
$H = new DOM_Query($htmlcode);
// Define your DOMCrawler based on an existing DOM_Query instance
$H = new DOM_Query($H->select('body'));
// Passing a string (CSS selector)
$s = $H->select('div.foo');
// Passing an element object (DOM Element)
$s = $H->select($documentBody);
// Passing a DOM Query object
$s = $H->select( $H->select('p + p'));
// Select the body tag
$body = $H->select('body');
// Combine different classes as one selector to get all site blocks
$siteblocks = $body->select('.site-header, .masthead, .site-body, .site-footer');
// Nest your methods just like you would with jQuery
$siteblocks->select('button')->add('span')->addClass('icon icon-printer');
// Use a lambda function to set the text of all site blocks
$siteblocks->text(function( $i, $val) {
return $i . " - " . $val->attr('class');
});
// Append the following HTML to all site blocks
$siteblocks->append('<div class="site-center"></div>');
// Use a descendant selector to select the site's footer
$sitefooter = $body->select('.site-footer > .site-center');
// Set some attributes for the site's footer
$sitefooter->attr(array('id' => 'aweeesome', 'data-val' => 'see'));
// Use a lambda function to set the attributes of all site blocks
$siteblocks->attr('data-val', function( $i, $val) {
return $i . " - " . $val->attr('class') . " - photo by Kelly Clark";
});
// Select the parent of the site's footer
$sitefooterparent = $sitefooter->parent();
// Remove the class of all i-tags within the site's footer's parent
$sitefooterparent->select('i')->removeAttr('class');
// Wrap the site's footer within two nex selectors
$sitefooter->wrap('<section><div class="footer-wrapper"></div></section>');
[...]
该库还包括其自己的用于PSR-0兼容库的零配置自动加载器。包含的示例无需任何其他配置即可直接使用。或者,您可以将其与作曲家一起使用。
您可以尝试使用类似HTML Tidy的方法来清理所有“损坏的” HTML,并将HTML转换为XHTML,然后可以使用XML解析器进行解析。
XML_HTMLSax
相当稳定-即使不再维护。另一种选择是通过HTML Tidy通过HTML传递HTML,然后使用标准XML工具对其进行解析。
有很多处理HTML / XML DOM的方法,其中大多数已经提到。因此,我不会尝试列出这些人。
我只想补充一点,我个人更喜欢使用DOM扩展,为什么:
而且,尽管我错过了使用CSS选择器的能力DOMDocument
,但是有一种相当简单方便的方法来添加此功能:子类化DOMDocument
并将类JS querySelectorAll
和querySelector
方法添加到子类中。
为了解析这些选择,我建议使用非常简约CssSelector组件从Symfony框架。该组件只是将CSS选择器转换为XPath选择器,然后可以将其输入到DOMXpath
以检索相应的Nodelist。
然后,您可以将此子类(仍为非常低的子类)用作更多高级类的基础,例如。解析非常特定类型的XML或添加更多类似jQuery的行为。
下面的代码直接给出了我的DOM-Query库,并使用了我描述的技术。
对于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决定使用CSS选择器解析XML文档,他决定为Symfony创建CssSelector组件以及如何使用它。
使用FluidXML,您可以使用XPath和CSS选择器查询和迭代XML 。
$doc = fluidxml('<html>...</html>');
$title = $doc->query('//head/title')[0]->nodeValue;
$doc->query('//body/p', 'div.active', '#bgId')
->each(function($i, $node) {
// $node is a DOMNode.
$tag = $node->nodeName;
$text = $node->nodeValue;
$class = $node->getAttribute('class');
});
来自XML的JSON和数组分为三行:
$xml = simplexml_load_string($xml_string);
$json = json_encode($xml);
$array = json_decode($json,TRUE);
da!
不使用正则表达式解析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/")));
我创建了一个名为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;
如果您熟悉jQuery选择器,则可以使用ScarletsQuery for PHP
<pre><?php
include "ScarletsQuery.php";
// Load the HTML content and parse it
$html = file_get_contents('https://www.lipsum.com');
$dom = Scarlets\Library\MarkupLanguage::parseText($html);
// Select meta tag on the HTML header
$description = $dom->selector('head meta[name="description"]')[0];
// Get 'content' attribute value from meta tag
print_r($description->attr('content'));
$description = $dom->selector('#Content p');
// Get element array
print_r($description->view);
该库通常需要不到1秒的时间来处理脱机html。
它还接受无效的HTML或标记属性上缺少引号。
解析xml的最佳方法:
$xml='http://www.example.com/rss.xml';
$rss = simplexml_load_string($xml);
$i = 0;
foreach ($rss->channel->item as $feedItem) {
$i++;
echo $title=$feedItem->title;
echo '<br>';
echo $link=$feedItem->link;
echo '<br>';
if($feedItem->description !='') {
$des=$feedItem->description;
} else {
$des='';
}
echo $des;
echo '<br>';
if($i>5) break;
}