Python爬虫Scrapy(七)_Request_Response

本章将介绍Request与Response,更多内容请参考:Python学习指南

Request

Request源码:

# 部分代码
class Request(object_ref):

    def __init__(self, url, callback=None, method='GET', headers=None, body=None, 
                 cookies=None, meta=None, encoding='utf-8', priority=0,
                 dont_filter=False, errback=None):

        self._encoding = encoding  # this one has to be set first
        self.method = str(method).upper()
        self._set_url(url)
        self._set_body(body)
        assert isinstance(priority, int), "Request priority not an integer: %r" % priority
        self.priority = priority

        assert callback or not errback, "Cannot use errback without a callback"
        self.callback = callback
        self.errback = errback

        self.cookies = cookies or {}
        self.headers = Headers(headers or {}, encoding=encoding)
        self.dont_filter = dont_filter

        self._meta = dict(meta) if meta else None

    @property
    def meta(self):
        if self._meta is None:
            self._meta = {}
        return self._meta

其中,比较常用的参数:

url:就是需要请求,并进行下一步处理的url
callback:指定该请求返回的response,由哪个函数来处理。
method: 请求一般不需要指定,默认为GET方法,可设置为"GET"、"POST"、"PUT"等,且保证字符串大写。
headers: 请求时,包含的头文件。一般不需要,内容一般如下:

  • Host:media.readthedocs.org
  • User-Agent:Mozilla/5.0 (Window NT 6.2; WOW64; rv:33.0) Cecko/20100101 Firefox/33.0
  • Accept:text/css, /;q=0.1
  • Accept-Language:zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
  • Accept-Encoding:gzip,deflate
  • Referrer:http://scrapy-chs.readthedocs.org/zh_CN/0.24
  • Cookie:_ga=GA2.1612165614.14532342342
  • Connection:keep-alive
  • If-Modified-Since:Mon, 25 Aug 2015 21:59:35 GMT
  • Cache-Contro:max-age=0

meta:比较常用,在不同的请求之间传递数据使用的。字典dict型
"""
request_with_cookies = Request(
url="http://www.example.com",
cookies={'currency': 'USD', 'country': 'UY'},
meta={'dont_merge_cookies': True}
)
"""
encoding:使用默认的'utf-8'就行。
dont_filter:表明该请求不由调度器过滤。这是当你想使用多次执行相同的其你去,就忽略重复的过滤器。默认为False
errback:指定错误处理函数

Request.meta

Request.meata在不同请求之间传递数据使用的。
Request.meta属性可以包含任意的数据,但是Scrapy和它的内置扩展可以识别一些特殊的键。

  • dont_rediect:不重定向
  • dont_retry:不重试
  • handle_httpstatus_list
  • dont_merge_cookies:不合并cookie
  • cookiejar:使用cookiejar
  • rediect_urls:重定向连接
  • bindaddress:绑定ip地址
  • dont_obey_robotstxt:不遵循反爬虫协议
  • download_timeout:下载超时

Request的子类FormRequest

FormRequest是Request的子类,一般用作表单数据提交。

class FormRequest(Request):

    def __init__(self, *args, **kwargs):
        formdata = kwargs.pop('formdata', None)
        if formdata and kwargs.get('method') is None:
            kwargs['method'] = 'POST'

        super(FormRequest, self).__init__(*args, **kwargs)

FormRequest的构造:

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

FormRequest类除了有Request的功能,还提供了form_response的功能

def from_response(cls, response, formname=None, formid=None, formnumber=0, formdata=None,clickdata=None, dont_click=False, formxpath=None, formcss=None, **kwargs)
  • response:是指包含HTML表单的Response对象,该表单将用于预填充表单字段。
  • formname:如果给定,将使用form表单的name属性为该值的name属性的表单。
  • formid:如果给定,将使用form表单的id属性为该值的name属性的表单
  • formnumber:当响应包含多个表单时,要使用的表单的数量。 formnumber默认是0,表示使用第一个。
  • formdata:字段来覆盖表单数据。如果一个字段已经存在于响应<form>元素中,那么它的值被在这个参数中传递的值覆盖。
  • formxpath:如果给定,将使用与XPath匹配的第一个表单。
  • clickdata:查找单击控件的属性。如果没有给出,表单数据将被提交模拟点击第一个可点击的元素。
  • dont_click:如果为True,表单数据将被提交而不需要单击任何元素。

Response

# 部分代码
class Response(object_ref):
    def __init__(self, url, status=200, headers=None, body='', flags=None, request=None):
        self.headers = Headers(headers or {})
        self.status = int(status)
        self._set_body(body)
        self._set_url(url)
        self.request = request
        self.flags = [] if flags is None else list(flags)

    @property
    def meta(self):
        try:
            return self.request.meta
        except AttributeError:
            raise AttributeError("Response.meta not available, this response " \
                "is not tied to any request")

大部分参数和上面的差不多:

status :响应的状态码
body :响应体
url :响应url
headers:响应对象的响应报头
meta:为response.meta属性的初始值。如果给定的,字典将浅复制。

Response的子类

Response的继承关系

Response
    TextResponse
        HtmlResponse
        XmlResponse

TextResponse

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

TextResponse对象增加了编码能力的基础响应类,是指将只用于二进制数据,如图像、生硬或任何媒体文件。
TextResponse对象除了标准的Response对象外,还支持以下属性和方法:

  • encoding:
    与此响应编码的字符串。 通过尝试以下机制来解决编码问题:

    • 在构造函数编码参数中传递的编码
    • 在Content-Type HTTP头中声明的编码。如果这种编码是无效的(即未知的),它将被忽略,并尝试下一个解析机制。
    • 在响应正文中声明的编码。TextResponse类不提供任何特殊的功能。但是,HtmlResponse和XmlResponse类可以。
    • 通过查看响应主体来推断编码。 这是更脆弱的方法,但也是最后一个尝试。
  • selector : 使用响应作为目标的选择器实例。

  • ``body_as_unicode()` : 以unicode形式返回响应的主体。

  • xpath(query) : xpath解析

textresponse.selector.css('p')

#也可以简写为:

textresponse.css('p')
  • css(query) : :css解析,相当于BeautifulSoup4解析
textresponse.selector.css('p')

#也可以简写为:

textresponse.css('p')

HtmlResponse

HtmlResponse类是TextResponse的一个子类,它通过查看HTML meta http-equiv属性来添加编码自动发现支持。

XmlResponse

XmlResponse类是TextResponse的一个子类,它通过查看XML声明行来添加编码自动发现支持。

Response的

发送POST请求

  • 可以使用yield scrapy.FormRequest(url, formdata, callback)的方法发送POST请求。
  • 如果希望程序执行一开始就发送POST请求,可以重写Spider类的start_requests(self)方法,并且不再调用start_urls里的url。
class mySpider(scrapy.Spider):
    #start_utls = ['http://www.example d']
    def start_requests(self):
        url = "http://www.renren.com/PLogin.do"

    #FormRequest是Scrapy发送POST请求的方法
    yield scrapy.FormRequest(
            url = url,
            formdata = {"email" : "xxxx@qq.com", "password":"xxxxxx"}
            callback = self.parse_page
        )
    def parse_page(self, response):
    #do something

模拟登陆

使用FormRequest.form_response()方法模拟用户登陆

通常网站通过实现对某些表单字段(如数据或者登陆界面中的认证令牌等)的预填充。
使用Scrapy抓取网页时,如果想要预填充或重写像用户名、用户密码这些表单字段时,可以使用FormRequest.from_response()方法实现。
在Request中不存在formadata参数,所以无法使用提交表单的方式

下面是使用这种方法的爬虫例子:

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.log("Login failed", level=log.ERROR)
            return

        # continue scraping with authenticated session...

Github爬虫案例参考:

#-*- coding:utf-8 -*-
from scrapy import Spider, Request, FormRequest

class GithubLoginSpider(Spider):
    name = "github"
    allow_domains = ['github.com']

    #post登入必须的头字段
    post_headers = {
        "User-Agent" : "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36",
        "Referer" : "https://github.com",
        "Origin" : 'https://github.com',
        "Host":'github.com'
    }

    def start_requests(self):
        """
            执行spider请求
            :return 返回一个Request对象,请求登陆的页面
        """
        return [Request(url="https://github.com/login", meta={"cookiejar":1}, callback = self.post_login, headers = self.post_headers)]

    def post_login(self, response):
        """
            登陆的页面请求成功后,解析响应的页面,获取登陆需要的<input>标签信息
            :param response :登陆接口返回的页面
        """

        #github登陆上传必要的字段
        utf8 = response.xpath('//form//input[@name="utf8"]/@value').extract()[0]
        authenticity_token = response.xpath('//form//input[@name="authenticity_token"]/@value').extract()[0]
        login = "xxxx@qq.com"
        password = "xxxxxx"
        commit = response.xpath('//form//input[@name="commit"]/@value').extract()[0]

        #发送FormRequest表单请求
        return FormRequest.from_response(response=response, meta={"cookiejar":response.meta['cookiejar']},
            formdata = {
                "utf8" : utf8,
                "authenticity_token" :authenticity_token,
                "login" : login,
                "password" : password,
                "commit" : commit
            },
            callback = self.after_login,
            headers = self.post_headers
            )

    def after_login(self, response):
        """
            form表单请求成功后,请求登陆我的页面
            :param response
            :return:返回一个响应
        """
        print(response.body)
        if response.status == 200:
            with open("my_github.html", "wb") as f:
                f.write(response.body)

禁用遵循robot协议,打开Cookie

ROBOTSTXT_OBEY = False
COOKIE_ENABLED = True

启动爬虫

scrapy crawl github

参考:

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

推荐阅读更多精彩内容