[Selectors] - 数据提取的方法
摘要:对提取数据的三种方法,xpath,css,正则进行介绍。
在这一节中我们将学习如何从网页数据中匹配数据,同时这一节也是对Scrapy文档Selectors
的学习。
抛开爬虫所有繁琐的流程,我们单单从匹配数据出发来简单学习使用xpath/css/re的一些简单方法,然后再发散思维。
另外本节内容相对来说较为枯燥,主要是为了能让大家掌握一些基本的xpath和css的一些方法。具体大家还是再看看我的另外两篇笔记,现在暂时放在简书上,最近我会将其迁移过来。
传送门:
1 构造选择器(Constructing selectors)
我们预先构造一个选择器(Selectors)对象,然后在这个Selectors基础上进行操作。这个方法同样适用在我们单独编写的爬虫中,这样可以将Scrapy中的这些匹配方法迅速应用。另外我们也可以在Scrapy shell中进行匹配练习。
1.1 从文本(text)中构建选择器(selectors)
>>> from scrapy.selector import Selector
>>> body = '<html><body><span>good</span></body></html>'
>>> Selector(text=body).xpath('//span/text()').extract()
[u'good']
1.2 从响应(response)中构建选择器(selectors)
>>> from scrapy.http import HtmlResponse
>>> body = '<html><body><span>good</span></body></html>'.encode("utf-8")
>>> response = HtmlResponse(url='http://example.com', body=body)
>>> Selector(response=response).xpath('//span/text()').extract()
[u'good']
1.3 在shell中调试
我们同样也可以在Pycharm中打开命令窗口并打开shell。需要
scrapy shell "http://quotes.toscrape.com/page/1/"
2 选择器的使用(Using selectors)
2.1 在shell中的简单使用方法
在shell中输入命令后,尝试如下语句:
view(response) # 自动打开浏览器查看当前操作的网页
如果我们要获取这个网页的标题该如何操作呢?
使用css()方法或者xpath()方法如下:
>>> response.css('title::text').extract()
['Quotes to Scrape']
>>> response.xpath('//title/text()').extract_first()
'Quotes to Scrape'
extract返回的是选择器列表,为了防止后续通过索引的方式取值发生IndexError 应该使用下面的方法extract_first(),这等价于
>>> response.css('title::text')[0].extract()
'Quotes to Scrape'
这样我们就完成了第一个数据提取操作。
2.2 选择器的方法
在1.1中我们构建了一个response
>>> from scrapy.http import HtmlResponse
>>> body = '<html><body><span>good</span></body></html>'.encode("utf-8")
>>> response = HtmlResponse(url='http://example.com', body=body)
response对象的.selector属性可以用来访问选择器(selectors),也就是:
>>> response.selector.xpath('//span/text()').extract()
[u'good']
它和原来的等价:
Selector(response=response).xpath('//span/text()').extract()
同时也等价与:
>>> response.xpath('//span/text()').extract() # 重点
好了下面我们就用最后一句来展开本次的核心内容。
2.3 练习使用CSS和XPath
本次的文本内容如下,在此之前请通过上面的方法先构建一个response。
<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中进行操作。
scrapy shell "http://doc.scrapy.org/en/latest/_static/selectors-sample1.html"
2.3.1 从根节点访问标题文字
>>> response.xpath('//title/text()').extract_first()
>>> response.css('title::text').extract_first()
Example website
2.3.2 提取图片资源的地址
>>> 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']
2.3.3 提取文字
>>> response.css('a::text').extract()
>>> response.xpath('//a/text()').extract()
>>> response.xpath('//div[@id="images"]/a/text()').extract()
[u'Name: My image 1 ',
u'Name: My image 2 ',
u'Name: My image 3 ',
u'Name: My image 4 ',
u'Name: My image 5 ']
如果没有匹配到任何东西返回None,同时可以为extract_first设置默认值。
>>> response.xpath('//div[@id="not-exists"]/text()').extract_first() is None:
True
>>> response.xpath('//div[@id="not-exists"]/text()').extract_first(default='not-found')
'not-found'
2.3.4 提取base标签href属性的属性值
>>> response.css('base::attr(href)').extract()
>>> response.xpath('//base/@href').extract()
[u'http://example.com/']
2.3.5 XPath的starts-with和contains
- xpath中contains用于匹配一个属性中包含的字符串;
starts-with 匹配一个属性开始位置的关键字;
>>> response.xpath('//a[starts-with(@href, "image1")]/@href').extract() [u'image1.html'] >>> response.css('a[href^="image1"]::attr(href)').extract() [u'image1.html']
匹配图片所指向的网页
>>> response.xpath('//a[contains(@href, "image")]/@href').extract()
>>> 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()
>>> 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']
2.3.6 提取id="xxx"的div标签class属性的属性值
>>> response.css('div#1234::attr(class)').extract()
['quote']
2.3.7 提取div下面的子级或者后辈的文本
提取子级span的文本
>>> response.css('div.quote > span::text').extract()
['erzi']
提取所有后辈span的文本
>>> response.css('div.quote span::text').extract()
['sunzi', 'erzi']
2.3.8 提取和div平级的span的文本
提取和id="56"同级,位置在div标签下的所有<span>的文本
>>> response.css('div#56 ~ span::text').extract()
['erzi']
2.4 选择器的re()方法
选择器对象的css()和xpath()返回的是与原来相同类型的选择器对象列表,而选择器对象的re()方法返回的是unicode 字符串。
>>> 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'
简单解释下正则语句的意思:
re(r'Name:\s*(.*)')表示:
匹配开始字符为Name:
\s 表示空白字符,*表示零次或多次,那么\s*表示匹配空白字符零次或多次
() 表示对正则表达式进行嵌套
.(点)表示任意字符,那么(.*)便是匹配任意字符多次并返回
2.5 xpath的相对(relative XPaths)
- 从当前div标签中再探索p标签
>>> divs = response.xpath('//div')
>>> for p in divs.xpath('.//p'): # extracts all <p> inside
... print p.extract()
错误的做法:
- 从整个文档中获p标签
>>> for p in divs.xpath('//p'): # extracts all <p> inside
... print p.extract()
附上一张效果图:
2.6 在XPath中使用变量
2.6.1 匹配div标签属性值为images的元素
>>> response.xpath('//div[@id=$val]/a/text()', val='images').extract_first()
u'Name: My image 1 '
2.6.2 找到某个包含5个a标签的div标签
>>> response.xpath('//div[count(a)=$cnt]/@id', cnt=5).extract_first()
u'images'
2.6.3 返回全部文本
当使用文本内容作为匹配参数是不要使用text(),应该使用.(点)
>>> from scrapy import Selector >>> sel = Selector(text='<a href="#">Click here to go to the <strong>NextPage</strong></a>') >>> sel.xpath("//a[contains(.//text(), 'Next Page')]").extract() [] >>> sel.xpath("//a[contains(., 'Next Page')]").extract() [u'<a href="#">Click here to go to the <strong>Next Page</strong></a>']
提取文本内容
>>> sel.xpath("string(//a[1])").extract() >>> a_text = sel.xpath("//a") >>> content = a_text.xpath("string(.)").extract() [u'Click here to go to the Next Page']
2.7 XPath中节点表达式的区别
//node[1]:选择每块node第一个出现的node
(//node)[1]:选择全文的所有node节点中的第一个
>>> from scrapy import Selector
>>> sel = Selector(text="""
....: <ul class="list">
....: <li>1</li>
....: <li>2</li>
....: <li>3</li>
....: </ul>
....: <ul class="list">
....: <li>4</li>
....: <li>5</li>
....: <li>6</li>
....: </ul>""")
>>> xp = lambda x: sel.xpath(x).extract()
>>> xp("//li[1]")
[u'<li>1</li>', u'<li>4</li>']
>>> xp("(//li)[1]")
[u'<li>1</li>']
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。