介绍

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
import os
from requests_toolbelt.multipart.encoder import MultipartEncoder multipart_encoder = MultipartEncoder(
fields={
# 表单所需要的参数直接填在fields里面即可
# 包括上传的文件也是
'parentId': "xxx",
'size': f'{os.stat("1.png").st_size}',
'lParentId': "xxxxx",
'isDir': "false",
"fileSize": f'{os.stat("1.png").st_size}',
"fileName": "1.png",
"userId": "admin",
"directCheck": "true",
"mode": "0",
'file': ('file', open("1.png", 'rb'))
},
boundary="----WebKitFormBoundaryIHASM5Ndp6lUZPA8"
) session = requests.session()
# 设置Content-Type
session.headers["Content-Type"] = multipart_encoder.content_type
session.post(
"http://xxx.com",
data=multipart_encoder
)

响应状态码

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

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

cookie

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:pass@10.10.1.10: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'
}

requests:用于发送http请求,专为人类设计的更多相关文章

  1. 『居善地』接口测试 — 5、使用Requests库发送POST请求

    目录 1.请求正文是application/x-www-form-urlencoded 2.请求正文是raw (1)json格式文本(application/json) (2)xml格式文本(text ...

  2. Python接口测试-使用requests模块发送GET请求

    本篇主要记录下使用python的requests模块发送GET请求的实现代码. 向服务器发送get请求:无参数时:r = requests.get(url)带params时:r = requests. ...

  3. Python接口测试-使用requests模块发送post请求

    本篇主要记录下使用python的requests模块发送post请求的实现代码. #coding=utf-8 import unittest import requests class PostTes ...

  4. 【python接口自动化】- 使用requests库发送http请求

    前言:什么是Requests ?Requests 是⽤Python语⾔编写,基于urllib,采⽤Apache2 Licensed开源协议的 HTTP 库.它⽐ urllib 更加⽅便,可以节约我们⼤ ...

  5. requests模块发送POST请求

    在HTTP协议中,post提交的数据必须放在消息主体中,但是协议中并没有规定必须使用什么编码方式,从而导致了 提交方式 的不同.服务端根据请求头中的 Content-Type 字段来获知请求中的消息主 ...

  6. 『居善地』接口测试 — 4、Requests库发送GET请求

    目录 1.使用Requests库发送带参数的GET请求 2.查看GET请求的内容 3.带请求头.参数的Get请求 Requests库GET请求是使用HTTP协议中的GET请求方式对目标网站发起请求. ...

  7. Requests库发送post请求,传入接口参数后报JSON parse error

    报错信息: {"timestamp":"2020-01-08T14:42:40.894+0000","status":400,"e ...

  8. python - 怎样使用 requests 模块发送http请求

    最近在学python自动化,怎样用python发起一个http请求呢? 通过了解 request 模块可以帮助我们发起http请求 步骤: 1.首先import 下 request 模块 2.然后看请 ...

  9. Python 学习之urllib模块---用于发送网络请求,获取数据

    1.urllib urllib是Python标准库的一部分,包含urllib.request,urllib.error,urllib.parse,urlli.robotparser四个子模块. (1) ...

随机推荐

  1. web搜索框的制作(必应)

    搜索框中我们输入一些字或者字母,为何下面就会有一些自动补齐的相关搜索,比如我在搜索输入框中输入一个字母e,下面就会出现饿了么,e租宝,ems等相关的搜索链接.然后经过百度,发现原来很多厂商的服务器早已 ...

  2. [spring]基于注解的spring配置

    Spring是一个基于IOC和AOP的结构J2EE系统的框架 IOC 反转控制 是Spring的基础,Inversion Of Control 简单说就是创建对象由以前的程序员自己new 构造方法来调 ...

  3. Ironic 裸金属实例的部署流程

    目录 文章目录 目录 逻辑架构 部署架构 前提条件 部署流程 iSCSI Deploy UML PXE Deploy Driver Direct Deploy UML IPA Deploy Drive ...

  4. Python面向对象进阶和socket网络编程

    写在前面 为什么坚持?想一想当初: 一.面向对象进阶 - 1.反射补充 - 通过字符串去操作一个对象的属性,称之为反射: - 示例1: class Chinese: def __init__(self ...

  5. Java ——类型转换 向args传递参数

    本节重点思维导图  自动类型转换 整型.实型(常量).字符型数据可以混合运算 运算中,不同类型的数据先转化为同一类型,然后进行运算 转换从低级到高级 低 ----------------------- ...

  6. app测试自动化操作方法之三

    首先导包: from appium.webdriver.common.touch_action import TouchAction #(导包指针定位滑动手势密码那个) #设置手势密码(前提是在设备上 ...

  7. IE条件注释语句

    项目 范例 说明 ! [if !IE] The NOT operator. This is placed immediately in front of the _feature_, _operato ...

  8. Git本地初始化并推送到远程仓库

    git常用命令 1.全局配置git用户名邮箱 git config --global user.name '你的名字' git config --global user.email '你的邮箱地址' ...

  9. linux中为什么删除文件比创建文件要快,读取文件和删除文件的过程是什么?

    一.为什么删除文件比创建文件要快? 因为删除文件只是将bitmap位图表中将文件所占据的inode 和dacablock的使用状态从1变成0,相当于释放了这些快的使用权. 二.读取文件和删除文件的过程 ...

  10. 【Python开发】python PIL读取图像转换为灰度图及另存为其它格式(也可批量改格式)

    例如有一幅图,文件名为"a.jpg'.  读取: from PIL import Image #或直接import Image im = Image.open('a.jpg') 将图片转换成 ...