本文地址:https://www.jianshu.com/p/df7e56f2024c
数据提取(Selector)
在Scrapy
中,封装了我们常用的提取数据的方式,有正则、Xpath、CSS选择器等。而且Selector
是基于lxml
构建的,这就意味着性能上不会有太大问题。
Xpath和CSS选择器
由于使用Xpath和CSS选择器来提取数据非常普遍,所以Scrapy
在response中设置了两个快捷接口,可以很方便的在response中使用Xpath和CSS选择器。
>>> response.xpath('//title/text()')
[<Selector (text) xpath=//title/text()>]
>>> response.css('title::text')
[<Selector (text) xpath=//title/text()>]
可以看到,使用Selector
获取的是一个Selector
对象的列表,所以可以嵌套和混合Xpath和CSS选择器使用:
>>> 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']
同时,由于获得的是一个Selector
对象,所以,如果需要实际的提取出文本数据,那么就要使用一个特殊的方法.extract()
,这个方法提取出的会是一个列表,包含的是所有符合条件的内容。
>>> response.xpath('//title/text()').extract()
[u'Example website']
如果只需要提取一个数据,那么可以使用.extract_first()
,这个方法会默认的提取出第一个结果。
>>> response.xpath('//div[@id="images"]/a/text()').extract_first()
u'Name: My image 1 '
这个方法如果没有提取到任何数据,那么将会返回一个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'
正则表达式
Selector
同样也提供了一个.re()
方法,可以快捷的使用正则提供数据,但是由于正则提取出的结果都是字符串,所以正则提取的结果不再能够像.xpath()
一样嵌套使用了。
>>> 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']
同时,对正则也提供了一个与.extract_first()
相对应地.re_first()
方法,可以只提取出第一个匹配的字符串。
>>> response.xpath('//a[contains(@href, "image")]/text()').re_first(r'Name:\s*(.*)')
u'My image 1'
Request
在Scrapy
中,请求和响应都被封装成了一个个Request
和Response
对象,方便传递和进行处理。
在Spider
的解析方法中,如果我们获得了接下来要访问的url,那么我们就需要使用yield Request()
的方式生成一个Request
对象,并返回出去。
这是目前大规模爬取基本都会使用的一个模式。
class scrapy.http.Request(url[, callback, method='GET', headers, body, cookies, meta, encoding='utf-8', priority=0, dont_filter=False, errback, flags])
参数
url (string) – 访问的URL,这是唯一一个必备参数。
callback (callable) – 回调函数,网页请求完毕之后,将使用这个函数来解析获得的响应。如果没有指定这个参数,那么
Scrapy
将会默认调用Spider
的parse()
来处理。需要注意的是,如果在处理的过程中触发了异常,则会调用errback。
method (string) – 请求方法,默认为
'GET'
。meta (dict) – 通过这个参数,可以将一些数据传递到接下来的
callback
中,传递数据使用的是浅拷贝的方式。body (str or unicode) – 请求体数据。
headers (dict) – 请求报头。字典的值可以为列表(多个值的报头)。如果值为None,那么不会发送任何报头。
cookies (dict or list) – cookies可以有字典和列表两种格式。
使用字典则可以正常的传递cookies
request_with_cookies = Request(url="http://www.example.com", cookies={'currency': 'USD', 'country': 'UY'})
如果使用的是包含着字典的列表,那么可以在字典中附带上cookies的domain和path等参数
request_with_cookies = Request(url="http://www.example.com", cookies=[{'name': 'currency', 'value': 'USD', 'domain': 'example.com', 'path': '/currency'}])
encoding (string) – 编码格式,默认为utf-8。
priority (int) – 优先级。
Scheduler
会使用这个参数来决定Request
的处理顺序,优先级的值越高,则排的位置越前,越早被处理。允许使用负值来指定较低的优先级。dont_filter (boolean) – 如果这个值设定为True,那么这个请求将不会被
Scheduler
筛选,常常被用来发起一些重复的请求。谨慎使用这个参数,因为容易使爬取陷入死循环中,默认为False。errback (callable) – 在处理request时触发异常时调用的异常处理函数。
Request属性与方法
Request这个对象里也有一些属性和方法,可以在后续的处理中进行使用。
url
请求的url。
method
请求的方法。
headers
请求的headers。
body
请求的body。
meta
请求的元数据。
copy()
复制出一个新的
Request
对象。replace([url, method, headers, body, cookies, meta, encoding, dont_filter, callback, errback])**
可以使用此方法将
Request
对象中的指定内容进行替换。
Request.meta中的特殊key
在构造Request
对象的时候,可以使用Request.meta
属性来传递一些数据到回调函数中。但是meta
这个属性不止有这个作用,其中还有一些特殊的key有特殊的作用。
它们分别是:
-
dont_redirect
– 禁止重定向 -
dont_retry
– 禁止重试 handle_httpstatus_list
handle_httpstatus_all
dont_merge_cookies
cookiejar
-
dont_cache
– 禁止缓存 redirect_urls
bindaddress
-
dont_obey_robotstxt
– 不遵守robots.txt -
download_timeout
– 下载超时时间 -
download_maxsize
– 最大下载大小 download_latency
download_fail_on_dataloss
-
proxy
– 设置代理 -
ftp_user
(See FTP_USER for more info) -
ftp_password
(See FTP_PASSWORD for more info) referrer_policy
-
max_retry_times
– 最大重试次数
FormRequest
FormRequest
是Request
的子类,主要是扩展了处理HTML表单的功能。
class scrapy.http.FormRequest(url[, formdata, ...])
FormRequest
添加了一个formdata
参数,其余的参数都与Request
相同。
- formdata(
dict
对象或者可迭代的一系列tuple
) – 这个属性包含了HTML的表单数据,并且会被url编码并分配给请求的请求体。
这个子类最大的作用是在发送POST请求时,能够使用字典发送POST提交的表单数据。
例子:
yield scrapy.FormRequest(url="http://www.example.com/post/action",
formdata={'name': 'John Doe', 'age': '27'},
callback=self.after_post)
系列文章: