本章将介绍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