Scrapy爬虫入门教程十一 Request和Response(请求和响应)

Python版本管理:pyenv和pyenv-virtualenv
Scrapy爬虫入门教程一 安装和基本使用
Scrapy爬虫入门教程二 官方提供Demo
Scrapy爬虫入门教程三 命令行工具介绍和示例
Scrapy爬虫入门教程四 Spider(爬虫)
Scrapy爬虫入门教程五 Selectors(选择器)
Scrapy爬虫入门教程六 Items(项目)
Scrapy爬虫入门教程七 Item Loaders(项目加载器)
Scrapy爬虫入门教程八 交互式 shell 方便调试
Scrapy爬虫入门教程九 Item Pipeline(项目管道)
Scrapy爬虫入门教程十 Feed exports(导出文件)
Scrapy爬虫入门教程十一 Request和Response(请求和响应)
Scrapy爬虫入门教程十二 Link Extractors(链接提取器)

开发环境:
Python 3.6.0 版本 (当前最新)
Scrapy 1.3.2 版本 (当前最新)

请求和响应

Scrapy的Request和Response对象用于爬网网站。

通常,Request对象在爬虫程序中生成并传递到系统,直到它们到达下载程序,后者执行请求并返回一个Response对象,该对象返回到发出请求的爬虫程序。

上面一段话比较拗口,有web经验的同学,应该都了解的,不明白看下面的图大概理解下。

爬虫->Request:创建
Request->Response:获取下载数据
Response->爬虫:数据

两个类Request和Response类都有一些子类,它们添加基类中不需要的功能。这些在下面的请求子类和 响应子类中描述。


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请求,它通常是在爬虫生成,并由下载执行,从而生成Response。

  • 参数:
    • url(string) - 此请求的网址

    • callback(callable) - 将使用此请求的响应(一旦下载)作为其第一个参数调用的函数。有关更多信息,请参阅下面的将附加数据传递给回调函数。如果请求没有指定回调,parse()将使用spider的 方法。请注意,如果在处理期间引发异常,则会调用errback。

    • method(string) - 此请求的HTTP方法。默认为'GET'。

    • meta(dict) - 属性的初始值Request.meta。如果给定,在此参数中传递的dict将被浅复制。

    • body(str或unicode) - 请求体。如果unicode传递了a,那么它被编码为 str使用传递的编码(默认为utf-8)。如果 body没有给出,则存储一个空字符串。不管这个参数的类型,存储的最终值将是一个str(不会是unicode或None)。

    • headers(dict) - 这个请求的头。dict值可以是字符串(对于单值标头)或列表(对于多值标头)。如果 None作为值传递,则不会发送HTTP头。

    • cookie(dict或list) - 请求cookie。这些可以以两种形式发送。

      • 使用dict:
      request_with_cookies = Request(url="http://www.example.com",
                                     cookies={'currency': 'USD', 'country': 'UY'})
      
    * 使用列表:

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

后一种形式允许定制 cookie的属性domain和path属性。这只有在保存Cookie用于以后的请求时才有用。

当某些网站返回Cookie(在响应中)时,这些Cookie会存储在该域的Cookie中,并在将来的请求中再次发送。这是任何常规网络浏览器的典型行为。但是,如果由于某种原因,您想要避免与现有Cookie合并,您可以通过将dont_merge_cookies关键字设置为True 来指示Scrapy如此操作 Request.meta。

不合并Cookie的请求示例:

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

有关详细信息,请参阅CookiesMiddleware

  • encoding(string) - 此请求的编码(默认为'utf-8')。此编码将用于对URL进行百分比编码,并将正文转换为str(如果给定unicode)。

  • priority(int) - 此请求的优先级(默认为0)。调度器使用优先级来定义用于处理请求的顺序。具有较高优先级值的请求将较早执行。允许负值以指示相对低优先级。

  • dont_filter(boolean) - 表示此请求不应由调度程序过滤。当您想要多次执行相同的请求时忽略重复过滤器时使用。小心使用它,或者你会进入爬行循环。默认为False。

  • errback(callable) - 如果在处理请求时引发任何异常,将调用的函数。这包括失败的404 HTTP错误等页面。它接收一个Twisted Failure实例作为第一个参数。有关更多信息,请参阅使用errbacks在请求处理捕获异常

  • url
    包含此请求的网址的字符串。请记住,此属性包含转义的网址,因此它可能与构造函数中传递的网址不同。

此属性为只读。更改请求使用的URL replace()

  • method
    表示请求中的HTTP方法的字符串。这保证是大写的。例如:"GET","POST","PUT"

  • headers
    包含请求标头的类似字典的对象。

  • body
    包含请求正文的str。

此属性为只读。更改请求使用的正文 replace()

  • meta
    包含此请求的任意元数据的字典。此dict对于新请求为空,通常由不同的Scrapy组件(扩展程序,中间件等)填充。因此,此dict中包含的数据取决于您启用的扩展。

有关Scrapy识别的特殊元键列表,请参阅Request.meta特殊键

当使用or 方法克隆请求时,此dict是浅复制的 ,并且也可以在您的爬虫中从属性访问。copy()replace()response.meta

  • copy()
    返回一个新的请求,它是这个请求的副本。另请参见: 将附加数据传递到回调函数

  • replace([url, method, headers, body, cookies, meta, encoding, dont_filter, callback, errback])
    返回具有相同成员的Request对象,但通过指定的任何关键字参数赋予新值的成员除外。该属性Request.meta是默认复制(除非新的值在给定的meta参数)。另请参见 将附加数据传递给回调函数

将附加数据传递给回调函数

请求的回调是当下载该请求的响应时将被调用的函数。将使用下载的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)

在某些情况下,您可能有兴趣向这些回调函数传递参数,以便稍后在第二个回调中接收参数。您可以使用该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

使用errbacks在请求处理中捕获异常

请求的errback是在处理异常时被调用的函数。

它接收一个Twisted Failure实例作为第一个参数,并可用于跟踪连接建立超时,DNS错误等。

这里有一个示例爬虫记录所有错误,并捕获一些特定的错误,如果需要:

import scrapy

from scrapy.spidermiddlewares.httperror import HttpError
from twisted.internet.error import DNSLookupError
from twisted.internet.error import TimeoutError, TCPTimedOutError

class ErrbackSpider(scrapy.Spider):
    name = "errback_example"
    start_urls = [
        "http://www.httpbin.org/",              # HTTP 200 expected
        "http://www.httpbin.org/status/404",    # Not found error
        "http://www.httpbin.org/status/500",    # server issue
        "http://www.httpbin.org:12345/",        # non-responding host, timeout expected
        "http://www.httphttpbinbin.org/",       # DNS error expected
    ]

    def start_requests(self):
        for u in self.start_urls:
            yield scrapy.Request(u, callback=self.parse_httpbin,
                                    errback=self.errback_httpbin,
                                    dont_filter=True)

    def parse_httpbin(self, response):
        self.logger.info('Got successful response from {}'.format(response.url))
        # do something useful here...

    def errback_httpbin(self, failure):
        # log all failures
        self.logger.error(repr(failure))

        # in case you want to do something special for some errors,
        # you may need the failure's type:

        if failure.check(HttpError):
            # these exceptions come from HttpError spider middleware
            # you can get the non-200 response
            response = failure.value.response
            self.logger.error('HttpError on %s', response.url)

        elif failure.check(DNSLookupError):
            # this is the original request
            request = failure.request
            self.logger.error('DNSLookupError on %s', request.url)

        elif failure.check(TimeoutError, TCPTimedOutError):
            request = failure.request
            self.logger.error('TimeoutError on %s', request.url)


Request.meta特殊键

Request.meta属性可以包含任何任意数据,但有一些特殊的键由Scrapy及其内置扩展识别。

那些是:

dont_redirect
dont_retry
handle_httpstatus_list
handle_httpstatus_all
dont_merge_cookies(参见cookies构造函数的Request参数)
cookiejar
dont_cache
redirect_urls
bindaddress
dont_obey_robotstxt
download_timeout
download_maxsize
download_latency
proxy

bindaddress

用于执行请求的出站IP地址的IP。

download_timeout

下载器在超时前等待的时间量(以秒为单位)。参见:DOWNLOAD_TIMEOUT

download_latency

自请求已启动以来,用于获取响应的时间量,即通过网络发送的HTTP消息。此元键仅在响应已下载时可用。虽然大多数其他元键用于控制Scrapy行为,但这应该是只读的。

请求子类

这里是内置子类的Request列表。您还可以将其子类化以实现您自己的自定义功能。

FormRequest对象
FormRequest类扩展了Request具有处理HTML表单的功能的基础。它使用lxml.html表单 从Response对象的表单数据预填充表单字段。

class scrapy.http.FormRequest(url[, formdata, ...])

FormRequest类增加了新的构造函数的参数。其余的参数与Request类相同,这里没有记录。

  • 参数:formdata(元组的dict或iterable) - 是一个包含HTML Form数据的字典(或(key,value)元组的迭代),它将被url编码并分配给请求的主体。
    FormRequest对象支持除标准以下类方法Request的方法:
classmethod from_response(response[, formname=None, formid=None, formnumber=0, formdata=None, formxpath=None, formcss=None, clickdata=None, dont_click=False, ...])

返回一个新FormRequest对象,其中的表单字段值已预先<form>填充在给定响应中包含的HTML 元素中。有关示例,请参阅 使用FormRequest.from_response()来模拟用户登录

该策略是在任何可查看的表单控件上默认自动模拟点击,如a 。即使这是相当方便,并且经常想要的行为,有时它可能导致难以调试的问题。例如,当使用使用javascript填充和/或提交的表单时,默认行为可能不是最合适的。要禁用此行为,您可以将参数设置 为。此外,如果要更改单击的控件(而不是禁用它),您还可以使用 参数。<input type="submit"> from_response() dont_click True clickdata

参数:

  • response(Responseobject) - 包含将用于预填充表单字段的HTML表单的响应
  • formname(string) - 如果给定,将使用name属性设置为此值的形式。
  • formid(string) - 如果给定,将使用id属性设置为此值的形式。
  • formxpath(string) - 如果给定,将使用匹配xpath的第一个表单。
  • formcss(string) - 如果给定,将使用匹配css选择器的第一个形式。
  • formnumber(integer) - 当响应包含多个表单时要使用的表单的数量。第一个(也是默认)是0。
  • formdata(dict) - 要在表单数据中覆盖的字段。如果响应<form>元素中已存在字段,则其值将被在此参数中传递的值覆盖。
  • clickdata(dict) - 查找控件被点击的属性。如果没有提供,表单数据将被提交,模拟第一个可点击元素的点击。除了html属性,控件可以通过其相对于表单中其他提交表输入的基于零的索引,通过nr属性来标识。
  • dont_click(boolean) - 如果为True,表单数据将在不点击任何元素的情况下提交。

这个类方法的其他参数直接传递给 FormRequest构造函数。
在新版本0.10.3:该formname参数。
在新版本0.17:该formxpath参数。
新的版本1.1.0:该formcss参数。
新的版本1.1.0:该formid参数。



请求使用示例

使用FormRequest通过HTTP POST发送数据

如果你想在你的爬虫中模拟HTML表单POST并发送几个键值字段,你可以返回一个FormRequest对象(从你的爬虫)像这样:

return [FormRequest(url="http://www.example.com/post/action",
                    formdata={'name': 'John Doe', 'age': '27'},
                    callback=self.after_post)]

使用FormRequest.from_response()来模拟用户登录

网站通常通过元素(例如会话相关数据或认证令牌(用于登录页面))提供预填充的表单字段。进行剪贴时,您需要自动预填充这些字段,并且只覆盖其中的一些,例如用户名和密码。您可以使用 此作业的方法。这里有一个使用它的爬虫示例:<input type="hidden"> FormRequest.from_response()

import scrapy

class LoginSpider(scrapy.Spider):
    name = 'example.com'
    start_urls = ['http://www.example.com/users/login.php']

    def parse(self, response):
        return scrapy.FormRequest.from_response(
            response,
            formdata={'username': 'john', 'password': 'secret'},
            callback=self.after_login
        )

    def after_login(self, response):
        # check login succeed before going on
        if "authentication failed" in response.body:
            self.logger.error("Login failed")
            return

        # continue scraping with authenticated session...

响应对象

class scrapy.http.Response(url[, status=200, headers=None, body=b'', flags=None, request=None])
一个Response对象表示的HTTP响应,这通常是下载(由下载),并供给到爬虫进行处理。

参数:

  • url(string) - 此响应的URL
  • status(integer) - 响应的HTTP状态。默认为200
  • headers(dict) - 这个响应的头。dict值可以是字符串(对于单值标头)或列表(对于多值标头)。
  • body(str) - 响应体。它必须是str,而不是unicode,除非你使用一个编码感知响应子类,如 TextResponse
  • flags(list) - 是一个包含属性初始值的 Response.flags列表。如果给定,列表将被浅复制。
  • request(Requestobject) - 属性的初始值Response.request。这代表Request生成此响应。

url
包含响应的URL的字符串。

此属性为只读。更改响应使用的URL replace()。

status
表示响应的HTTP状态的整数。示例:200, 404。

headers
包含响应标题的类字典对象。可以使用get()返回具有指定名称的第一个标头值或getlist()返回具有指定名称的所有标头值来访问值。例如,此调用会为您提供标题中的所有Cookie:

response.headers.getlist('Set-Cookie')

body
本回复的正文。记住Response.body总是一个字节对象。如果你想unicode版本使用 TextResponse.text(只在TextResponse 和子类中可用)。

此属性为只读。更改响应使用的主体 replace()。

request
Request生成此响应的对象。在响应和请求通过所有下载中间件后,此属性在Scrapy引擎中分配。特别地,这意味着:

HTTP重定向将导致将原始请求(重定向之前的URL)分配给重定向响应(重定向后具有最终URL)。
Response.request.url并不总是等于Response.url
此属性仅在爬虫程序代码和 Spider Middleware中可用,但不能在Downloader Middleware中使用(尽管您有通过其他方式可用的请求)和处理程序response_downloaded。

meta
的快捷方式Request.meta的属性 Response.request对象(即self.request.meta)。

与Response.request属性不同,Response.meta 属性沿重定向和重试传播,因此您将获得Request.meta从您的爬虫发送的原始属性。

也可以看看

Request.meta 属性

flags
包含此响应的标志的列表。标志是用于标记响应的标签。例如:'cached','redirected '等等。它们显示在Response(__ str__ 方法)的字符串表示上,它被引擎用于日志记录。

copy()
返回一个新的响应,它是此响应的副本。

replace([ url,status,headers,body,request,flags,cls ] )
返回具有相同成员的Response对象,但通过指定的任何关键字参数赋予新值的成员除外。该属性Response.meta是默认复制。

urljoin(url )
通过将响应url与可能的相对URL 组合构造绝对url。

这是一个包装在urlparse.urljoin,它只是一个别名,使这个调用:

urlparse.urljoin(response.url, url)

响应子类

这里是可用的内置Response子类的列表。您还可以将Response类子类化以实现您自己的功能。

TextResponse对象

class scrapy.http.TextResponse(url[, encoding[, ...]])

TextResponse对象向基Response类添加编码能力 ,这意味着仅用于二进制数据,例如图像,声音或任何媒体文件。

TextResponse对象支持一个新的构造函数参数,除了基础Response对象。其余的功能与Response类相同,这里没有记录。

参数: encoding(string) - 是一个字符串,包含用于此响应的编码。如果你创建一个TextResponse具有unicode主体的对象,它将使用这个编码进行编码(记住body属性总是一个字符串)。如果encoding是None(默认值),则将在响应标头和正文中查找编码。
TextResponse除了标准对象之外,对象还支持以下属性Response

text
响应体,如unicode。

同样response.body.decode(response.encoding),但结果是在第一次调用后缓存,因此您可以访问 response.text多次,无需额外的开销。

注意
unicode(response.body)不是一个正确的方法来将响应身体转换为unicode:您将使用系统默认编码(通常为ascii)而不是响应编码。

encoding
包含此响应的编码的字符串。编码通过尝试以下机制按顺序解决:

  1. 在构造函数编码参数中传递的编码
  2. 在Content-Type HTTP头中声明的编码。如果此编码无效(即未知),则会被忽略,并尝试下一个解析机制。
  3. 在响应主体中声明的编码。TextResponse类不提供任何特殊功能。然而, HtmlResponse和XmlResponse类做。
  4. 通过查看响应体来推断的编码。这是更脆弱的方法,但也是最后一个尝试。

selector
一个Selector使用响应为目标实例。选择器在第一次访问时被延迟实例化。

TextResponse对象除了标准对象外还支持以下方法Response:

xpath(查询)
快捷方式TextResponse.selector.xpath(query)

response.xpath('//p')

css(query)
快捷方式 TextResponse.selector.css(query):

response.css('p')

body_as_unicode()
同样text,但可用作方法。保留此方法以实现向后兼容; 请喜欢response.text。

HtmlResponse对象

class scrapy.http.HtmlResponse(url [,... ] )
本HtmlResponse类的子类,TextResponse 这增加了通过查看HTML编码自动发现支持META HTTP-EQUIV属性。见TextResponse.encoding。

XmlResponse对象

class scrapy.http.XmlResponse(url [,... ] )
本XmlResponse类的子类,TextResponse这增加了通过查看XML声明线路编码自动发现支持。见TextResponse.encoding。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容