requests:用于发送http请求,专为人类设计

介绍

requests模块是一个专门用来发送http请求的模块

如何发送请求

import requests

"""
使用requests模块发送请求非常简单
首先请求有get、post、delete、put、head
这些请求直接通过requests来调用即可
"""
# 这样就发送了一个get请求
res = requests.get("http://www.baidu.com")
# res就是我们发送请求之后,得到的返回值,这是一个Response对象。里面包含了很多的属性
"""
url:我们请求的url
status_code:返回的状态码
reason:一般网站中跟在状态码后边的那个,比如200 ok,404 not found
headers:返回的头部信息
cookies:返回的cookie
encoding:返回网站的编码
text:网页的html
content:网页的html(字节)
"""
print(res.url)  # http://www.baidu.com/
print(res.status_code)  # 200
print(res.reason)  # OK
print(res.headers)  # {'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 'Connection': 'Keep-Alive', 'Content-Encoding': 'gzip', 'Content-Type': 'text/html', 'Date': 'Wed, 26 Jun 2019 12:22:26 GMT', 'Last-Modified': 'Mon, 23 Jan 2017 13:27:36 GMT', 'Pragma': 'no-cache', 'Server': 'bfe/1.0.8.18', 'Set-Cookie': 'BDORZ=27315; max-age=86400; domain=.baidu.com; path=/', 'Transfer-Encoding': 'chunked'}

print(res.cookies)  # <RequestsCookieJar[<Cookie BDORZ=27315 for .baidu.com/>]>
print(res.encoding)  # ISO-8859-1


# 调用res.text可以直接打印html信息,但是由于可能会出现乱码
# 因此需要加上一个res.encoding = res.apparent_encoding
# 因为实际上获得的最原始的流数据,还是字节的形式,也就是我们上面说的content。
# 只不过requests为了调用者方便,从而进行了封装,将content解码成了text
# 但是使用res.encoding进行解码,有可能会造成编码错误,于是将apparent_encoding赋值给了encoding
# 至于apparent_encoding是什么?
# 实际上是根据python的一个可以对字节流所使用的编码进行检测的第三方模块(chardet),对content检测所得到的编码
from chardet import detect
# 可以看到检测出来是utf-8,如果我们再用ISO-8859-1那么可能会得到乱码
print(detect(res.content).get("encoding"))  # utf-8
# 实际上,这在requests内部也是这么做的
"""
@property
def apparent_encoding(self):
    return chardet.detect(self.content)['encoding']
"""

# 至于其他的请求也是一样的
# 实际上这些方法在requests中,都统一调用一个方法,都调用requests.request()
"""
requests.get(url) == requests.request('get', url) 
requests.post(url) == requests.request('post', url) 
requests.head(url) == requests.request('head', url) 
requests.delete(url) == requests.request('delete', url) 
requests.put(url) == requests.request('put', url) 
"""
# 为了方便,创建了这些对应的api

传递url参数

有些时候我们相对url添加一些查询字符串(query string),比如我们百度搜索python,那么就可以输入https://www.baidu.com/s?wd=python,但是这样显然不够人性化,因为requests就是号称for humans。
import requests

# 调用get方法时,可以加上一个参数params,以字典的形式传入
res = requests.get("http://www.baidu.com/s", params={"wd": "python"})
# 可以看到,自动帮我们拼接在一起了
print(res.url)  # http://www.baidu.com/s?wd=python

# 当然也可以传递多个参数
res = requests.get("http://httpbin.org/get", params={"k1": "v1", "k2": ["v2", "v3"]})
print(res.url)  # http://httpbin.org/get?k1=v1&k2=v2&k2=v3

响应内容

import requests
import re

res = requests.get("http://www.baidu.com/s", params={"wd": "python"})
# res.text可以打印出返回的html页面内容,也就是单击右键查看网页源代码所得到的内容
# 但是之前说过requests,是基于http头部响应的编码进行解码,也就是res.encoding
# 但是返回的内容的编码并不一定是res.encoding,因此我们可以在获取text之前改变编码,然后在获取text的时候就会使用我们指定的编码来对content进行解码了
res.encoding = res.apparent_encoding

text = res.text
match = re.findall(r".{20}python.{20}", text)
for i in match:
    print(i)
"""
节选一部分输出

ta-tools='{"title":"python吧-百度贴吧--python学习交流基地
r=baidu&fm=sc&query=python&qid=e49f6f960022996
><th><a href="/s?wd=python%E8%87%AA%E5%AD%A6&r
000013&rsp=0&f=1&oq=python&ie=utf-8&usm=1&rsv_
WRNBwvCY6eGkEholvw">python自学</a></th><td></td>
000001&rsp=1&f=1&oq=python&ie=utf-8&usm=1&rsv_
WRNBwvCY6eGkEholvw">python为什么叫爬虫</a></th><td><
9F%E6%95%99%E7%A8%8Bpython&rsf=1000012&rsp=2&f
wvCY6eGkEholvw">菜鸟教程python</a></th></tr><tr><t
&rsf=8&rsp=3&f=1&oq=python&ie=utf-8&usm=1&rsv_
"""

二进制响应内容

实际上,也正如我们之前说的,我们还可以以二进制的格式获取响应内容。比如图片,不像文本,图片这种内容只能以二进制的格式返回。然而我们之前也说过,即便是文本,仍是以二进制返回的,只不过为了文本方便阅读,在requests内部又封装了一个text,可以方便我们获取文本内容
import requests

res = requests.get("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1561565986554&di=028d5ad55109447f9a136ad7654c7268&imgtype=0&src=http%3A%2F%2Fi0.hdslb.com%2Fbfs%2Farticle%2Fa79025d0a0789582f90110411355bffd81ccdac8.jpg")
# 这个url是百度图片的一张我太太蕾姆的照片,如果这个时候再用text打印,会是一大堆乱码
# 因为我们可以以二进制的格式写入文件里面
with open("太太蕾姆.jpg", "wb") as f:
    f.write(res.content)


json响应内容

之前说过返回的是二进制的流数据,text是由content进行解码得到的。那么同理requests中json数据(如果符合json规范的话),也是由content转化得来的。这里不再代码演示了,和text一样,只不过由于没有@property,因此需要调用res.json()。

但需要注意的是,如果返回的内容不符合json格式,导致JSON解码失败,那么 res.json() 就会抛出一个异常。然而成功调用 r.json() 并**不**意味着响应的成功。有的服务器会在失败的响应中包含一个 JSON 对象(比如 HTTP 500 的错误细节)。这种 JSON 会被解码返回。要检查请求是否成功,请使用 r.raise_for_status() 或者检查 r.status_code 是否和你的期望相同。

原始响应内容

在罕见的情况下,你可能会想获取来自服务器的原始套接字响应,那么可以使用res.raw。如果真的想这么做,那么请加上参数stream=True,否则得到的是一个空字节串。
基本上不用这种方式获取数据
import requests

res = requests.get("https://api.github.com/events", stream=True)
raw = res.raw
# 可以对raw进行读取
print(raw.read(10))  # b'\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\x03'

定制请求头

很多网站,都设置了反爬虫机制。最常用的反爬虫机制就是,判断请求头,如果请求头不是浏览器的请求头,会直接将你屏蔽掉。
import requests

res = requests.get("http://www.baidu.com")
# 我们可以通过res.request.headers 来看看请求头是什么?
# 之前说的res.headers指的是服务器端返回的http报头
# 而这里的res.request.headers指的是我们传过去的请求头
print(res.request.headers)  # {'User-Agent': 'python-requests/2.19.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}
# 可以看到默认是'User-Agent': 'python-requests/2.19.1'

# 这是百度,用来搜索的,所以没有做这种限制。本身就是爬虫,再搞反爬,也太。。。。
# 但是其他网站,比如拉钩网,基本上百分百屏蔽
# 所以我们可以加上一个请求头,也是通过字典来传值
res = requests.get("http://www.baidu.com", headers={"User-Agent": "emmmmmmmm"})
# 我这里是随便指定的,真正爬虫的时候,从浏览里拷贝一下就行
print(res.request.headers)  # {'User-Agent': 'emmmmmmmm', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}

post请求

上面都是关于get请求的,下面我们介绍post请求。post请求一般是用于表单提交的,比如登录页面,我们通过脚本来模拟表单登录
import requests

payload = {"username": "komeijisatori", "password": "123456", "remember": "1"}

# 通过参数data指定
res = requests.post("http://httpbin.org/post", data=payload)
print(res.text)
"""
{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "password": "123456", 
    "remember": "1", 
    "username": "komeijisatori"
  }, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "49", 
    ...
    ...
    ...
    ...
"""
但有些时候,我们想发送的数据并不是以表单形式提交的,也就是需要传递一个string,但是格式是字典格式的。想到了什么,json。
import requests

payload = {"some": "data"}

# 这时候指定参数json即可
res = requests.post("https://api.github.com/some/endpoint", json=payload)

# 或者手动转化为json数据也可以
import json
res = requests.post("https://api.github.com/some/endpoint", json=json.dumps(payload))

post提交文件

如果我们需要提交文件的话,那么requests也是支持的
import requests

payload = {"some": "data"}

res = requests.post("http://httpbin.org/post", files={"file": open("1.txt", "rb")})
print(res.text)
"""
{
  "args": {}, 
  "data": "", 
  "files": {
    "file": "this is a file"
  }, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "155", 
    "Content-Type": "multipart/form-data; boundary=e34f63359be140870846d7fd5340daff", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.19.1"
  }, 
  "json": null, 
  "origin": "120.244.41.108, 120.244.41.108", 
  "url": "https://httpbin.org/post"
}
"""

# 此外还可以显示的指定文件名
res = requests.post("http://httpbin.org/post", files={"file": ("2.txt", open("1.txt", "rb"))})
print(res.text)
"""
{
  "args": {}, 
  "data": "", 
  "files": {
    "file": "this is a file"
  }, 
  "form": {}, 
  "headers": {
    "Accept": "*/*", 
    "Accept-Encoding": "gzip, deflate", 
    "Content-Length": "155", 
    "Content-Type": "multipart/form-data; boundary=ab3d5c2226580ff643ca947c0b4e2cee", 
    "Host": "httpbin.org", 
    "User-Agent": "python-requests/2.19.1"
  }, 
  "json": null, 
  "origin": "120.244.41.108, 120.244.41.108", 
  "url": "https://httpbin.org/post"
}
"""

可以看到,将我们文件的内容打印了出来


响应状态码

我们还可以检测返回来的状态码
import requests

res = requests.get("https://www.baidu.com")
print(res.status_code)  # 200

# 如果发送了一个错误请求,那么可以调用raise_for_status来抛出异常
# 由于我们这里返回正常,所以打印个None
print(res.raise_for_status())  # None

bad_res = requests.get("http://httpbin.org/status/404")
print(bad_res.status_code)  # 404

try:
    bad_res.raise_for_status()
except Exception as e:
    import traceback
    print(traceback.format_exc())
    """
    Traceback (most recent call last):
      File "D:/mashiro/python模块/requests模块.py", line 14, in <module>
        bad_res.raise_for_status()
      File "C:\python37\lib\site-packages\requests\models.py", line 939, in raise_for_status
        raise HTTPError(http_error_msg, response=self)
    requests.exceptions.HTTPError: 404 Client Error: NOT FOUND for url: http://httpbin.org/status/404

    """

响应头

import requests

res = requests.get("https://www.baidu.com")
print(res.headers)
"""
{'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 
'Connection': 'Keep-Alive', 
'Content-Encoding': 'gzip', 
'Content-Type': 'text/html', 
'Date': 'Wed, 26 Jun 2019 15:45:05 GMT', 
'Last-Modified': 'Mon, 23 Jan 2017 13:23:55 GMT', 
'Pragma': 'no-cache', 
'Server': 'bfe/1.0.8.18', 
'Set-Cookie': 'BDORZ=27315; max-age=86400; domain=.baidu.com; path=/', 
'Transfer-Encoding': 'chunked'}
"""
print(res.headers["Content-Type"])  # text/html

import requests

res = requests.get("https://www.baidu.com")
cookies = res.cookies
print(cookies.get_dict())  # {'BDORZ': '27315'}

# 此外还可以将得到cookie作为参数传进去,cookies={}
# 得到的cookies是一个 RequestsCookieJar类型,行为和字典类型,但是功能比字典更多,并且可以跨路径使用
from requests.cookies import RequestsCookieJar
jar = RequestsCookieJar()
jar.set("cookie1_name", "cookie1_value")
jar.set("cookie2_name", "cookie2_value")
res = requests.get("http://httpbin.org/cookies", cookies=jar)
print(res.text)
"""
{
  "cookies": {
    "cookie1_name": "cookie1_value", 
    "cookie2_name": "cookie2_value"
  }
}
"""

重定向与请求历史

有些时候当我们访问一个域名时,这个域名已经被修改了,但是呢?又怕你不知道,因此你访问以前的域名依旧可以,但是会被重定向到新的域名。
重定向分为暂时性重定向和永久性重定向。
暂时性重定向:code为302,当我们发表评论时,但是没有登录,此时就会被暂时重定向到登录页面
永久性重定向:code为301,比如我们访问http://www.taobao.com,但是这个域名已经废弃了,因此会被永久性重定向到https://www.taobao.com

调用res.history可以查看历史
import requests

res = requests.get("http://www.taobao.com")
print(res.status_code)  # 200
print(res.history)  # [<Response [301]>]

此外我们还可以禁止重定向,只需要指定allow_redirects=False即可

import requests

res = requests.get("http://www.taobao.com", allow_redirects=False)
print(res.status_code)  # 301
print(res.history)  # []

超时

告诉requests,如果在规定的时间内一直没有连接上的话,那么就放弃连接,可以通过timeout指定
import requests

"""
timeout可以是一个元组,比如(m, n)
第一个元素表示发送连接请求所使用的最大时间,如果m秒内连接没有建立成功,则放弃
第二个元素则是等待服务器响应的所使用的最大时间,如果m秒内连接建立好了,但是n秒内服务器一直没有返回数据,也放弃

这两种情况都会抛出一个Timeout异常

如果timeout是一个int,比如m,那么两者的最大时间都是m
"""
try:
    res = requests.get("https://www.google.com", timeout=(2, 3))
except requests.exceptions.Timeout as e:
    print(e)  # HTTPSConnectionPool(host='www.google.com', port=443): Max retries exceeded with url: / (Caused by ConnectTimeoutError(<urllib3.connection.VerifiedHTTPSConnection object at 0x000001E44616C320>, 'Connection to www.google.com timed out. (connect timeout=2)'))


错误与异常

  • ConnectionError:遇到网络问题,如DNS查询失败,拒绝连接时
  • HTTPError:如果http请求返回了不成功的状态码,res.raise_for_status会抛出一个异常
  • Timeout:若请求超时,则抛出此异常
  • TooManyRedirects:若请求超过了最大重定向次数,则抛出此异常
  • requests.exceptions.RequestException:所以requests显示抛出的异常,都继承自该异常

会话对象

import requests

"""
之前我们说过,requests里面的get、post等方法,底层都是调用的request方法
但是request调用的是谁呢?

去掉注释的话,长这样
def request(method, url, **kwargs):
    with sessions.Session() as session:
        return session.request(method=method, url=url, **kwargs)

可以看到首先是调用Session这个类实例化一个session对象,然后调用session下的request方法。

session(会话对象),是一个类实例化的对象,那么将我们的一些参数给保存起来
会话对象可以让我们跨请求保持某些参数,它也会在同一个Session实例发出的所有请求之间保持cookie,期间使用urllib3的connection pooling功能
因为requests主要是基于urllib3的。
如果我们向同一个主机发送多次请求,底层的tcp连接会被重用,从而带来显著的性能提升
"""
session = requests.Session()  # 也可以调用requests.session,两者是一样的
session.get('http://httpbin.org/cookies/set/sessioncookie/123456789')

res1 = session.get("http://httpbin.org/cookies")
print(res1.text)
"""
{
  "cookies": {
    "sessioncookie": "123456789"
  }
}
"""

res2 = requests.get("http://httpbin.org/cookies")
print(res2.text)
"""
{
  "cookies": {}
}
"""

可以看到,当我们使用res1.text的时候,能够打印出cookie,但是使用res2.text,就打印不出cookie了,这是为什么?

当我们执行session.get('http://httpbin.org/cookies/set/sessioncookie/123456789')的时候,会返回一个cookie,这个cookie已经被保存在会话里了,因此当我们再次使用session(会话)发送请求的时候会自动将这个cookie带过去。但是当我们使用requests.get的时候,则是创建一个新会话。因此只能先拿到cookie,然后再显示的将cookie传过去,但如果是会话的话就不用了,因为会自动的将cookie保存起来

此外会话还可以为请求方法提供缺省数据

import requests


session = requests.Session()
"""
Session这个类里面,有很多的属性,比如headers,auth,cookies,proxies等等
但是没有在__init__中定义,即便如此我们仍然可以通过属性访问的方式来添加
"""
session.headers.update({"User-Agent": "kubernetes"})
# 此时调用get方法的时候,会将headers带过去
res = session.get("http://www.baidu.com")
print(res.request.headers)  # {'User-Agent': 'kubernetes', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}

# 但是如果我们在请求的方法中又传了相应的参数,那么请求方法中的参数,会覆盖会话当中的参数
res = session.get("http://www.baidu.com", headers={"User-Agent": "docker", "name": "satori"})
print(res.request.headers)
"""
{'User-Agent': 'docker', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 
'Connection': 'keep-alive', 'name': 'satori', 
'Cookie': 'BAIDUID=B2A159333ED50D72BC07D66F1040A68E:FG=1; BIDUPSID=B2A159333ED50D72BC07D66F1040A68E; H_PS_PSSID=26525_1447_21090_29135_29238_28519_29099_29131_28832_29221_26350_29072; PSTM=1561642196; delPer=0; BDSVRTM=0; BD_HOME=0'}
"""
# 可以看到kubernetes被docker覆盖了,而且我们设置的name也出现了。最关键的是,居然还出现了cookie,这是为什么?
# 因为在第一次访问百度的时候,返回给我们一个cookie,第二次访问的时候,使用的是同一个session,所以会将上一步得到的cookie带过去

res = session.get("http://www.baidu.com")
print(res.request.headers)
"""
{'User-Agent': 'kubernetes', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive', 
'Cookie': 'BAIDUID=AB0351E419827120EABABE13ECC4C9ED:FG=1; BIDUPSID=AB0351E419827120EABABE13ECC4C9ED; H_PS_PSSID=1422_21110_29135_29237_28519_29098_29131_28838_29220_22160; PSTM=1561642425; delPer=0; BDSVRTM=0; BD_HOME=0'}
"""
# 这里问题又来了,可以看到,我们第三次访问的时候,User-Agent又变回了kubernetes,而且name也没了,cookie还在。这是为什么?
# 因为方法中的参数不会跨请求保持,我们在请求的时候所设置的参数,仅仅对那一次请求有效。如果之后不指定,那么请求还是会使用会话里面的,所以是这个结果。

并且,由于Session类实现了__enter__和__exit__方法,所以还可以使用with语句


请求与响应对象

在任何时候,只要进行了类似于requests.get的调用,都在做两件主要的事情。第一,在构建一个Request对象,该对象被发送到某个服务器上请求或查询一些资源。第二,一旦requests得到一个从服务器返回的相应就会产生一个Response对象、该响应对象包含服务器返回的所有信息,也包含原来创建的Request对象。

import requests


session = requests.Session()
# 先来看看session.request的源码
# 值得一提的是,requests.get、post等请求方法,都是调用的requests.request
# 但是requests.requests实际上也是创建了一个session对象,调用session对象下的request方法,只不过这个session对象在这一次请求之后就没有了,因为我们没有保存
# 而session对象下也有get、post等方法,当然调用也是session.request。
# 因此无论是通过requests主模块、还是创建的session对象,其所调用的get、post、delete等请求方法,最终调用的都是session对象下的request方法
"""
def request(self, method, url,
        params=None, data=None, headers=None, cookies=None, files=None,
        auth=None, timeout=None, allow_redirects=True, proxies=None,
        hooks=None, stream=None, verify=None, cert=None, json=None):

    # Create the Request.创建一个Request对象
    # 里面主要包装我们的一些参数
    req = Request(
        method=method.upper(),
        url=url,
        headers=headers,
        files=files,
        data=data or {},
        json=json,
        params=params or {},
        auth=auth,
        cookies=cookies,
        hooks=hooks,
    )
    # 准备的请求,后面会说
    prep = self.prepare_request(req)

    proxies = proxies or {}

    settings = self.merge_environment_settings(
        prep.url, proxies, stream, verify, cert
    )

    # Send the request.
    send_kwargs = {
        'timeout': timeout,
        'allow_redirects': allow_redirects,
    }
    send_kwargs.update(settings)
    # 发送请求,得到Response对象
    resp = self.send(prep, **send_kwargs)

    return resp
"""
res = session.get("https://www.baidu.com")
# 获取服务器返回的头部信息
print(res.headers)
"""
{'Cache-Control': 'private, no-cache, no-store, proxy-revalidate, no-transform', 
'Connection': 'Keep-Alive', 
'Content-Encoding': 'gzip', 
'Content-Type': 'text/html', 
'Date': 'Thu, 27 Jun 2019 14:07:13 GMT', 
'Last-Modified': 'Mon, 23 Jan 2017 13:23:55 GMT', 
'Pragma': 'no-cache', 'Server': 'bfe/1.0.8.18', 
'Set-Cookie': 'BDORZ=27315; max-age=86400; domain=.baidu.com; path=/', 
'Transfer-Encoding': 'chunked'}
"""

# 还可以获取发送给服务器的请求的头部
print(res.request.headers)  # {'User-Agent': 'python-requests/2.19.1', 'Accept-Encoding': 'gzip, deflate', 'Accept': '*/*', 'Connection': 'keep-alive'}


准备的请求(prepared request)

当从api或者会话调用中收到一个Response对象时,request属性实际上使用了Prepared Request。因此在发送请求之前,我们可以对body、header做一些额外的处理

import requests


session = requests.Session()

req = requests.Request("get", url="https://www.baidu.com")
prepped = req.prepare()

# 可以做一些额外的处理,这里处理请求头
prepped.headers["User-Agent"] = "satori"

# 调用session的send方法,这个requests模块内部的session.request本质上也是这么做的,返回的就是Response对象
res = session.send(prepped)
print(res.request.headers)  # {'User-Agent': 'satori'}
print(res.cookies.get_dict())  # {'BIDUPSID': '6A947E75D63F889E10C89D7E6A2870AA', 'PSTM': '1561645151', 'BD_NOT_HTTPS': '1'}
print(res.url)  # https://www.baidu.com/

然而,上述代码会失去 Requests Session 对象的一些优势, 尤其session级别的状态,例如 cookie 就不会被应用到你的请求上去。要获取一个带有状态的PreparedRequest, 可以使用 Session.prepare_request 取代Request.prepare的调用.

只需要将prepped = req.prepare()换成prepped = session.prepare_request()即可

不过感觉不常用,至少我基本上没怎么用过


ssl证书验证

requests可以为HTTPS请求验证ssl证书,就像web浏览器一样。ssl验证默认是开启的,如果证书验证失败,那么requests会抛出一个SSLError,对于那些没有设置ssl的,如果验证失败那么可以在请求方法中加上verify=False,表示不验证

也可以为 verify 传入 CA_BUNDLE 文件的路径,或者包含可信任 CA 证书文件的文件夹路径

或者让其保持在会话中

s = requests.Session()
s.verify = '证书路径'
# 值得一提的是
# 如果 verify 设为文件夹路径,文件夹必须通过 OpenSSL 提供的 c_rehash 工具处理。

客户端证书(不常用)

也可以指定一个本地证书用作客户端证书,可以是单个文件(包含密钥和证书)或一个包含两个文件路径的元组:

requests.get('url', cert=('client.cert', 'client.key'))

或者让其保持在会话中

s = requests.Session()
s.cert = 'client.cert'

如果指定了一个错误的证书,那么同样会引发一个SSLError


钩子函数(不常用)

import requests


def foo(response, *args, **kwargs):
    print(response.url)


# 绑定一个钩子函数,那么当请求执行完毕后,会触发foo函数,key要指定为response
res = requests.get("http://www.baidu.com", hooks={"response": foo})

# http://www.baidu.com/

自定义身份验证

Requests 允许你使用自己指定的身份验证机制。

任何传递给请求方法的 auth 参数的可调用对象,在请求发出之前都有机会修改请求。

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

假设我们有一个web服务,仅在 X-Pizza 头被设置为一个密码值的情况下才会有响应。虽然这不太可能,但就以它为例好了。
from requests.auth import AuthBase
import requests

class PizzaAuth(AuthBase):
    def __init__(self, username):
        self.username = username

    def __call__(self, r):
        r.headers['X-Pizza'] = self.username
        return r
    
    
requests.get('http://pizzabin.org/admin', auth=PizzaAuth('fuck_you'))

流式请求

import requests


# 使用Response.iter_lines(),可以很方便地对流式 API进行迭代。简单地设置stream为True便可以使用iter_lines进行迭代
res = requests.get("https://www.baidu.com", stream=True)
res.encoding = res.apparent_encoding

# 加上decode_unicode=True表示默认解码, 否则得出的是字节类型
# 而且也要加上res.encoding = res.apparent_encoding,否则会是乱码
# 还可以指定chunk_size,默认是512
for line in res.iter_lines(decode_unicode=True):
    print(line[: 55])
"""
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-t
                </script> <a href=//www.baidu.com/more/
"""

# 除了iter_lines,还有一个iter_content,两者类似,但是这里必须要指定chunk_size,因为默认是1
for line in res.iter_content(chunk_size=200, decode_unicode=True):
    print(line[: 100])
"""
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charse
l=stylesheet type=text/css href=https://ss1.bdstatic.com/5eN1bjq8AAUYm2zgoY3K/r/www/cache/bdorz/baid
v id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img h
 action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden n
t type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"
pan class="bg s_btn_wr"><input type=submit id=su value=百度一下 class="bg s_btn" autofocus></span> </for
/a> <a href=https://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.c
/a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>贴吧</a> <noscript> <a href=http://www.b
ame=tj_login class=lb>登录</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz
= "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">登录</a>');
                </scrip
/a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>关于百度
 href=http://www.baidu.com/duty/>使用百度前必读</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedbac
</p> </div> </div> </div> </body> </html>
"""

代理

如果需要使用代理,可以通过为任意请求方法提供 proxies 参数来配置单个请求

import requests

proxies = {
  "http": "http://10.10.1.10:3128",
  "https": "http://10.10.1.10:1080",
}

requests.get("http://example.org", proxies=proxies)

若你的代理需要使用HTTP Basic Auth,可以使用 http://user:password@host/ 语法:

proxies = {
    "http": "http://user:[email protected]:3128/",
}

要为某个特定的连接方式或者主机设置代理,使用 scheme://hostname 作为 key, 它会针对指定的主机和连接方式进行匹配。

proxies = {'http://10.20.1.128': 'http://10.10.1.10:5323'}

注意,代理 URL 必须包含连接方式。


socks

除了基本的http代理,requests还支持socks代理。如果要使用的话,需要安装第三方库,pip install requests[socks]。

一般我们使用socks都是获取接口数据,在python中还有一个比较好用的模块,叫suds。

from suds.client import Client
client = Client(url="xxx")
res = getattr(client.service, "func")(*params)

但是在requests中,使用socks代理和http代理是一样的

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

猜你喜欢

转载自www.cnblogs.com/traditional/p/11111305.html