最近用php进行爬虫学习,用composer安装了一个类库 symfony/dom-crawler,用来分析抓取到的网页html元素,提取其中想要的内容。因其没有中文文档,也很少有使用这个类库的相关中文资料,所以使用过程中也遇到了一些坑,在这里将使用过程中的心得及遇到问题和解决办法记录一下。
DomCrawler工作原理:
工作原理就是将抓取到的html页面字符串实例化为一个dom对象,通过 xpath 语法或者 css选择器 语法选择其中的dom节点,类似于jquery的css选择器一样,提取页面元素的属性或者值,基本能获取到页面任意想要的内容,非常强大,是一个应用于爬虫中分析html元素及提取内容的利器。安装:
composer require symfony/dom-crawler
composer require symfony/css-selector #css-selector也需要安装,才能使用xpath选择器
- 使用:
- 简单使用
require_once 'vendor/autoload.php';
#引入安装的composer类库
use Symfony\Component\DomCrawler\Crawler;
use Symfony\Component\CssSelector\CssSelectorConverter;
#抓取到的html字符串
$html = <<<'HTML'
<!DOCTYPE html>
<html>
<body>
<p class="message">Hello World!</p>
<p>Hello Crawler!</p>
</body>
</html>
HTML;
#实例化dom对象
$crawler = new Crawler();
$crawler->addHtmlContent($html);
#获取节点元素
foreach ($crawler as $domElement) {
var_dump($domElement->nodeName);
}
- css选择器语法使用
语法规则如下:
$crawler->filter('selector名称');
如果想要选择页面的某个元素下的内容,css选择器语法该怎么写呢?
有一个简单的方法,以谷歌浏览器为例,打开 www.baidu.com,比如我要获取到 百度一下 这个按钮下的内容
按 F12 打开开发者工具,光标定位到该元素,是如下的元素:
想要获取上图中span下的内容,点击span > 右键 > copy >copy selector,便复制了该按钮的css选择器。
然后使用以下规则:
$crawler->filter('#s_btn_wr');
就能获取到该span下的内容了,至于获取到某个元素下的内容后怎么进一步处理,比如获取元素属性值,元素内容等,后面会详细说。
当然我们有时候页面某个元素并没有一个id或者class的选择器,但是也可以按照以上的方法来获取css选择器,只不过这个选择器可能会比较长了,如下:
#有时候可能会这样,不过也能获取到某个元素下的内容
#rmain > div > div:nth-child(2) > div:nth-child(3) > div:nth-child(5) > div.bd > table > tbody > tr
- xpath选择器语法使用
用css选择器可能有时候会比较憋屈,因为copy出来的选择器会比较长,而且有时候不一定准,要结合页面源代码观察,而且有时候在某些特殊情况下并没有办法获取到想要的内容,比如有以下要求:获取一个页面所有包含某个关键词的a标签href属性中的链接,这个用传统的css选择器比较难做到,需要费很大劲,这时候xpath选择器就上场了,不得不说,xpath很强大,非常强大,几乎能获取到页面中想要获取的任何内容,我在这里说几个典型的常用的用法吧,具体如何使用可以参考 xpath教程
###获取页面中所有a标签href属性值包含 *info* 关键词的a元素,在提取页面某一类url很有用
$crawler->filterXPath('//a[contains(@href,"info")]');
###获取文本中包含 *职称* 两个字的span标签,这在通过文本信息来提取某类元素有用
$crawler->filterXPath('//span[contains(text(),"职称")]');
####选择两个特定元素之间的所有元素,这个很变态,很强大,放一个公式吧,具体怎么用可参考https://ask.helplib.com/xml/post_1005470 文章内容
$ns1[count(.|$ns2) = count($ns2)]
关于xpath语法更多详细的使用,大家可参考下xpath教程的一些语法规则,多用用就知道了。
在这里我介绍一个谷歌浏览器的xpath的插件,在谷歌应用商店下载 xpath helper(前提需要fanqiang)如下图:
很好用,可以在某个页面中打开,用来检测自己的xpath表达式是否正确,很有用。
- 用选择器获取到元素下内容后的进一步处理
以获取某个页面 body > p 元素内容后的进一步处理为例:
$crawler->filter('body > p')->eq(0); ###获取body下的第一个p元素
$crawler->filter('body > p')->first(); ###获取body下的第一个p元素
$crawler->filter('body > p')->last(); ###获取body下的最后一个p元素
$crawler->filter('body > p')->siblings(); ###获取body下p元素的所有兄弟元素
$crawler->filter('body > p')->nextAll(); ###获取body下p元素所有后面的元素
$crawler->filter('body > p')->previousAll(); ###获取body下p元素所有前面的元素
$crawler->filter('body')->children(); ###获取body下的所有子元素
$crawler->filter('body > p')->parents(); ###获取body下p元素的父元素
$crawler->filterXPath('//body/*')->nodeName(); ###获取body下元素节点名称
$crawler->filterXPath('//body/p')->text(); ###获取body下p元素中的文本内容
$crawler->filterXPath('//body/p')->attr('class'); ###获取body下p元素的class属性值
有时候会获取到多个节点,需要采用循环对每一个节点进行处理,可以用循环如下:
$crawler->filter('p')->each(function (Crawler $node, $i) {
return $node->text();
});
- 使用中问题及解决办法:
- composer安装了 dom-crawler 后使用不了 xpath选择器,即调用 $crawler->filterXPath('//xxx') 会报错,如何解决?
解决办法:composer安装 css-selector,php页面引入 CssSelectorConverter
composer require symfony/css-selector #composer安装
use Symfony\Component\CssSelector\CssSelectorConverter; #php页面中引入
-
new Crawler($html) 返回的结果会出现乱码,如何解决?
解决办法:这个应该与页面编码有关,所以可以采用下面的方式,先初始化crawler,然后添加node。
$crawler = new Crawler();
$crawler->addHtmlContent($html);
- 用css选择器的时候,按照上面说的步骤去获取,为什么有时候获取不到,会报错 The current node list is empty,如何解决?
因为有时候用开发者工具获取的css选择器不太准,比如获取的以下内容:
#rmain > div > div:nth-child(2) > div:nth-child(3) > div:nth-child(5) > div.bd > table > tbody > tr
我们在查看页面源代码的时候,也许table下是没有tbody的,可能table下直接是tr,不过为什么通过开发者工具复制出来的selector有呢,因为浏览器对html的table有时候会自动把tbody给补上的,但是页面源代码中并没有,所有我们有时候还需要结合页面源代码来分析选择器是否正确。
-
参考资料:
xpath教程 http://www.w3school.com.cn/xpath/
domcrawler英文文档 symfony.com/doc/current/components/dom_crawler.html
在laravel中使用Symfony的Crawler组件分析HTML https://mp.weixin.qq.com/s?__biz=MzAxNzMwOTQ0NA%3D%3D&mid=2653355079&idx=1&sn=7d85dca5ba7c141d921c8f5b556ee9cf&chksm=8035d62cb7425f3a70f15d4fb572de11ebb50c86e3194904c3ecce813ae601763e649a18089f
symfony手册 http://api.symfony.com/3.2/Symfony/Component/DomCrawler/Crawler.html