Scrapy笔记04- Selector详解

在你爬取网页的时候,最广泛的事情就是在页面源码中提取须要的数据,我们有几个库可以帮你完成这个义务:

  1. BeautifulSoup是python中一个非常风行的抓取库,它还能公道的处置毛病格局的标签,但是有一个唯一缺陷就是:它运行很慢。
  2. lxml是一个基于ElementTree的XML解析库(同时还能解析HTML),不过lxml其实不是Python尺度库

而Scrapy实现了自己的数据提取机制,它们被称为选择器,通过XPathCSS表达式在HTML文档中来选择特定的部份

XPath是一用来在XML当选择节点的语言,同时可以用在HTML上面。CSS是一种HTML文档上面的样式语言。

Scrapy选择器构建在lxml基本之上,所以可以保证速度和精确性。

本章我们来详细讲授下选择器的工作原理,还有它们极为简略和类似的API,比lxml的API少多了,由于lxml可以用于很多其他范畴。

完全的API请查看Selector参考

关于选择器

Scrapy帮我们下载完页面后,我们怎样在满是html标签的内容中找到我们所须要的元素呢,这里就须要应用到选择器了,它们是用来定位元素并且提取元素的值。先来举几个例子看看:

  • /html/head/title: 选择<title>节点, 它位于html文档的<head>节点内
  • /html/head/title/text(): 选择上面的<title>节点的内容.
  • //td: 选择页面中所有的元素
  • //div[@class=”mine”]: 选择所有具有属性class="mine"的div元素

Scrapy应用css和xpath选择器来定位元素,它有四个基本办法:

  • xpath(): 返回选择器列表,每一个选择器代表应用xpath语法选择的节点
  • css(): 返回选择器列表,每一个选择器代表应用css语法选择的节点
  • extract(): 返回被选择元素的unicode字符串
  • re(): 返回通过正则表达式提取的unicode字符串列表

应用选择器

下面我们通过Scrapy shell演示下选择器的应用,假定我们有以下的一个网页http://doc.scrapy.org/en/latest/_static/selectors-sample1.html,内容以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<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>

首先我们打开shell

1
scrapy shell http://doc.scrapy.org/en/latest/_static/selectors-sample1.html

运行

1
2
3
4
>>> response.xpath('//title/text()')
[<Selector (text) xpath=//title/text()>]
>>> response.css('title::text')
[<Selector (text) xpath=//title/text()>]

成果可以看出,xpath()css()办法返回的是SelectorList实例,是一个选择器列表,你可以选择嵌套的数据:

1
2
3
4
5
6
>>> response.css('img').xpath('@src').extract()
[u'image1_thumb.jpg',
u'image2_thumb.jpg',
u'image3_thumb.jpg',
u'image4_thumb.jpg',
u'image5_thumb.jpg']

必需应用.extract()能力提取终究的数据,如果你只想取得第一个匹配的,可以应用.extract_first()

1
2
>>> response.xpath('//div[@id="images"]/a/text()').extract_first()
u'Name: My image 1 '

如果没有找到,会返回None,还可选择默许值

1
2
>>> response.xpath('//div[@id="not-exists"]/text()').extract_first(default='not-found')
'not-found'

而CSS选择器还可以应用CSS3尺度:

1
2
>>> response.css('title::text').extract()
[u'Example website']

下面是几个比拟全面的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
>>> response.xpath('//base/@href').extract()
[u'http://example.com/']

>>> response.css('base::attr(href)').extract()
[u'http://example.com/']

>>> response.xpath('//a[contains(@href, "image")]/@href').extract()
[u'image1.html',
u'image2.html',
u'image3.html',
u'image4.html',
u'image5.html']

>>> response.css('a[href*=image]::attr(href)').extract()
[u'image1.html',
u'image2.html',
u'image3.html',
u'image4.html',
u'image5.html']

>>> response.xpath('//a[contains(@href, "image")]/img/@src').extract()
[u'image1_thumb.jpg',
u'image2_thumb.jpg',
u'image3_thumb.jpg',
u'image4_thumb.jpg',
u'image5_thumb.jpg']

>>> response.css('a[href*=image] img::attr(src)').extract()
[u'image1_thumb.jpg',
u'image2_thumb.jpg',
u'image3_thumb.jpg',
u'image4_thumb.jpg',
u'image5_thumb.jpg']

嵌套选择器

xpath()css()返回的是选择器列表,所以你可以持续应用它们的办法。举例来说:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
>>> links = response.xpath('//a[contains(@href, "image")]')
>>> links.extract()
[u'<a href=#>
u'<a href=#>
u'<a href=#>
u'<a href=#>
u'<a href=#>

>>> for index, link in enumerate(links):
... args = (index, link.xpath('@href').extract(), link.xpath('img/@src').extract())
... print 'Link number %d points to url %s and image %s' % args

Link number 0 points to url [u'image1.html'] and image [u'image1_thumb.jpg']
Link number 1 points to url [u'image2.html'] and image [u'image2_thumb.jpg']
Link number 2 points to url [u'image3.html'] and image [u'image3_thumb.jpg']
Link number 3 points to url [u'image4.html'] and image [u'image4_thumb.jpg']
Link number 4 points to url [u'image5.html'] and image [u'image5_thumb.jpg']

应用正则表达式

Selector有一个re()办法通过正则表达式提取数据,它返回的是unicode字符串列表,你不能再去嵌套应用

1
2
3
4
5
6
7
8
9
>>> response.xpath('//a[contains(@href, "image")]/text()').re(r'Name:\s*(.*)')
[u'My image 1',
u'My image 2',
u'My image 3',
u'My image 4',
u'My image 5']

>>> response.xpath('//a[contains(@href, "image")]/text()').re_first(r'Name:\s*(.*)')
u'My image 1'

XPath相对路径

当你嵌套应用XPath时候,不要应用/开头的,由于这个会相对文档根节点开端算起,须要应用相对路径

1
2
3
4
5
6
7
>>> divs = response.xpath('//div')
>>> for p in divs.xpath('.//p'): # extracts all <p> inside
... print p.extract()

# 或下面这个直接应用p也能够
>>> for p in divs.xpath('p'):
... print p.extract()

XPath建议

应用text作为条件时

避免应用.//text(),直接应用.

1
2
>>> sel.xpath("//a[contains(., 'Next Page')]").extract()
[u'<a href=#>

//node[1]和(//node)[1]区分

  • //node[1]: 选择所有位于第一个子节点地位的node节点
  • (//node)[1]: 选择所有的node节点,然后返回成果中的第一个node节点

通过class查找时优先斟酌CSS

1
2
3
4
>> from scrapy import Selector
>>> sel = Selector(text='<div class="hero shout"><time datetime="2014-07-23 19:00">Special date</time></div>')
>>> sel.css('.shout').xpath('./time/@datetime').extract()
[u'2014-07-23 19:00']