Scrapy之Selector的用法

Selector的用法

Scrapy提供了自己的数据提取方法,即Selector(选择器),Selector是基于lxml来构建的,支持XPath选择器、CSS选择器以及正则表达式,功能全面,解析速度和准确度非常高。

直接使用

Selector是一个可以独立使用的模块。我们可以直接利用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)

Scrapy shell

Selector主要是与Scrapy结合使用,如 Scrapy 的回调函数中的参数 response 直接调用 xpath() 或者 css() 方法来提取数据,这里我们可以借助 Scrapy shell 来模拟 Scrapy 请求的过程。
开启 Scrapy shell,在命令行输入如下命令:
scrapy shell http://doc.scrapy .org/en/latest/_static/selectors-sample1.html
这个过程其实是, 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>


XPath 选择器

进入 Scrapy shell 之后,主要通过操作 response 这个变量来进行解析。 因为我们解析的是 HTML代码, Selector 将自动使用 HTML 语法来分析。
response:有一个属性 selector ,我们调用 response.selector 返回的内容就相当于用 response 的 body 构造了一个 Selector 对象 。 通过这个 Selector 对象我们可以调用解析方法如 xpath() 、css()等,通过向方法传入 XPath 或 css 选择器参数就可以实现信息的提取。

In [1]: result=response.selector.xpath("//a")

In [2]: result
Out[2]:
[<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="image5.html">Name: My image ...'>]

打印结果的形式是 Selector 组成的列表,它是 Selectorlist 类型, SelectorList 和 Selector 都可以继续调用 xpath()和 css()等方法来进一步提取数据 。

继续调用 xpath()方法来提取 a 节点内包含的 img 节点

In [3]: result.xpath("./img")
Out[3]:
[<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">'>]

选择器的最前方加**.**(点),这代表提取元素内部的数据,如果没有加点,则代表从根节点开始提取。 如果此处我们用//img,则还是从 html 节点里进行提取 。

除了使用 response.selector.xpath() 方法对数据进行了提取外。Scrapy 还提供了两个实用的快捷方法,response.xpath() 和 response.css(),它们二者的功能完全等同 response.selector.xpath() 和 response.selector.css()。

前面等到的结果是 SelectorList 类型的变量,改变量是由 Selector 对象组成的列表。可以用索引单独的取出某个 Selector 元素。

In [4]: result[0]
Out[4]: <Selector xpath='//a' data='<a href="image1.html">Name: My image ...'>

extract()方法

extract() 方法,可以把真实需要的内容获取下来。

例如,提取出 a 节点元素:

In [5]: result.extract()
Out[5]:
['<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>']

选取节点的内部文本和属性:

In [6]: response.xpath("//a/text()").extract()
Out[6]:
['Name: My image 1 ', 'Name: My image 2 ', 'Name: My image 3 ', 'Name: My image 4 ', 'Name: My image 5 ']

In [7]: response.xpath("//a/@href").extract()
Out[7]: ['image1.html', 'image2.html', 'image3.html', 'image4.html', 'image5.html']

如果符合要求的节点只有一个,那么可以使用 extract()[0] 来直接提取列表中的内容

In [8]: response.xpath("//a[@href='image1.html']/text()").extract()[0]
Out[8]: 'Name: My image 1 '

但是这个方法在使用中有一定的风险,如果我们写的 XPath 语法有问题或者目标网页发生了变化,那么 extract() 的结果很可能是一个空列表。此时在使用索引获取内容就可能导致数组越界。

另外一个方法可以专门提取单个元素 extract_first(),将上面的例子改写如下:

In [10]: response.xpath("//a[@href='image1.html']/text()").extract_first()
Out[10]: 'Name: My image 1 '

这样,我们直接利用 extract_first() 方法将匹配的第一个结果提取出来,同时我们也不用担心数组越界的问题。

我们也可以为 extract_first() 方法设置一个默认值参数,这样当 XPath 规则提取不到内容时会直接使用默认值。

例如将 XPath 改成一个不存在的规则

In [11]: response.xpath("//a[@href='image1']/text()").extract_first()

In [12]: response.xpath("//a[@href='image1']/text()").extract_first('Default Image')
Out[12]: 'Default Image'

在用不存在的规则提取时,没有默认值参数时什么都没有输出,设置了默认值参数后则输出了设置的默认值。

XPath 教程:https://www.w3school.com.cn/xpath/xpath_syntax.asp

CSS 选择器

Scrapy 的选择器同时还对接了 css 选择器,使用 response.css() 方法可以使用 css 选择器来应择
对应的元素 。用法与XPath方法一样。

选取所有的 a 节点:

In [13]: response.css('a')
Out[13]:
[<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="image4.html">Name: My image ...'>,
 <Selector xpath='descendant-or-self::a' data='<a href="image5.html">Name: My image ...'>]

调用 extract() 方法同样可以提取出节点

In [14]: response.css('a').extract()
Out[14]:
['<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>']

进行属性选择和嵌套选择:

In [16]: response.css('a[href="image1.html"]').extract()
Out[16]: ['<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>']

In [17]: response.css('a[href="image1.html"] img').extract()
Out[17]: ['<img src="image1_thumb.jpg">']

使用 extract_first() 方法提取列表的第一个元素:

In [18]: response.css('a[href="image1.html"] img').extract_first()
Out[18]: '<img src="image1_thumb.jpg">'

节点的内部文本和属性的获取与 XPath 有一点区别,获取文本需要用 ::text ,获取属性用 ::attr()

In [19]: response.css('a[href="image1.html"]::text').extract_first()
Out[19]: 'Name: My image 1 '

In [20]: response.css('a[href="image1.html"] img::attr(src)').extract_first()
Out[20]: 'image1_thumb.jpg'

另外 css 选择器和 XPath 选择器一样可以嵌套选择。 我们可以先用 XPath 选择器选中所有 a 节
点,再利用 css 选择器选中 img 节点,再用 XPath 选择器在取属性。

In [21]: response.xpath('//a').css('img').xpath('@src').extract()
Out[21]:
['image1_thumb.jpg', 'image2_thumb.jpg', 'image3_thumb.jpg', 'image4_thumb.jpg', 'image5_thumb.jpg']

我们可以随意使用 xpath()和 css() 方法二者自由组合实现嵌套查询,二者是完全兼容的 。

CSS语法: https://www.w3school.com.cn/cssref/css_selectors.asp

正则匹配

Scrapy 的选择器还支持正则匹配 。 比如,在示例的 a 节点中的文本类似于 Name: My image 1 ,现在我们只想把 Name:后面的内容提取出来,这时就可以借助 re() 方法,实现如下:

In [22]: response.xpath('//a/text()').re('Name:\s(.*)')
Out[22]: ['My image 1 ', 'My image 2 ', 'My image 3 ', 'My image 4 ', 'My image 5 ']

如果同时存在两个分组,那么结果将会被按序输出:

In [23]: response.xpath('//a/text()').re('(.*?):\s(.*)')
Out[23]:
['Name', 'My image 1 ', 'Name', 'My image 2 ', 'Name', 'My image 3 ', 'Name', 'My image 4 ', 'Name', 'My image 5 ']

类似 extract_first() 方法, re_first() 方法可以选取列表的第一个元素,用法如下:

In [24]: response.xpath('//a/text()').re_first('(.*?):\s(.*)')
Out[24]: 'Name'

In [25]: response.xpath('//a/text()').re_first('Name:\s(.*)')
Out[25]: 'My image 1 '

不论正则匹配了几个分组,结果都会等于列表的第一个元素 。

值得注意的是, response 对象不能直接调用 re() 和 re_first() 方法。 如果想要对全文进行正则匹配,可以先调用 xpath() 方法再正则匹配,如下所示:

In [26]: response.re('Name:\s(.*)')
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-29-3635822752bd> in <module>
----> 1 response.re('Name:\s(.*)')

AttributeError: 'HtmlResponse' object has no attribute 're'

In [27]: response.xpath('.').re('Name:\s(.*)<br>')
Out[27]: ['My image 1 ', 'My image 2 ', 'My image 3 ', 'My image 4 ', 'My image 5 ']

In [28]: response.xpath('.').re_first('Name:\s(.*)<br>')
Out[28]: 'My image 1 '

我们看到直接调用 re() 方法会提示没有 re 属性。 但是这里首先调用了 xpath(‘.’)选中全文,然后调用 re() 和 re_first() 方法,就可以进行正则匹配了 。

结语

熟练掌握 XPath 语法、 css 选择器语法、正则表达式语法可以大大提高数据提取效率。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值