数据抽取:CSS选择器还是XPath?

2021-07-12

编写爬虫的过程,数据抽取是我们完成目标的重要一环,一般可以用CSS选择器语法或者XPath语法来实现数据的定位和抽取,那到底那个更好呢?

爬虫简单来看,就是两个步骤,第一步请求目标页面,拿回页面原始数据。第二步,从页面原始数据中抽取我们需要的数据。我们今天关注数据抽取这一步。一般来说,我们通过请求,拿到了页面的原始数据,这个数据大多数情况下是HTML格式的数据。Scrapy框架内置了两种选择特定DOM节点的方法:一种是CSS选择器语法,另一种是XPath语法。那这两种语法应该用哪种呢?原则上来说CSS选择器语法和XPath语法都能够应对绝大部分的情况,几乎可以从HTML页面抽取任何你想要的数据。但是语法和能力上还是有所差别,各自有自己擅长的领域。

CSS选择器

Cascading Style Sheets (CSS)层叠样式表,是一种用来为结构化文档(如HTML)添加样式的语言。CSS选择器是选择特定样式元素的语法。

Xpath

Xml Path Language(XPath)是XML路径语言,它是一种用来确定XML文档中某部分位置的计算机语言。XPath基于XML的树状结构,提供在数据结构树中寻找节点的能力。

使用对比

在抽取HTML页面数据上,CSS选择器和XPath几乎都能胜任绝大部分的工作。但是使用上还是有所不同。我们观察下面HTML片段:

<div>
    <p class="data expandable">
        这里是段落文字
    </p>
</div>

如果我们要选取P元素,用CSS选择器我们可以这样写:

p.data.expandable

用XPath的话,需要这样写:

//p[@class='data expandable']

对于一般情况下,我们会根据DOM标签的ID或者class来定位,CSS在这方面有一定的优势,书写的更加直接和清晰。相比而言,XPath有点啰嗦。但是XPath有更多实用的特性,可以说XPath能应对更加复杂的定位需求。

例如,如下HTML片段:

<p> 第一段 </p>
<p> 第二段 </p>
<p> 第三段. 描述这些 </p>

我们可以通过XPath来选择第三个P标签:

//p[contains(text(), '描述这些')]

那么我们怎么用CSS选择器来选择这个节点呢?事实上,我们做不到,我们无法通过匹配节点内的内容来区分选定节点。CSS的标准中支持节点标签选择,标签属性选择,ID,class选择。但是我们没有办法支持节点内容的选择。如果要基于节点内容做选择,我们只能使用XPath。

下面是CSS选择器和XPath语法的一些对比和区别:

功能 CSS3 选择器 XPath
所有节点 * //*
所有p节点 p //p
p节点下的所有子节点 p > * //p/*
指定ID的节点 #foo //*[@id=’foo’]
指定Class的节点 .foo //*[contains(@class,’foo’)]
指定属性的节点 *[title] //*[@title]
p节点的第一个子节点 p>*:first-child //p/*[0]
有a子节点的p节点 无法选择 //p[a]
p的下一个节点 p + * //p/following-sibling::*[0]
p的上一个节点 无法选择 //p/preceding-sibling::*[0]

我们的结论

一般情况下,建议直接使用CSS选择器来做节点选择和数据抽取。CSS选择器语法简单直接,运行效率也相对更高。大部分场景都能应对。但是,如果是非常复杂的选择,或者需要根据节点内容进行筛选选择,那么可以选择使用XPath语法来处理。

如果是你是用Scrapy来编写爬虫脚本,那Scrapy内置了CSS和XPath的支持,你可以直接使用。如果你没有用Scrapy,而是基于Nodejs编写爬虫,那么你可以了解以下库:

  1. cheerio: 支持JQuery语法进行节点选择,十分方便。
  2. jsdom: 提供了符合WHATWG的DOM和HTML标准接口,你可以使用熟悉的浏览器接口进行DOM操作。
  3. xpath: 提供了XPath的支持。