我们在使用scrapy框架的时候,会经常疑惑,数据流是怎么样在各个组件中间传递的。最近经常用scrapy+selenium爬取淘宝,又因为今天周五心情好,本宝宝决定梳理一下这方面知识。

scrapy中各个组件相互通信的方式是通过request对象和response对象来完成的。也就是说spider和middleware之间的数据传递时通过这两个对象传递的。request对象是在spider中产生的,看代码:

from scrapyseleniumtest.items import ProductItem

class TaobaoSpider(Spider):
name = 'taobao'
allowed_domains = ['www.taobao.com']
base_url = 'https://s.taobao.com/search?q=' def start_requests(self):
for keyword in self.settings.get('KEYWORDS'):
for page in range(1, self.settings.get('MAX_PAGE') + 1):
url = self.base_url + quote(keyword)
yield Request(url=url, callback=self.parse, meta={'page': page}, dont_filter=True)

这个是scrapy中的spider,大家看最后的yield Request(url=url, callback=self.parse, meta={'page': page}, dont_filter=True),这就是将Request类实例化了一个request对象,通过request对象来传递数据。比如在middleware.py中

class SeleniumMiddleware():
def __init__(self, timeout=None, service_args=[]):
self.logger = getLogger(__name__)
self.timeout = timeout
self.browser = webdriver.Firefox(executable_path="geckodriver.exe")
self.browser.set_window_size(1400, 700)
self.browser.set_page_load_timeout(self.timeout)
self.wait = WebDriverWait(self.browser, self.timeout) def __del__(self):
self.browser.close() def process_request(self, request, spider):
"""
用PhantomJS抓取页面
:param request: Request对象
:param spider: Spider对象
:return: HtmlResponse
"""
self.logger.debug('PhantomJS is Starting')
page = request.meta.get('page', 1)

  在process_request(self, request, spider)中,我们看到了第二个参数是request,这个就是request对象,一个request对象代表一个HTTP请求,通常有Spider产生,经Downloader执行从而产生一个Response。但是呢,这里我们使用了selenium,这个reponse就不是Downloader执行产生的了,而是由火狐浏览器对象browser代替Downloader完成了下载(页面加载),然后构筑了一个HtmlResponse对象,返回给了spider进行解析,request对象到这里也就不在继续处理了。看downloadmiddleware的完整代码:

from selenium import webdriver
from selenium.common.exceptions import TimeoutException
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from scrapy.http import HtmlResponse
from logging import getLogger class SeleniumMiddleware():
def __init__(self, timeout=None, service_args=[]):
self.logger = getLogger(__name__)
self.timeout = timeout
self.browser = webdriver.Firefox(executable_path="geckodriver.exe")
self.browser.set_window_size(1400, 700)
self.browser.set_page_load_timeout(self.timeout)
self.wait = WebDriverWait(self.browser, self.timeout) def __del__(self):
self.browser.close() def process_request(self, request, spider):
"""
用PhantomJS抓取页面
:param request: Request对象
:param spider: Spider对象
:return: HtmlResponse
"""
self.logger.debug('PhantomJS is Starting')
page = request.meta.get('page', 1)
try:
self.browser.get(request.url)
if page > 1:
input = self.wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, '#mainsrp-pager div.form > input')))
submit = self.wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, '#mainsrp-pager div.form > span.btn.J_Submit')))
input.clear()
input.send_keys(page)
submit.click()
self.wait.until(
EC.text_to_be_present_in_element((By.CSS_SELECTOR, '#mainsrp-pager li.item.active > span'), str(page)))
self.wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, '.m-itemlist .items .item')))
return HtmlResponse(url=request.url, body=self.browser.page_source, request=request, encoding='utf-8',
status=200)
except TimeoutException:
return HtmlResponse(url=request.url, status=500, request=request) @classmethod
def from_crawler(cls, crawler):
return cls(timeout=crawler.settings.get('SELENIUM_TIMEOUT'),
service_args=crawler.settings.get('PHANTOMJS_SERVICE_ARGS'))

然后根据scrapy官方文档的解释,看看request对象的一些具体参数:

1,Request objects

class scrapy.http.Request(url[, callback, method='GET', headers, body, cookies, meta, encoding='utf-8', priority=0, dont_filter=False, errback])

一个request对象代表一个HTTP请求,通常有Spider产生,经Downloader执行从而产生一个Response。

Paremeters:     url(string): 用于请求的URL

callback(callable):指定一个回调函数,该回调函数以这个request是的response作为第一个参数。如果未指定callback,

则默认使用spider的parse()方法。

method(string):HTTP请求的方法,默认为GET(看到GET你应该明白了,过不不明白建议先学习urllib或者requets模块)

meta(dict):指定Request.meta属性的初始值。如果给了该参数,dict将会浅拷贝。(浅拷贝不懂的赶紧回炉)

body(str):the request body.(这个没有理解,若有哪位大神明白,请指教,谢谢)

headers(dict):request的头信息。

cookies(dict or list):cookie有两种格式。

1、使用dict:

request_with_cookies = Request(url="http://www.example.com", cookies={'currency': 'USD', 'country': 'UY'})

2、使用字典的list

request_with_cookies = Request(url="http://www.example.com",
cookies=[{'name': 'currency',
'value': 'USD',
'domain': 'example.com',
'path': '/currency'}])

后面这种形式可以定制cookie的domain和path属性,只有cookies为接下来的请求保存的时候才有用。

当网站在response中返回cookie时,这些cookie将被保存以便未来的访问请求。这是常规浏览器的行为。如果你想避免修改当前

正在使用的cookie,你可以通过设置Request.meta中的dont_merge_cookies为True来实现。

request_with_cookies = Request(url="http://www.example.com",
cookies={'currency': 'USD', 'country': 'UY'},
meta={'dont_merge_cookies': True})

encoding(string):请求的编码, 默认为utf-8

priority(int):请求的优先级

dont_filter(boolean):指定该请求是否被 Scheduler过滤。该参数可以是request重复使用(Scheduler默认过滤重复请求)。谨慎使用!!

errback(callable):处理异常的回调函数。

属性和方法:

url: 包含request的URL的字符串

method: 代表HTTP的请求方法的字符串,例如'GET', 'POST'...

headers: request的头信息

body: 请求体

meta: 一个dict,包含request的任意元数据。该dict在新Requests中为空,当Scrapy的其他扩展启用的时候填充数据。dict在传输是浅拷贝。

copy(): 拷贝当前Request

replace([url, method, headers, body, cookies, meta, encoding, dont_filter, callback, errback]): 返回一个参数相同的Request,

可以为参数指定新数据。

给回调函数传递数据

当request的response被下载是,就会调用回调函数,并以response对象为第一个参数

def parse_page1(self, response):
return scrapy.Request("http://www.example.com/some_page.html",
callback=self.parse_page2) def parse_page2(self, response):
# this would log http://www.example.com/some_page.html
self.logger.info("Visited %s", response.url)

example

在某些情况下,你希望在回调函数们之间传递参数,可以使用Request.meta。(其实有点类似全局变量的赶脚)

def parse_page1(self, response):
item = MyItem()
item['main_url'] = response.url
request = scrapy.Request("http://www.example.com/some_page.html",
callback=self.parse_page2)
request.meta['item'] = item
yield request def parse_page2(self, response):
item = response.meta['item']
item['other_url'] = response.url
yield item

使用errback来捕获请求执行中的异常

当request执行时有异常抛出将会调用errback回调函数。

它接收一个Twisted Failure实例作为第一个参数,并被用来回溯连接超时或DNS错误等。

 1 import scrapy
2
3 from scrapy.spidermiddlewares.httperror import HttpError
4 from twisted.internet.error import DNSLookupError
5 from twisted.internet.error import TimeoutError, TCPTimedOutError
6
7 class ErrbackSpider(scrapy.Spider):
8 name = "errback_example"
9 start_urls = [
10 "http://www.httpbin.org/", # HTTP 200 expected
11 "http://www.httpbin.org/status/404", # Not found error
12 "http://www.httpbin.org/status/500", # server issue
13 "http://www.httpbin.org:12345/", # non-responding host, timeout expected
14 "http://www.httphttpbinbin.org/", # DNS error expected
15 ]
16
17 def start_requests(self):
18 for u in self.start_urls:
19 yield scrapy.Request(u, callback=self.parse_httpbin,
20 errback=self.errback_httpbin,
21 dont_filter=True)
22
23 def parse_httpbin(self, response):
24 self.logger.info('Got successful response from {}'.format(response.url))
25 # do something useful here...
26
27 def errback_httpbin(self, failure):
28 # log all failures
29 self.logger.error(repr(failure))
30
31 # in case you want to do something special for some errors,
32 # you may need the failure's type:
33
34 if failure.check(HttpError):
35 # these exceptions come from HttpError spider middleware
36 # you can get the non-200 response
37 response = failure.value.response
38 self.logger.error('HttpError on %s', response.url)
39
40 elif failure.check(DNSLookupError):
41 # this is the original request
42 request = failure.request
43 self.logger.error('DNSLookupError on %s', request.url)
44
45 elif failure.check(TimeoutError, TCPTimedOutError):
46 request = failure.request
47 self.logger.error('TimeoutError on %s', request.url)

example

Request.meta的特殊关键字

Request.meta可以包含任意的数据,但Scrapy和内置扩展提供了一些特殊的关键字

  • dont_redirect             (其实dont就是don't,嗯哼~)
  • dont_retry
  • handle_httpstatus_list
  • handle_httpstatus_all
  • dont_merge_cookies (see cookies parameter of Request constructor)
  • cookiejar
  • dont_cache
  • redirect_urls
  • bindaddress
  • dont_obey_robotstxt
  • download_timeout(下载超时)
  • download_maxsize
  • download_latency(下载延时)
  • proxy

scrapy之Request对象的更多相关文章

  1. Scrapy 中 Request 对象和 Response 对象的各参数及属性介绍

    Request 对象 Request构造器方法的参数列表: Request(url [, callback=None, method='GET', headers=None, body=None,co ...

  2. Scrapy框架--Requests对象

    Scrapy使用request对象来爬取web站点. request对象由spiders对象产生,经由Scheduler传送到Downloader,Downloader执行request并返回resp ...

  3. Scrapy的Request和Response对象

    一.Request 发送一个请求,参数如下: url :request对象发送请求的url callback :在下载器下载完相应的数据后执行的回调函数 method :请求方法,默认为get hea ...

  4. Scrapy 中的 Request 对象和 Respionse 对象

    1.Request 对象 Request 对象用来描述一个 HTTP 请求,下面是其构造方法的参数列表 Request(url, [, callback, method='Get', headers, ...

  5. scrapy的request的meta参数是什么意思?

    作者:乌尔班链接:https://www.zhihu.com/question/54773510/answer/146971644来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...

  6. python的scrapy框架的使用 和xpath的使用 && scrapy中request和response的函数参数 && parse()函数运行机制

    这篇博客主要是讲一下scrapy框架的使用,对于糗事百科爬取数据并未去专门处理 最后爬取的数据保存为json格式 一.先说一下pyharm怎么去看一些函数在源码中的代码实现 按着ctrl然后点击函数就 ...

  7. Scrapy的Request和Response

    Scrapy的Request和Response   本文链接:https://blog.csdn.net/kissazhu/article/details/80865773 上节课我们学习了中间件,知 ...

  8. request 对象和 response 对象

    Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象.和代表响应的response对象 HttpServletResponse HttpServletR ...

  9. JSP内置对象之request对象【学习笔记】

    request对象是JSP中重要的对象,每个request对象封装着一次用户的请求,并且所有的请求参数都被封装在request对象中,因此request对象是获取请求参数的重要途径. 一.获取请求头与 ...

随机推荐

  1. EditPlus配色方案

    找到配置文件:editplus_u.ini配置文件 [Options] Placement=2C00000002000000030000000083FFFF0083FFFFFFFFFFFFFFFFFF ...

  2. 1.关于狗书《Flask Web开发 基于Python的web开发应用实战》身份验证的改进

    在我学习用户身份验证的时候,我发现这里有个小弊端,在用户注册完成后想要验证邮箱的时候,点击邮箱中的网址进行验证,此时还要登陆,这及其不符合我们的习惯.一般情况下我们只需要点击网址就可以验证成功并且进入 ...

  3. WPF资源字典的使用

    1.在解决方案中添加资源字典:鼠标右键——添加——资源字典 2.在资源字典中写入你需要的样式,我这里简单的写了一个窗体的边框样式 3.在App.xaml中加入刚刚新建的资源字典就好了

  4. js获取select下拉框中的值

    现在有一id为userType的下拉框,怎么获取选中的值: 用户类型: <select name="type" id="userType"> < ...

  5. [AGC035F]Two Histograms

    Description 你有一个 \(N\) 行.\(M\) 列的.每个格子都填写着 0 的表格.你进行了下面的操作: 对于每一行 \(i\) ,选定自然数 \(r_i\ (0 ≤ r i ≤ M ) ...

  6. BZOJ 1878(离散化+线段树)

    题面 传送门 分析 首先我们观察到区间范围较大,而区间个数较少,考虑离散化,将所有询问按照右端点进行排序 离散化之后研究区间颜色个数变化的规律 当我们处理到第a[i]个段时,设a[i]上一次出现的地方 ...

  7. Codeforces 1159E Permutation recovery(构造+拓扑)

    这道题其实只要解决了什么时候输出 -1 ,那么此题的构造方法也就解决了.首先我们可以观察这组 3 3 4 和 3 4 4 ,可以算出第二组是不成立的,在观察一组 2 3 4 5 和  3 2 4 5 ...

  8. UI标签库专题十:JEECG智能开发平台 Form(form标签)

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/zhangdaiscott/article/details/30099121  1. Form(f ...

  9. controller函数中参数列表使用多个@RequestBody

    首先出现这种情况是因为有下面这种需求 $.ajax({ type: "POST", url: "${pageContext.request.contextPath}/co ...

  10. 一台电脑关联多个git账号

    一台电脑连接多个git账号 现需要一台电脑连接gitlab,github,码云,之前的操作时,用公司账号,在这几个地方都注册一遍,导致自己就有两类号,一个自己的,一个公司的,这样也是可以,但总是不太好 ...