文档章节

Scrapy 1.5.0之选择器

Eappo_Geng
 Eappo_Geng
发布于 09/24 23:45
字数 2338
阅读 14
收藏 0

构造选择器

Scrapy选择器是通过文本(Text)或 TextResponse 对象构造的 Selector 类的实例。

它根据输入类型自动选择最佳的解析规则(XML vs HTML):

>>> from scrapy.selector import Selector
>>> from scrapy.http import HtmlResponse

文本构造:

>>> body = '<html><body><span>good</span></body></html>'
>>> Selector(text=body).xpath('//span/text()').extract()
[u'good']

从response构造: (调试未成功)

>>> response = HtmlResponse(url='http://example.com', body=body)
>>> Selector(response=response).xpath('//span/text()').extract()
[u'good']

为了方便起见,response对象以 .selector 属性提供了一个selector, 您可以随时使用该快捷方法:

>>> res.selector.xpath('//span/text()').extract()
[u'good']

使用选择器

为了解释如何使用选择器,我们将使用 Scrapy shell(提供交互式测试)和位于Scrapy文档服务器中的示例页面:

我们将使用 Scrapy shell(提供交互式测试)和位于Scrapy文档服务器中的示例页面:http://doc.scrapy.org/en/latest/_static/selectors-sample1.html

scrapy shell http://doc.scrapy.org/en/latest/_static/selectors-sample1.html
<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>

由于在response中使用XPath、CSS查询十分普遍,因此,Scrapy提供了两个实用的快捷方式: response.xpath()response.css():

>>>response.xpath('//title/text()')
[<Selector xpath='//title/text()' data='Example website'>]

>>>response.css('title::text').extract()
[<Selector xpath='//title/text()' data='Example website'>]

.xpath().css() 方法返回一个 SelectorList 实例,它是一个新的选择器列表。

此API可用于快速提取嵌套数据。

为了实际提取文本数据,必须调用选择器 .extract() 方法,如下所示:

 >>> response.xpath('//title/text()').extract()
['Example website']

如果只想提取第一个匹配的元素,可以调用 .extract_first()

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

如果没有匹配的元素,则返回 None,可以提供默认返回值作为参数,而不使用None。

>>> 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'

获取base标签下的URL和一些图片的链接

In [16]: response.css('base::attr(href)')
Out[16]: [<Selector xpath='descendant-or-self::base/@href' data='http://example.com/'>]

In [17]: response.xpath('//base/@href')
Out[17]: [<Selector xpath='//base/@href' data='http://example.com/'>]
In [19]: response.css('a::attr(href)')
Out[19]: 
[<Selector xpath='descendant-or-self::a/@href' data='image1.html'>,
 <Selector xpath='descendant-or-self::a/@href' data='image2.html'>,
 <Selector xpath='descendant-or-self::a/@href' data='image3.html'>,
 <Selector xpath='descendant-or-self::a/@href' data='image4.html'>,
 <Selector xpath='descendant-or-self::a/@href' data='image5.html'>]


In [21]: response.xpath('//a/@href')
Out[21]: 
[<Selector xpath='//a/@href' data='image1.html'>,
 <Selector xpath='//a/@href' data='image2.html'>,
 <Selector xpath='//a/@href' data='image3.html'>,
 <Selector xpath='//a/@href' data='image4.html'>,
 <Selector xpath='//a/@href' data='image5.html'>]

嵌套选择器

选择器方法( .xpath() or .css() )返回相同类型的选择器列表,因此你也可以对这些选择器调用选择器方法。

css和xpath可以混合嵌套。

>>> links = response.xpath('//a[contains(@href, "image")]')
>>> links.extract()
[u'<a href="image1.html">Name: My image 1 <br><img src="image1_thumb.jpg"></a>',
 u'<a href="image2.html">Name: My image 2 <br><img src="image2_thumb.jpg"></a>',
 u'<a href="image3.html">Name: My image 3 <br><img src="image3_thumb.jpg"></a>',
 u'<a href="image4.html">Name: My image 4 <br><img src="image4_thumb.jpg"></a>',
 u'<a href="image5.html">Name: My image 5 <br><img src="image5_thumb.jpg"></a>']

>>> 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() 方法,用来通过正则表达式来提取数据。然而,不同于使用 .xpath() 或者 .css() 方法, .re() 方法返回unicode字符串的列表。所以你无法构造嵌套式的 .re() 调用。

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

还有一个额外的辅助函数 .extract_first() for .re(),命名为 .re_first()。使用它只提取第一个匹配的字符串:

In [35]: response.xpath('//a[contains(@href, "image")]/text()').re_first('Name:\s(.*?)image\s(\d)')
Out[35]: 'My '

使用相对XPaths

如果您嵌套选择器并使用起始为 / 的 XPath,该XPath将对文档使用绝对路径,而且对于你调用的 Selector 不是相对路径。

假设要提取 ‘div’ 中的所有元素。首先,你会得到所有的 ‘div’:

>>> divs = response.xpath('//div')

首先,你可能会使用下面的方法,这是错误的,因为它实际上从文档中提取所有 <p> 元素,而不仅仅是 <div> 元素内的元素:

>>> for p in divs.xpath('//p'):  # this is wrong - gets all <p> from the whole document
...     print p.extract()

这是正确的方式(注意点前面的 .//p XPath):

>>> for p in divs.xpath('.//p'):  # extracts all <p> inside
...     print p.extract()

另一个常见的情况是提取所有直接的子标签 <p>

>>> for p in divs.xpath('p'):
...     print p.extract()

使用EXSLT扩展

因构建在 lxml 之上,Scrapy选择器也支持一些 EXSLT 扩展,可以在XPath表达式中使用这些预先制定的命名空间:

前缀 命名空间 用法
re http://exslt.org/regular-expressions http://exslt.org/regexp/index.html
set http://exslt.org/sets http://exslt.org/set/index.html

正则表达式

例如,当XPath的starts-with()或contains()不能满足需求时,test()函数可以证明是非常有用的。

例如在列表中选择有”class”元素且结尾为一个数字的链接:

>>> from scrapy import Selector
>>> doc = """
... <div>
...     <ul>
...         <li class="item-0"><a href="link1.html">first item</a></li>
...         <li class="item-1"><a href="link2.html">second item</a></li>
...         <li class="item-inactive"><a href="link3.html">third item</a></li>
...         <li class="item-1"><a href="link4.html">fourth item</a></li>
...         <li class="item-0"><a href="link5.html">fifth item</a></li>
...     </ul>
... </div>
... """
>>> sel = Selector(text=doc, type="html")
>>> sel.xpath('//li//@href').extract()
[u'link1.html', u'link2.html', u'link3.html', u'link4.html', u'link5.html']
>>> sel.xpath('//li[re:test(@class, "item-\d$")]//@href').extract()
[u'link1.html', u'link2.html', u'link4.html', u'link5.html']
>>>

集合操作

集合操作可以方便地在提取文本元素之前去除文档树中的一部分内容。

使用itemscopes组和相应的itemprops组提取微数据(从http://schema.org/Product获取的示例内容)示例:

>>> doc = """
... <div itemscope itemtype="http://schema.org/Product">
...   <span itemprop="name">Kenmore White 17" Microwave</span>
...   <img src="kenmore-microwave-17in.jpg" alt='Kenmore 17" Microwave' />
...   <div itemprop="aggregateRating"
...     itemscope itemtype="http://schema.org/AggregateRating">
...    Rated <span itemprop="ratingValue">3.5</span>/5
...    based on <span itemprop="reviewCount">11</span> customer reviews
...   </div>
...
...   <div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
...     <span itemprop="price">$55.00</span>
...     <link itemprop="availability" href="http://schema.org/InStock" />In stock
...   </div>
...
...   Product description:
...   <span itemprop="description">0.7 cubic feet countertop microwave.
...   Has six preset cooking categories and convenience features like
...   Add-A-Minute and Child Lock.</span>
...
...   Customer reviews:
...
...   <div itemprop="review" itemscope itemtype="http://schema.org/Review">
...     <span itemprop="name">Not a happy camper</span> -
...     by <span itemprop="author">Ellie</span>,
...     <meta itemprop="datePublished" content="2011-04-01">April 1, 2011
...     <div itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating">
...       <meta itemprop="worstRating" content = "1">
...       <span itemprop="ratingValue">1</span>/
...       <span itemprop="bestRating">5</span>stars
...     </div>
...     <span itemprop="description">The lamp burned out and now I have to replace
...     it. </span>
...   </div>
...
...   <div itemprop="review" itemscope itemtype="http://schema.org/Review">
...     <span itemprop="name">Value purchase</span> -
...     by <span itemprop="author">Lucas</span>,
...     <meta itemprop="datePublished" content="2011-03-25">March 25, 2011
...     <div itemprop="reviewRating" itemscope itemtype="http://schema.org/Rating">
...       <meta itemprop="worstRating" content = "1"/>
...       <span itemprop="ratingValue">4</span>/
...       <span itemprop="bestRating">5</span>stars
...     </div>
...     <span itemprop="description">Great microwave for the price. It is small and
...     fits in my apartment.</span>
...   </div>
...   ...
... </div>
... """
>>> sel = Selector(text=doc, type="html")
>>> for scope in sel.xpath('//div[@itemscope]'):
...     print "current scope:", scope.xpath('@itemtype').extract()
...     props = scope.xpath('''
...                 set:difference(./descendant::*/@itemprop,
...                                .//*[@itemscope]/*/@itemprop)''')
...     print "    properties:", props.extract()
...     print

current scope: [u'http://schema.org/Product']
    properties: [u'name', u'aggregateRating', u'offers', u'description', u'review', u'review']

current scope: [u'http://schema.org/AggregateRating']
    properties: [u'ratingValue', u'reviewCount']

current scope: [u'http://schema.org/Offer']
    properties: [u'price', u'availability']

current scope: [u'http://schema.org/Review']
    properties: [u'name', u'author', u'datePublished', u'reviewRating', u'description']

current scope: [u'http://schema.org/Rating']
    properties: [u'worstRating', u'ratingValue', u'bestRating']

current scope: [u'http://schema.org/Review']
    properties: [u'name', u'author', u'datePublished', u'reviewRating', u'description']

current scope: [u'http://schema.org/Rating']
    properties: [u'worstRating', u'ratingValue', u'bestRating']

>>>

其中props = scope.xpath(''' set:difference(./descendant::/@itemprop, .//[@itemscope]/*/@itemprop)''')的 ‘descendant’ 取得是子节点,set::difference(a, b)在a里面却不在b里面的结果。

注意 //node[1] 和 (//node)[1] 的区别

  • //node[1] 选择在其各自父节点下首先出现的所有 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()

This gets all first <li> elements under whatever it is its parent:

>>> xp("//li[1]")
[u'<li>1</li>', u'<li>4</li>']

And this gets the first <li> element in the whole document:

>>> xp("(//li)[1]")
[u'<li>1</li>']

This gets all first <li> elements under an <ul> parent:

>>> xp("//ul/li[1]")
[u'<li>1</li>', u'<li>4</li>']

And this gets the first<li> element under an <ul> parent in the whole document:

>>> xp("(//ul/li)[1]")
[u'<li>1</li>']

当按类查询时,请考虑使用CSS

因为一个元素可以包含多个CSS类,XPath选择元素的方法是相当冗长:

*[contains(concat(' ', normalize-space(@class), ' '), ' someclass ')]

如果您使用 @class='someclass',你可能会丢失有其他类的元素,如果你只是使用 contains(@class,'someclass') 来弥补,您可能会得到更多的您想要的元素,前提是他们的类名中都包含字符串 someclass 。 事实证明,Scrapy 选择器允许你链接选择器,所以大多数时候你可以使用CSS选择类,然后在需要时切换到XPath:

>>> 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']

这比使用上面显示的详细XPath技巧更清晰。记得在XPath表达式中使用 . 来表示相对路径。

© 著作权归作者所有

共有 人打赏支持
Eappo_Geng
粉丝 4
博文 74
码字总数 68102
作品 0
徐汇
程序员
私信 提问
Scrapy 1.5.0之Spider

爬虫原理 创建一个以百度为名的爬虫,其中spiders/baidu.py内容如下: 对spider来说,爬取的过程如下: 以初始的URL初始化Request,并设置回调函数。 当该request下载完毕并返回时,将生成res...

Eappo_Geng
06/12
0
0
Scrapy 安装和使用

安装 新建scrapy项目 目录 scrapy模板 使用pycharm调试scrapy执行流程 scrapy 终端调试 xpath 使用 简介 节点关系 语法1 语法2-谓语 语法3 css选择器 css选择器1 css选择器2 css选择器3...

Meet相识_bfa5
2017/11/03
0
0
Scrapy 1.5.0之命令行

配置参数 系统层面: , 用户层面: () 及 () 作全局设置 项目定义: 在scrapy项目根路径下的 . 优先权:项目定义>用户层面>系统层面 Scrapy也会读取并通过环境变量来设置. 目前支持的有: scr...

Eappo_Geng
06/11
0
0
学习笔记之爬虫篇

网络爬虫(又被称为网页蜘蛛,网络机器人,更经常的称为网页追逐者),是一种按照一定的规则,自动的抓取万维网信息的程序或者脚本。另外一些不常使用的名字还有蚂蚁,自动索引,模拟程序或者...

董小洋
2017/08/22
0
0
scrapy的Response和Selector编码格式介绍

一、response介绍 当spider发出请求之后会返回response。response是一个类,其中包括一些内置的属性。 body body是response返回的内容,它的类型是str,所以body的内容编码类型是爬取页面的原...

mickelfeng
2016/08/05
317
0

没有更多内容

加载失败,请刷新页面

加载更多

ElasticSearch实战:Linux日志对接Kibana

本文由云+社区发表 ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTFul web接口。ElasticSearch是用Java开发的,并作为Apache许可条款下...

腾讯云加社区
2分钟前
0
0
FeignClient超时配置

1前沿 使用Feign调用接口分两层,ribbon的调用和hystrix的调用,所以ribbon的超时时间和Hystrix的超时时间的结合就是Feign的超时时间 1.1ribbon配置 ribbon: OkToRetryOnAllOperations: f...

lovelan1314
5分钟前
0
0
分布式ID

grace_233
7分钟前
0
0
spring boot集成socketIO

spring boot集成netty-socketio java用socket给用户分组,然后给分组发送消息,或者给单个人发送消息 第一步:加载netty-socketio包 <dependency><groupId>com.corundumstudio.socketio<......

小马_wolf
10分钟前
0
0
Linux无法写入权限问题 & 解决Wordpress不能自动安装主题、插件

正 文: 当你的wordpress遇到以下问题时: 1、不能上传图片 2、不能自动安装主题、插件(需要FTP账户) 3、不能自动更新 4、其它任何需要wordpress写文件的问题 这些问题基本都是一个原因,你...

Yao--靠自己
14分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部