Scrapy爬虫框架中Selector的具体用法介绍
Posted zhi_neng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Scrapy爬虫框架中Selector的具体用法介绍相关的知识,希望对你有一定的参考价值。
之前介绍了利用Beautiful Soup、pyquery以及正则表达式来提取网页数据,这确实非常方便。而Scrapy本身提供了自己的数据提取方法,即Selector(选择器)。Selector是基于lxml来构建的,支持XPath选择器、CSS选择器以及正则表达式,功能全面,解析速度和准确度非常高。我们来具体的了解一下。
1.直接使用
Seletor是一个可以独立使用的模块。我们可以直接利用Selector这个类来构建一个选择器对象,然后调用它的相关方法如xpath()、css()等来提取数据。
比如,针对一段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)
运行结果如下:
Hello World
我们这里没有在Scrapy框架中运行,而是把Scrapy中的Selector单独拿出来使用了,构建的时候传入了text参数,就生成了一个Selector选择器对象,然后就可以像前面我们所用的Scrapy中的解析方式一样,调用xpath()、css()等方法来提取了。
在这里我们查找的是源代码中的title中的文本,在Xpath选择器最后加上text()方法就可以是想文本的提取了。
以上内容就是Selector的直接使用方式。Beautiful Soup等库类似,Selector其实也是强大的网页解析库。如果方便的话,我们也可以在其他项目中直接使用Selector来提取数据。
2.Scrapy shell
由于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>
3.Xpath选择器
进入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 ...'>, <Selector xpath='//a' data='<a href="image2.html">Name: My image ...'>, <Selector xpath='//a
' data='<a href="image3.html">Name: My image ...'>, <Selector xpath='//a' data='<a href="image4.html">Name: My image ...'>, <Selector xpath='//a' data='<a href="imag
e5.html">Name: My image ...'>]
>>> type(result)<class 'scrapy.selector.unified.SelectorList'>
在上面的例子中,我们提取了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节点里面全部5个img节点,注意在选择的最前方加.(点),这代表提取元素内部的数据,如果没有加点,则代表从根节点开始提取,我们使用./img的提取方式,代表从a节点里面进行提取,如果我们用//img,则还是从html节点里进行提取。
我们使用response.selector.xpath()方法进行数据的提取。Scrapy提供了两个实用的快捷方法,response.xpath()和response.css()方法,功能完全一样,因此我们方便可以使用者两个方法,以后都会使用这个两个方法来介绍。
上面我们得到了SelectorList类型的变量,此变量是由Selector对象组成的列表。我们可以用索引单独取出其中某一个Selector元素,如下:
>>> result[0]
<Selector xpath='//a' data='<a href="image1.html">Name: My image ...'>
因此我们可以像操作列表一样来操作这个SelectorList。
但是我们想要获取的是真正的文本内容,具体应该如何操作呢?
我们可以使用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 hr
ef="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="ima
ge5.html">Name: My image 5 <br><img src="image5_thumb.jpg"></a>']
我们也可以使用xpath表达式,来获取节点的内部文本和属性,如下所示:
>>> result.xpath('//a/text()').extract()
['Name: My image 1 ', 'Name: My image 2 ', 'Name: My image 3 ', 'Name: My image 4 ', 'Name: My image 5 ', 'Name: My image 1 ', 'Name: My image 2 ', 'Name: My image 3
', 'Name: My image 4 ', 'Name: My image 5 ', 'Name: My image 1 ', 'Name: My image 2 ', 'Name: My image 3 ', 'Name: My image 4 ', 'Name: My image 5 ', 'Name: My imag
e 1 ', 'Name: My image 2 ', 'Name: My image 3 ', 'Name: My image 4 ', 'Name: My image 5 ', 'Name: My image 1 ', 'Name: My image 2 ', 'Name: My image 3 ', 'Name: My i
mage 4 ', 'Name: My image 5 ']
>>> result.xpath('//a/@href').extract()
['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html', 'image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html', 'image1.html',
'image2.html', 'image3.html', 'image4.html', 'image5.html', 'image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html', '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()方法设置一个默认值参数,这样当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的使用方法,包括嵌套查询、提取内容、提取单个内容、获取文本和属性等。
4.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 ...'>, <Selector xpath='descendant-or-self::a' data='<a href="image2.html">Name:
My image ...'>, <Selector xpath='descendant-or-self::a' data='<a href="image3.html">Name: My image ...'>, <Selector xpath='descendant-or-self::a' data='<a href="imag
e4.html">Name: My image ...'>, <Selector xpath='descendant-or-self::a' data='<a href="image5.html">Name: My image ...'>]
同样,我们调用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 hr
ef="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="ima
ge5.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="image1.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']
由此可见,我们可以使用Xpath()和css()方法二者自由组合实现嵌套查询,二者是完全兼容的。
5.正则表达式
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()方法,就可以进行正则匹配了。
6.结束语
以上就是Scrapy选择器的用法,它包括两个常用选择器和正则匹配功能。数量掌握Xpath语法、CSS选择器语法、正则表达式语法可以大大的提高我们数据提取的效率。
以上是关于Scrapy爬虫框架中Selector的具体用法介绍的主要内容,如果未能解决你的问题,请参考以下文章
Python爬虫之Scrapy框架系列(10)——Scrapy选择器selector
Python爬虫之Scrapy框架系列(10)——Scrapy选择器selector