Scrapy提供了本身的数据提取方法,即Selector(选择器)。Selector是基于lxml来构建的,支持XPath选择器、CSS选择器以及正则表达式,功能全面,解析速度和准确度很是高。
css
本节将介绍Selector的用法。html
Selector是一个能够独立使用的模块。咱们能够直接利用Selector
这个类来构建一个选择器对象,而后调用它的相关方法如xpath()
、css()
等来提取数据。
web
例如,针对一段HTML代码,咱们能够用以下方式构建Selector
对象来提取数据:正则表达式
from scrapy import Selector
body = '<html><head><title>Hello World</title></head><body></body></html>'
selector = Selector(text=body)
title = selector.xpath('//title/text()').extract_first()
print(title)复制代码
运行结果以下所示:shell
Hello World复制代码
咱们在这里没有在Scrapy框架中运行,而是把Scrapy中的Selector单独拿出来使用了,构建的时候传入text
参数,就生成了一个Selector
选择器对象,而后就能够像前面咱们所用的Scrapy中的解析方式同样,调用xpath()
、css()
等方法来提取了。数组
在这里咱们查找的是源代码中的title中的文本,在XPath选择器最后加text()
方法就能够实现文本的提取了。bash
以上内容就是Selector的直接使用方式。同Beautiful Soup等库相似,Selector其实也是强大的网页解析库。若是方便的话,咱们也能够在其余项目中直接使用Selector来提取数据。微信
接下来,咱们用实例来详细讲解Selector的用法。网络
因为Selector主要是与Scrapy结合使用,如Scrapy的回调函数中的参数response
直接调用xpath()
或者css()
方法来提取数据,因此在这里咱们借助Scrapy Shell来模拟Scrapy请求的过程,来说解相关的提取方法。
框架
咱们用官方文档的一个样例页面来作演示:http://doc.scrapy.org/en/latest/_static/selectors-sample1.html。
开启Scrapy Shell,在命令行输入以下命令:
scrapy shell http://doc.scrapy.org/en/latest/_static/selectors-sample1.html复制代码
咱们就进入到Scrapy Shell模式。这个过程实际上是,Scrapy发起了一次请求,请求的URL就是刚才命令行下输入的URL,而后把一些可操做的变量传递给咱们,如request
、response
等,以下图所示。
咱们能够在命令行模式下输入命令调用对象的一些操做方法,回车以后实时显示结果。这与Python的命令行交互模式是相似的。
接下来,演示的实例都将页面的源码做为分析目标,页面源码以下所示:
<html>
<head>
<base href='http://example.com/' />
<title>Example website</title>
</head>
<body>
<div id='images'>
<a href='image1.html'>Name: My image 1 <br /><img src='image1_thumb.jpg' /></a>
<a href='image2.html'>Name: My image 2 <br /><img src='image2_thumb.jpg' /></a>
<a href='image3.html'>Name: My image 3 <br /><img src='image3_thumb.jpg' /></a>
<a href='image4.html'>Name: My image 4 <br /><img src='image4_thumb.jpg' /></a>
<a href='image5.html'>Name: My image 5 <br /><img src='image5_thumb.jpg' /></a>
</div>
</body>
</html>复制代码
进入Scrapy Shell以后,咱们将主要操做response
这个变量来进行解析。由于咱们解析的是HTML代码,Selector将自动使用HTML语法来分析。
response
有一个属性selector
,咱们调用response.selector
返回的内容就至关于用response
的body
构造了一个Selector对象。经过这个Selector对象咱们能够调用解析方法如xpath()
、css()
等,经过向方法传入XPath或CSS选择器参数就能够实现信息的提取。
咱们用一个实例感觉一下,以下所示:
>>> result = response.selector.xpath('//a')
>>> result
[<Selector xpath='//a' data='<a href="image1.html">Name: My image 1 <'>,
<Selector xpath='//a' data='<a href="image2.html">Name: My image 2 <'>,
<Selector xpath='//a' data='<a href="image3.html">Name: My image 3 <'>,
<Selector xpath='//a' data='<a href="image4.html">Name: My image 4 <'>,
<Selector xpath='//a' data='<a href="image5.html">Name: My image 5 <'>]
>>> type(result)
scrapy.selector.unified.SelectorList复制代码
打印结果的形式是Selector组成的列表,其实它是SelectorList
类型,SelectorList和Selector均可以继续调用xpath()
和css()
等方法来进一步提取数据。
在上面的例子中,咱们提取了a
节点。接下来,咱们尝试继续调用xpath()
方法来提取a
节点内包含的img
节点,以下所示:
>>> result.xpath('./img')
[<Selector xpath='./img' data='<img src="image1_thumb.jpg">'>,
<Selector xpath='./img' data='<img src="image2_thumb.jpg">'>,
<Selector xpath='./img' data='<img src="image3_thumb.jpg">'>,
<Selector xpath='./img' data='<img src="image4_thumb.jpg">'>,
<Selector xpath='./img' data='<img src="image5_thumb.jpg">'>]复制代码
咱们得到了a
节点里面的全部img
节点,结果为5。
值得注意的是,选择器的最前方加 .(点),这表明提取元素内部的数据,若是没有加点,则表明从根节点开始提取。此处咱们用了./img
的提取方式,则表明从a
节点里进行提取。若是此处咱们用//img
,则仍是从html
节点里进行提取。
咱们刚才使用了response.selector.xpath()
方法对数据进行了提取。Scrapy提供了两个实用的快捷方法,response.xpath()
和response.css()
,它们两者的功能彻底等同于response.selector.xpath()
和response.selector.css()
。方便起见,后面咱们统一直接调用response
的xpath()
和css()
方法进行选择。
如今咱们获得的是SelectorList
类型的变量,该变量是由Selector
对象组成的列表。咱们能够用索引单独取出其中某个Selector
元素,以下所示:
>>> result[0]
<Selector xpath='//a' data='<a href="image1.html">Name: My image 1 <'>复制代码
咱们能够像操做列表同样操做这个SelectorList
。
可是如今获取的内容是Selector
或者SelectorList
类型,并非真正的文本内容。那么具体的内容怎么提取呢?
好比咱们如今想提取出a
节点元素,就能够利用extract()
方法,以下所示:
>>> result.extract()
['<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>', '<a href="image2.html">Name: My image 2 <br><img src="image2_thumb.jpg"></a>', '<a href="image3.html">Name: My image 3 <br><img src="image3_thumb.jpg"></a>', '<a href="image4.html">Name: My image 4 <br><img src="image4_thumb.jpg"></a>', '<a href="image5.html">Name: My image 5 <br><img src="image5_thumb.jpg"></a>']复制代码
这里使用了extract()
方法,咱们就能够把真实须要的内容获取下来。
咱们还能够改写XPath表达式,来选取节点的内部文本和属性,以下所示:
>>> response.xpath('//a/text()').extract()
['Name: My image 1 ', 'Name: My image 2 ', 'Name: My image 3 ', 'Name: My image 4 ', 'Name: My image 5 ']
>>> response.xpath('//a/@href').extract()
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']复制代码
咱们只须要再加一层/text()
就能够获取节点的内部文本,或者加一层/@href
就能够获取节点的href
属性。其中,@
符号后面内容就是要获取的属性名称。
如今咱们能够用一个规则把全部符合要求的节点都获取下来,返回的类型是列表类型。
可是这里有一个问题:若是符合要求的节点只有一个,那么返回的结果会是什么呢?咱们再用一个实例来感觉一下,以下所示:
>>> response.xpath('//a[@href="image1.html"]/text()').extract()
['Name: My image 1 ']复制代码
咱们用属性限制了匹配的范围,使XPath只能够匹配到一个元素。而后用extract()
方法提取结果,其结果仍是一个列表形式,其文本是列表的第一个元素。但不少状况下,咱们其实想要的数据就是第一个元素内容,这里咱们经过加一个索引来获取,以下所示:
>>> response.xpath('//a[@href="image1.html"]/text()').extract()[0]
'Name: My image 1 '复制代码
可是,这个写法很明显是有风险的。一旦XPath有问题,那么extract()
后的结果多是一个空列表。若是咱们再用索引来获取,那不就会可能致使数组越界吗?
因此,另一个方法能够专门提取单个元素,它叫做extract_first()
。咱们能够改写上面的例子以下所示:
>>> response.xpath('//a[@href="image1.html"]/text()').extract_first()
'Name: My image 1 '复制代码
这样,咱们直接利用extract_first()
方法将匹配的第一个结果提取出来,同时咱们也不用担忧数组越界的问题。
另外咱们也能够为extract_first()
方法设置一个默认值参数,这样当XPath规则提取不到内容时会直接使用默认值。例如将XPath改为一个不存在的规则,从新执行代码,以下所示:
>>> response.xpath('//a[@href="image1"]/text()').extract_first()
>>> response.xpath('//a[@href="image1"]/text()').extract_first('Default Image')
'Default Image'复制代码
这里,若是XPath匹配不到任何元素,调用extract_first()
会返回空,也不会报错。
在第二行代码中,咱们还传递了一个参数看成默认值,如Default Image。这样若是XPath匹配不到结果的话,返回值会使用这个参数来代替,能够看到输出正是如此。
如今为止,咱们了解了Scrapy中的XPath的相关用法,包括嵌套查询、提取内容、提取单个内容、获取文本和属性等。
接下来,咱们看看CSS选择器的用法。
Scrapy的选择器同时还对接了CSS选择器,使用response.css()
方法可使用CSS选择器来选择对应的元素。
例如在上文咱们选取了全部的a
节点,那么CSS选择器一样能够作到,以下所示:
>>> response.css('a')
[<Selector xpath='descendant-or-self::a' data='<a href="image1.html">Name: My image 1 <'>,
<Selector xpath='descendant-or-self::a' data='<a href="image2.html">Name: My image 2 <'>,
<Selector xpath='descendant-or-self::a' data='<a href="image3.html">Name: My image 3 <'>,
<Selector xpath='descendant-or-self::a' data='<a href="image4.html">Name: My image 4 <'>,
<Selector xpath='descendant-or-self::a' data='<a href="image5.html">Name: My image 5 <'>]复制代码
一样,调用extract()
方法就能够提取出节点,以下所示:
>>> response.css('a').extract()
['<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>', '<a href="image2.html">Name: My image 2 <br><img src="image2_thumb.jpg"></a>', '<a href="image3.html">Name: My image 3 <br><img src="image3_thumb.jpg"></a>', '<a href="image4.html">Name: My image 4 <br><img src="image4_thumb.jpg"></a>', '<a href="image5.html">Name: My image 5 <br><img src="image5_thumb.jpg"></a>']复制代码
用法和XPath选择是彻底同样的。
另外,咱们也能够进行属性选择和嵌套选择,以下所示:
>>> response.css('a[href="image1.html"]').extract()
['<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>']
>>> response.css('a[href="image1.html"] img').extract()
['<img src="image1_thumb.jpg">']复制代码
这里用[href="image.html"]
限定了href
属性,能够看到匹配结果就只有一个了。另外若是想查找a
节点内的img
节点,只须要再加一个空格和img
便可。选择器的写法和标准CSS选择器写法一模一样。
咱们也可使用extract_first()
方法提取列表的第一个元素,以下所示:
>>> response.css('a[href="image1.html"] img').extract_first()
'<img src="image1_thumb.jpg">'复制代码
接下来的两个用法不太同样。节点的内部文本和属性的获取是这样实现的,以下所示:
>>> response.css('a[href="image1.html"]::text').extract_first()
'Name: My image 1 '
>>> response.css('a[href="image1.html"] img::attr(src)').extract_first()
'image1_thumb.jpg'复制代码
获取文本和属性须要用::text
和::attr()
的写法。而其余库如Beautiful Soup或pyquery都有单独的方法。
另外,CSS选择器和XPath选择器同样能够嵌套选择。咱们能够先用XPath选择器选中全部a
节点,再利用CSS选择器选中img
节点,再用XPath选择器获取属性。咱们用一个实例来感觉一下,以下所示:
>>> response.xpath('//a').css('img').xpath('@src').extract()
['image1_thumb.jpg', 'image2_thumb.jpg', 'image3_thumb.jpg', 'image4_thumb.jpg', 'image5_thumb.jpg']复制代码
咱们成功获取了全部img
节点的src
属性。
所以,咱们能够随意使用xpath()
和css()
方法两者自由组合实现嵌套查询,两者是彻底兼容的。
Scrapy的选择器还支持正则匹配。好比,在示例的a
节点中的文本相似于Name: My image 1
,如今咱们只想把Name:
后面的内容提取出来,这时就能够借助re()
方法,实现以下:
>>> response.xpath('//a/text()').re('Name:\s(.*)')
['My image 1 ', 'My image 2 ', 'My image 3 ', 'My image 4 ', 'My image 5 ']复制代码
咱们给re()
方法传了一个正则表达式,其中(.*)
就是要匹配的内容,输出的结果就是正则表达式匹配的分组,结果会依次输出。
若是同时存在两个分组,那么结果依然会被按序输出,以下所示:
>>> response.xpath('//a/text()').re('(.*?):\s(.*)')
['Name', 'My image 1 ', 'Name', 'My image 2 ', 'Name', 'My image 3 ', 'Name', 'My image 4 ', 'Name', 'My image 5 ']复制代码
相似extract_first()
方法,re_first()
方法能够选取列表的第一个元素,用法以下:
>>> response.xpath('//a/text()').re_first('(.*?):\s(.*)')
'Name'
>>> response.xpath('//a/text()').re_first('Name:\s(.*)')
'My image 1 '复制代码
不论正则匹配了几个分组,结果都会等于列表的第一个元素。
值得注意的是,response
对象不能直接调用re()
和re_first()
方法。若是想要对全文进行正则匹配,能够先调用xpath()
方法再正则匹配,以下所示:
>>> response.re('Name:\s(.*)')
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'HtmlResponse' object has no attribute 're'
>>> response.xpath('.').re('Name:\s(.*)<br>')
['My image 1 ', 'My image 2 ', 'My image 3 ', 'My image 4 ', 'My image 5 ']
>>> response.xpath('.').re_first('Name:\s(.*)<br>')
'My image 1 '复制代码
经过上面的例子,咱们能够看到,直接调用re()
方法会提示没有re
属性。可是这里首先调用了xpath('.')
选中全文,而后调用re()
和re_first()
方法,就能够进行正则匹配了。
以上内容即是Scrapy选择器的用法,它包括两个经常使用选择器和正则匹配功能。熟练掌握XPath语法、CSS选择器语法、正则表达式语法能够大大提升数据提取效率。
本资源首发于崔庆才的我的博客静觅: Python3网络爬虫开发实战教程 | 静觅
如想了解更多爬虫资讯,请关注个人我的微信公众号:进击的Coder
weixin.qq.com/r/5zsjOyvEZ… (二维码自动识别)