3.3.2 request库的高级用法

在前一节已经了解了request库的基本使用方法,例如GET请求、POST请求等,本节将继续深入了解requests的其他高级用法。

1.利用Session保持会话连接

在进行实际爬虫开发的时候,我们会调用多个接口来发出多个请求,在这些请求中有时需要保持一些共用的数据,例如Cookies信息。此时requests库的Session对象能够帮我们跨请求保持某些参数,也会在同一个Session实例发出的所有请求之间保持Cookies。

我们能够利用Session方便地维护一个会话,而且不用担心Cookies的问题,它会自动帮我们处理好。我们可以尝试利用Session请求与Cookies请求来比较两者的不同。Cookies保持会话实例如下所示。

【例3-23】Cookies保持会话实例


1  import requests
2  requests.get('http://httpbin.org/cookies/set/cook123/test123')
3  r = requests.get('http://httpbin.org/cookies')
4  print(r.text)

运行结果如下:


{
    "cookies": {}
}

在例3-23的代码中,对http://httpbin.org网站设置了一个名为cook123、内容为test123的Cookies,随后再次请求该网站。从结果可以看到,二次请求无法获得已经设置好的Cookies。接着我们利用Session进行测试,实例如下所示。

【例3-24】Session保持会话实例


1  import requests
2  s = requests.Session()
3  s.get('http://httpbin.org/cookies/set/number/test123')
4  r = s.get('http://httpbin.org/cookies')
5  print(r.text)

运行结果如下:


{
    "cookies": {
        "number": "test123"
    }
}

显然,我们能够成功获取Session值。因此,利用Session可以做到模拟同一个会话而不用担心Cookies的问题,解决了在同一个浏览器中打开同一网站的不同页面的问题,Session通常用于模拟登录成功之后再进行下一步的操作。

2.代理

在爬取的过程中可能会遇到一种情况,即所爬取的网站给出提示“访问频率太高”,如果再想进行访问,那么必须要等一会儿,或者对方会给出一个验证码,使用该验证码来对被访问的网站进行解封。之所以会有这样的提示是因为我们所要爬取或者访问的网站设置了反爬虫机制,比如使用同一个IP频繁地请求网页的次数过多,则服务器会因反爬虫机制的指令而选择拒绝服务,这种情况单单依靠解封是比较难处理的。

因此一个解决的方法就是伪装本机的IP地址去访问或者爬取网页,即设置代理IP去访问网页,我们可以通过为任意请求方法提供proxies参数来配置单个请求。设置代理的实例如下所示。

【例3-25】设置代理


1  import requests
2  proxies = {
3    "http": "http://10.10.1.10:3128",
4    "https": "http://10.10.1.10:1080",
5  }
6  requests.get("http://example.org", proxies=proxies)

在Linux环境下,我们也可以通过环境变量HTTP_PROXY和HTTPS_PROXY来配置代理,如下所示:


$ export HTTP_PROXY="http://10.10.1.10:3128"
$ export HTTPS_PROXY="http://10.10.1.10:1080"
$ Python
>>> import requests
>>> requests.get("http://example.org")

除了基本的HTTP代理,request还支持SOCKS协议的代理。这是一个可选功能,若要使用,需要先安装第三方库socks。


pip install requests[socks]

安装好依赖以后,使用SOCKS代理和使用HTTP代理一样简单,示例如下:


proxies = {
    'http': 'socks5://user:pass@host:port',
    'https': 'socks5://user:pass@host:port'
}

3.SSL证书验证

requests可以为HTTPS请求验证SSL证书,就像Web浏览器一样。SSL验证默认是开启的,如果证书验证失败,那么requests会抛出SSLError:


>>> requests.get('https://requestb.in')

抛出SSLError:


requests.exceptions.SSLError: hostname 'requestb.in' doesn't match either of '*.herok
app.com', 'herokuapp.com'

在该域名上没有设置SSL,所以访问失败了。但GitHub设置了SSL,设置SSL验证的代码如下:


>>> requests.get('https://github.com', verify=True)

运行结果如下:


<Response [200]>

4.POST发送多个文件

我们可以在一个POST请求中发送多个文件。例如,假设我们要上传多个图像文件到一个HTML表单,可以使用如下的代码:


<input type="file" name="images" multiple="true" required="true"/>

要实现这些,只需要把文件设置到一个元组的列表中,其中元组结构为(form_field_name,file_info),具体实例如下所示。

【例3-26】上传多个文件


1  import requests
2  url = 'http://httpbin.org/post'
3  multiple_files = [
4          ('images', ('1.png', open('1.png', 'rb'), 'image/png')),
5          ('images', ('2.png', open('2.png', 'rb'), 'image/png'))]
6  r = requests.post(url, files=multiple_files)
7  print(r.text)

运行结果如下:


{
    "args": {}, 
    "data": "", 
    "files": {
        "images": "data:image/png;base64,iVBOR--"
    }, 
    "form": {}, 
    "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Content-Length": "4976", 
        "Content-Type": "multipart/form-data;boundary=2ebb71ee02024ef8919f45463546dca1", 
        "Host": "httpbin.org", 
        "User-Agent": "Python-requests/2.14.2"
    }, 
    "json": null, 
    "origin": "113.9.216.26, 113.9.216.26", 
    "url": "https://httpbin.org/post"
}

注意,强烈建议你用二进制模式(binarymode)打开文件。这是因为requests可能会提供header中的Content-Length,在这种情况下该值会被设为文件的字节数。如果你用文本模式打开文件,就可能碰到错误。

5.自定义身份验证

requests允许你使用自己指定的身份验证机制。任何传递给请求方法的auth参数的可调用对象,在请求发出之前都有机会修改请求。

自定义的身份验证机制是作为requests.auth.AuthBase的子类来实现的,也非常容易定义。requests在requests.auth中提供了两种常见的身份验证方案:HTTPBasicAuth和HTTPDigestAuth。

假设我们有一个Web服务,仅在X-Pizza头被设置为一个密码值的情况下才会有响应,那么部分代码如下:


1  from requests.auth import AuthBase
2  class PizzaAuth(AuthBase):
3      """Attaches HTTP Pizza Authentication to the given Request object."""
4      def __init__(self, username):
5          # setup any auth-related data here
6          self.username = username
7  
8      def __call__(self, r):
9          # modify and return the request
10          r.headers['X-Pizza'] = self.username
11          return r

然后就可以使用我们所定义的PizzaAuth来进行网络请求:


requests.get('http://pizzabin.org/admin', auth=PizzaAuth('kenneth'))

6.Prepared Request

当我们从API或者会话调用中收到一个Response对象时,request属性其实是使用了PreparedRequest方法。有时在发送请求之前,我们需要对body或者header等做一些额外处理,如下实例演示了一个简单的做法。

【例3-27】准备请求实例


1  from requests import Request, Session
2  s = Session()
3  url = 'http://httpbin.org/post'
4  data = {
5      'name': 'germey'
6  }
7  headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWe
8  bKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36'}
9  req = Request('POST', url, data=data, headers=headers)
10 prepped = s.prepare_request(req)
11 r = s.send(prepped)
12 print(r.text)

上述实例代码引入了Request和Session对象,然后利用url、data和headers参数构造了一个Request对象,同时需要再调用Session的prepare_request()方法将其转换为一个Prepared Request对象,然后调用send()方法发送即可,运行结果如下:


{
    "args": {}, 
    "data": "", 
    "files": {}, 
    "form": {
        "name": "germey"
    }, 
    "headers": {
        "Accept": "*/*", 
        "Accept-Encoding": "gzip, deflate", 
        "Content-Length": "11", 
        "Content-Type": "application/x-www-form-urlencoded", 
        "Host": "httpbin.org", 
        "Use r-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537336 
(KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36"
    }, 
    "json": null, 
    "origin": "113.9.216.26, 113.9.216.26", 
    "url": "https://httpbin.org/post"
}

如结果所示,已成功实现了POST请求。