运用the Simple HTML DOM Library实现HTML解析及内容抓取

类别:译文      发布日期:2010-06-08

如果你需要解析HTML,正则表达式不是一个太好的办法。在本教程中,将会向你讲述如何用一个开源的,简单易学的解析器从外部代码中来读取、修改及提取HTML。通过一个实例,你将学会如何获取并显示某个页面中已发布文章的列表。

第一步 准备工作

首先从sourceforge上下载 the simpleHTMLdom library。在已下载的文件中只有一个simple_html_dom.php 文件是我们所需要的,其余的都是例子跟文档。

第二步 解析的基础知识

the simpleHTMLdom library用起来比较容易,但是在进入实际操作前,先回顾一下基本知识。

载入HTML

$html = new simple_html_dom();

// Load from a string
$html->load('<html><body><p>Hello World!</p><p>We're here</p></body></html>');

// Load a file
$html->load_file('http://net.tutsplus.com/');

你可以通过一个字符串或者一个文件加载HTML来初始化一个对象。 加载文件可以通过URL或者本机路径来实现。

注意:load_file()方法把他的工作委派给了PHP的file_get_contents方法。如果在你的php.ini文件中没有将allow_url_fopen设置为true,将不能够打开一个远程的文件。这种情况下,需要调用CURL库来加载远程页面,并用load()方法读取。

访问信息

一旦建立了DOM对象,你就可以用find()方法开始工作并建立集合。一个集合是通过选择器得到的一组对象。语法与JQuery类似。

<html>
<body>
 <p>Hello World!</p>
 <p>We're Here.</p>
</body>
</html>

在本例中,我们将看到如何访问第二个段落的信息,修改它并输出结果。

# create and load the HTML
include('simple_html_dom.php');
$html = new simple_html_dom();
$html->load("<html><body><p>Hello World!</p><p>We're here</p></body></html>");

# get an element representing the second paragraph
$element = $html->find("p");

# modify it
$element[1]->innertext .= " and we're here to stay.";

# output it!
echo $html->save();

第2-4行:加载HTML

第7行:查找HTML中所有的标签,并且将他们作为一个数组返回。第一个段落的索引是0,随后的段落索引依次递增。

第10行:进入我们的段落集合的第二项(索引是1),并且在它的innertext属性中添加一些内容。Innertext呈现的是标签之间的内容(不含有标签),而outertext呈现的是包括标签的内容(含有标签)。

我们可以通过outertext将标签彻底地替换。

$element[1]->class = "class_name";
echo $html->save();

保存后的结果是:

<html>
<body>
 <p>Hello World!</p>
 <p>We're here and we're here to stay.</p>
</body>
</html>

其他的选择器

下面是一些其他的选择器。如果你曾经用过jQuery,看起来会觉得很熟悉。

# get the first occurrence of id="foo"
$single = $html->find('#foo', 0);

# get all elements with
$collection = $html->find('.foo');

# get all the anchor tags on a page
$collection = $html->find('a');

# get all anchor tags that are inside H1 tags
$collection = $html->find('h1 a');

# get all img tags with a title of 'himom'
$collection = $html->find('img[title=himom]');

第一个例子不是太直观——所有的查询即使是应该只返回一个单独结果的的ID查询,都默认地返回一个集合。但是,当指定第二个参数后,我们就可以说:只返回集合的第一项。

这意味着$single是一个单独的元素,而不是只有一个项目的元素数组。

文档

全部文档资料可以在项目文档页面中找到。

第三步 真实的例子

为了达到实践的目的,我们要写一个抓取Nettuts网站的脚本并且将文章的标题及摘要生成一个列表。这只是一个例子。如果没有得到授权,请不要随意抓取别人网站上的内容。

include('simple_html_dom.php');

$articles = array();
getArticles('http://net.tutsplus.com/page/76/');

首先引入库文件,然后调用getArticles方法,参数是要进行解析的页面。

定义一个全局数组来存放所有文章的信息。在开始解析之前,我们来看看Nettuts+中是如何描述文章摘要的。

<div>
 <!-- Post Taxonomies -->
 <div> ... </div>
 <!-- Post Title -->
 <h1><a>Title</a></h1>
 <!-- Post Meta -->
 <div> ... </div>
 <div><p>Description</p></div>
</div>

以上代码描述的是页面上一篇文章的基本格式,包括对代码的注释。为什么注释很重要?因为解析器把它当作节点。

第4步 开始解析

首先声明一个全局变量并建立一个新的simple_html_dom对象,然后加载要解析的页面。这个函数会在稍后的代码中调用自己,所以我们设置了一个参数用于接收URL。

function getArticles($page) {
 global $articles;
 $html = new simple_html_dom();
 $html->load_file($page);
 // ... more ...
}

第五步 查找我们想要的信息

$items = $html->find('div[class=preview]');  

foreach($items as $post) {
 # remember comments count as nodes
 $articles[] = array($post->children(3)->outertext,
 $post->children(6)->first_child()->outertext);
}

我们来详细了解一下:

第1行:通过class创建一个div元素的数组。$items存放的是文章的集合。

第5行:$post引用了一个单独的div.如果我们去看原始的HTML代码,会发现第三个节点是H1元素包含的文章标题。我们获取到它并把它放到$articles[index][0]中。

记住:节点的索引都是从零开始的。

第6行:$post的第六个节点是<div class=”text”>。我们想要得到里面的文本,因此我们抓取第一个子节点的outertext——包含段落标签。文章中的一条单独的记录如下所示:

$articles[0][0] = "My Article Name Here";
$articles[0][1] = "This is my article description"

第六步 分页

首先我们要做的是如何查找下一页。在Nettuts+中,很容易找出URL,但是我们要假设不容易查找出,然后通过解析找出下一页的链接。

如果我们看看HTML代码,会看到如下所示:

<a href="http://net.tutsplus.com/page/2/">»</a>

如果有下一页(当然存在只有一页的情况),我们要找到class="nextpostslink"的锚。现在信息可以利用了。

if($next = $html->find('a[class=nextpostslink]', 0)) {
 $URL = $next->href;

 $html->clear();
 unset($html);

 getArticles($URL);
}

第一行代码是为了查找是否有class="nextpostslink"的锚。需要特别主意的是find()方法的第二个参数。它表示我们只想从被查找到的并返回的集合中获得第一个元素(index=0)。$next中保存的是一个单独的元素,而不是一个元素的集合。

接下来,我们把链接的href属性值放到变量$URL中。这一步很重要因为我们即将销毁HTML对象。由于php5的一个循环引用内存泄露问题,在生成一个新的simple_html_dom对象之前,必须要将simple_html_dom object 对象清空。否则,会吃掉所有可用的内存。

最后,我们调用getArticles方法,并传一个下一页链接的参数。当没有更多页面需要解析的时候递归就结束了。

第七步 输出结果

首先,设置一些基本的样式。只要符合你的期望,你可以随意设置样式。

main {
 margin:80px auto;
 width:500px;
 }
 h1 {
 font:bold 40px/38px helvetica, verdana, sans-serif;
 margin:0;
 }
 h1 a {
 color:600;
 text-decoration:none;
 }
 p {
 background: ECECEC;
 font:10px/14px verdana, sans-serif;
 margin:8px 0 15px;
 border: 1px CCC solid;
 padding: 15px;
 }
 .item {
 padding:10px;
 }

接下来,我们用一小段Php代码来输出先前存储的信息。

<?php
 foreach($articles as $item) {
 echo "<div class='item'>";
 echo $item[0];
 echo $item[1];
 echo "</div>";
 }
?>

最后,得到的结果是一个单独的显示所有文章的HTML页面。

第八步 小结

如果你想要解析大量的页面(例如:整个站点),花费的时间也许会超出你的服务器所允许运行的时间限制。举个例子,从我本机上每抓取一个页面大约需要花费1秒钟。

在Nettuts这个站点上,有78篇教程,抓取所用的时间已经超出1分钟了。

有好多中方法来进行HTML的解析,从易学易用的角度上说,我觉得这个库是最好中的一个。最后,始终要记住在抓取某个站点之前要获得人家的许可,这是很重要的。感谢阅读。

原文地址:http://net.tutsplus.com/tutorials/php/html-parsing-and-screen-scraping-with-the-simple-html-dom-library/

/* */
    • 点点爱
    • 2010-06-28 10:29:03

    点点爱: http://daogou.pinkdolorous.cn

留 言 板
您的鼎鼎大名:
您的金玉良言:
验证码: 验证码