zoukankan      html  css  js  c++  java
  • scrapy之Request对象

    我们在使用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
    View Code

    使用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
  • 相关阅读:
    BZOJ 3208: 花神的秒题计划Ⅰ
    BZOJ 3207: 花神的嘲讽计划Ⅰ
    BZOJ 2732: [HNOI2012]射箭
    BZOJ 3165: [Heoi2013]Segment
    BZOJ 3626: [LNOI2014]LCA
    2017 01 16 校内小测 ZXR专场
    BZOJ 3101: N皇后
    BZOJ 1106: [POI2007]立方体大作战tet
    BZOJ 2084: [Poi2010]Antisymmetry
    【UOJ#228】基础数据结构练习题 线段树
  • 原文地址:https://www.cnblogs.com/chaojiyingxiong/p/10220511.html
Copyright © 2011-2022 走看看