requests: sending a http request, specifically designed for human

Introduction

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

How to send a request

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

Passing url parameters

有些时候我们相对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

Response content

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_
"""

Binary response content

实际上,也正如我们之前说的,我们还可以以二进制的格式获取响应内容。比如图片,不像文本,图片这种内容只能以二进制的格式返回。然而我们之前也说过,即便是文本,仍是以二进制返回的,只不过为了文本方便阅读,在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 response content

之前说过返回的是二进制的流数据,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 是否和你的期望相同。

Original response content

在罕见的情况下,你可能会想获取来自服务器的原始套接字响应,那么可以使用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'

Custom request headers

很多网站,都设置了反爬虫机制。最常用的反爬虫机制就是,判断请求头,如果请求头不是浏览器的请求头,会直接将你屏蔽掉。
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 request

上面都是关于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 submission

如果我们需要提交文件的话,那么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"
}
"""

We can see the contents of the file we print out


The response status code

我们还可以检测返回来的状态码
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

    """

Response header

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"
  }
}
"""

Redirect the request history

有些时候当我们访问一个域名时,这个域名已经被修改了,但是呢?又怕你不知道,因此你访问以前的域名依旧可以,但是会被重定向到新的域名。
重定向分为暂时性重定向和永久性重定向。
暂时性重定向: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]>]

In addition, we can also prevent redirection, you only need to specify allow_redirects = False

import requests

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

time out

告诉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)'))


Errors and exceptions

  • ConnectionError encountered network issues, such as DNS query fails, reject the connection:
  • HTTPError : If http request returns an unsuccessful status code, res.raise_for_status will throw an exception
  • Timeout : If the request times out, Thrown
  • TooManyRedirects : If the request exceeds the maximum number of redirects, Thrown
  • requests.exceptions.RequestException : so requests display thrown exception, are inherited from the exception

Session object

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了,这是为什么?

When we execute session.get ( 'http://httpbin.org/cookies/set/sessioncookie/123456789'), it will return a cookie, the cookie has been saved in your session, so when we again use the session ( session) when sending the request with the cookie will automatically be in the past. But when we use requests.get, it is creating a new session. It can only get the first cookie, then the cookie will pass longer appear in the past, but if it is, then the session would not have, because it will automatically be saved in cookie

Furthermore method session may also be requested to provide default data

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语句


Request and response objects

在任何时候,只要进行了类似于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'}


Request prepared (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/

However, the above code will lose some of the advantages Requests Session object, in particular, the session-level state, such as cookie will not be applied to your requests go up. To get PreparedRequest with a state, you can use the call Session.prepare_request replace Request.prepare.

Only need prepped = req.prepare () replaced prepped = session.prepare_request () can

But common sense is not, at least I basically how not used


ssl certificate validation

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

You may also verifyfile incoming CA_BUNDLE path to the file, or the file that contains the trusted CA certificate folder path

Or allowed to remain in the session

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

The client certificate (not used)

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

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

Or allowed to remain in the session

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

If an error certificate is specified, it will also lead to a SSLError


Hook function (not used)

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/

Custom Authentication

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'))

Streaming Request

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>
"""

proxy

To use the proxy, the request may be by any method providing proxiesto individual requests configuration parameters

import requests

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

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

If you need to use a proxy HTTP Basic Auth, you can use http: // user: password @ host / syntax:

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

To set up a proxy for a specific connection or host, using the scheme: // hostname as a key, it will be matched against the specified host and connections.

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

Note that the URL must include the proxy connection.


socks

In addition to basic http proxy, requests also supports socks proxy. If you want to use, then you need to install third-party libraries, pip install requests [socks].

Generally, we use the socks are acquired interface data, in python there is a relatively easy to use module, called suds.

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

But in requests, use the socks proxy and http proxy is the same

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

Guess you like

Origin www.cnblogs.com/traditional/p/11111305.html