0

HTTP请求:requests的进阶使用方法浅析 - 京东云开发者

 11 months ago
source link: https://www.cnblogs.com/Jcloud/p/17486070.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

上篇文章讲解了requests模块的基础使用,其中有get、put、post等多种请求方式,使用data、json等格式做为请求参数,在请求体中添加请求头部信息的常见信息,如:headers、cookies,以及对请求响应的处理方法。接下来讲解一下requests的高级用法。

2 进阶方法举例

2.1 requests.request()

method:提交方式(get|post);

url:提交地址;

kwargs:14个控制访问的参数;

17fc44c27da94787809ba2a9f95d23e2~tplv-k3u1fbpfcp-zoom-1.image

常用的参数有:params、data、json、headers、cookies,已在上篇文章中介绍过了,感兴趣的朋友,可以到上篇文章再回顾一下。以下将讲解与示例其他参数的使用。

2.1.1 files

请求携带文件,如果有的请求需要上传文件,可以用它来实现。

import requests

# 上传文件
f= {"files": open("favicon.ico", "rb") }
data = {"name": "上传文件"}

requests.request(
    method = 'POST', 
    url = 'http://127.0.0.1:8080/example/request',  
    data = data,
    files = f
)

需注意:favicon.ico文件需和当前脚本在同一目录下,如果不在,可以将文件名称修改为文件路径

import requests
from requests.auth import HTTPBasicAuth, HTTPDigestAuth

# 1、Basic Auth认证
res = requests.request(
    method = 'GET',
    url = 'http://127.0.0.1:8080/example/request',
    auth = HTTPBasicAuth("username", "password")
)
res.encoding = "gbk"

print(res.status)  # 200


# 2、DIGEST 认证
res = requests.request(
    method = 'GET',
    url = 'http://127.0.0.1:8080/example/request',
    auth = HTTPDigestAuth("username", "password")
)
res.encoding = "gbk"

print(res.status)  # 200

http auth认证的两种方式,分别为Basic方式和Digest认证,其中:Basic Auth的优点是提供简单的用户验证功能,其认证过程简单明了,适合于对安全性要求不高的系统或设备中;同样存在缺点:输入的用户名,密码 base64编码后会出现在Authorization里,很容易被解析出来。

那么Digest对比Basic认证有什么不同呢?

  • Digest思想,是使用一种随机数字符串,双方约定好对哪些信息进行哈希运算,即可完成双方身份的验证。Digest模式避免了密码在网络上明文传输,提高了安全性,但它依然存在缺点,例如认证报文被攻击者拦截到攻击者可以获取到资源。
  • DIGEST 认证提供了高于 BASIC 认证的安全等级,但是和 HTTPS 的客户端认证相比仍旧很弱。
  • DIGEST 认证提供防止密码被窃听的保护机制,但并不存在防止用户伪装的保护机制。
  • DIGEST 认证和 BASIC 认证一样,使用上不那么便捷灵活,且仍达不到多数 Web 网站对高度安全等级的追求标准。因此它的适用范围也有所受限。

2.1.2 timeout

请求和响应的超时时间,在网络响应延迟或者无响应时,可以通过设置超时时间,避免等待。

import requests

# 设置请求超时1秒,1秒后无响应,将抛出异常,1秒为connect和read时间总和
requests.request(
    method = 'POST',
    url = 'http://127.0.0.1:8080/example/request',
    json = {'k1' : 'v1', 'k2' : 'v2'},
    timeout = 1
)

# 分别设置connect和read的超时时间,传入一个数组
requests.request(
    method = 'POST',
    url = 'http://127.0.0.1:8080/example/request',
    json = {'k1' : 'v1', 'k2' : 'v2'},
    timeout = (5, 15)
)

# 永久等待
requests.request(
    method = 'POST',
    url = 'http://127.0.0.1:8080/example/request',
    json = {'k1' : 'v1', 'k2' : 'v2'},
    timeout = None
    # 或者删除timeout参数
)

# 捕捉超时异常
from requests.exceptions import ReadTimeout
try:
    res = requests.get('http://127.0.0.1:8080/example/request', timeout=0.1)
    print(res.status_code)
except ReadTimeout:
    print("捕捉到超时异常")

2.1.3 allow_redirects

设置重定向开关。

>>> import requests
>>> r = requests.get('http://github.com')
>>> r.url
'https://github.com/'

>>> r.status_code
200

>>> r.history
[<Response [301]>]

# 如果使用GET、OPTIONS、POST、PUT、PATCH或DELETE,则可以使用allow_redirects参数禁用重定向
>>> r = requests.get('http://github.com', allow_redirects=False)

>>> r.status_code
301

>>> r.history
[]

# 用HEAD启动重定向
>>> r = requests.head('http://github.com', allow_redirects=True)

>>> r.url
'https://github.com/'

>>> r.history
[<Response [301]>]


import requests
import re

# 第一次请求
r1=requests.get('https://github.com/login')
r1_cookie=r1.cookies.get_dict() #拿到初始cookie(未被授权)
authenticity_token=re.findall(r'name="authenticity_token".*?value="(.*?)"',r1.text)[0] #从页面中拿到CSRF TOKEN

# 第二次请求:带着初始cookie和TOKEN发送POST请求给登录页面,带上账号密码
data={
    'commit':'Sign in',
    'utf8':'✓',
    'authenticity_token':authenticity_token,
    'login':'[email protected]',
    'password':'password'
}


# 测试一:没有指定allow_redirects=False,则响应头中出现Location就跳转到新页面,
# r2代表新页面的response
r2=requests.post('https://github.com/session',
             data=data,
             cookies=r1_cookie
             )

print(r2.status_code) # 200
print(r2.url) # 看到的是跳转后的页面
print(r2.history) # 看到的是跳转前的response
print(r2.history[0].text) # 看到的是跳转前的response.text

# 测试二:指定allow_redirects=False,则响应头中即便出现Location也不会跳转到新页面,
# r2代表的仍然是老页面的response
r2=requests.post('https://github.com/session',
             data=data,
             cookies=r1_cookie,
             allow_redirects=False
             )

print(r2.status_code) # 302
print(r2.url) # 看到的是跳转前的页面https://github.com/session
print(r2.history) # []

2.1.4 proxies

同添加headers方法一样,代理参数是dict。

import requests
import re
def get_html(url):
    proxy = {
        'http': '120.25.253.234:812',
        'https' '163.125.222.244:8123'
    }
    heads = {}
    heads['User-Agent'] = 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.221 Safari/537.36 SE 2.X MetaSr 1.0'
    req = requests.get(url, headers=heads,proxies=proxy)
    html = req.text
    return html
def get_ipport(html):
    regex = r'<td data-title="IP">(.+)</td>'
    iplist = re.findall(regex, html)
    regex2 = '<td data-title="PORT">(.+)</td>'
    portlist = re.findall(regex2, html)
    regex3 = r'<td data-title="类型">(.+)</td>'
    typelist = re.findall(regex3, html)
    sumray = []
    for i in iplist:
        for p in portlist:
            for t in typelist:
                pass
            pass
        a = t+','+i + ':' + p
        sumray.append(a)
    print('代理')
    print(sumray)
if __name__ == '__main__':
    url = 'http://www.baidu.com'
    get_ipport(get_html(url))

某些接口增加了防骚扰模式,对于大规模且频繁的请求,可能会弹出验证码,或者跳转到登录验证页面,或者封禁IP地址,此时如果想要正常访问,可以通过设置代理来解决这个问题。
除了基本的HTTP代理外,requests还支持SOCKS协议的代理。

# 安装socks库
pip3 install "requests[socks]"

# 进行代理
import requests

proxies = {
    'http': 'socks5://user:password@host:port',
    'https': 'socks5://user:password@host:port'
}
res = requests.get('http://www.baidu.com', proxies=proxies)
print(res.status)  # 200

2.1.5 hooks

即钩子方法,requests库只支持一个response的钩子,即在响应返回时,可以捎带执行自定义方法。可以用于打印一些信息、做一些响应检查、或者向响应中添加额外的信息。

import requests
url = 'http://www.baidu.com'

def verify_res(res, *args, **kwargs):
    print('url', res.url)
    res.status='PASS' if res.status_code == 200 else 'FAIL'

res = requests.get(url, hooks={'response': verify_res})
print(res.text) # <!DOCTYPE html><!--STATUS OK--><html> 
print(res.status) # PASS

2.1.6 stream

获取内容立即下载开关,response会将报文一次性全部加载到内存中,如果报文过大,可以使用此参数,迭代下载。

import requests

url="http://www.baidu.com"

r = requests.get(url, stream=True)

# 解析response_body,以\n分割
for lines in r.iter_lines():
    print("lines:", lines)

# 解析response_body,以字节分割
for chunk in r.iter_content(chunk_size=1024):
    print("chunk:", chunk)

2.1.7 verify

认证SSL证书开关,当发送HTTPS请求的时候,如果该网站的证书没有被CA机构信任,程序将报错,可以使用verify参数控制是否检查SSL证书。

# 1、直接设置
import requests

response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code)

# 2、请求时虽然设置了跳过检查,但是程序运行时仍然会产生警告,警告中包含建议给我们的指定证书
# 可以通过设置,忽略屏蔽这个警告
from requests.packages import urllib3  # 如果报错,则直接引入import urllib3

# 3、屏蔽警告
urllib3.disable_warnings()

response = requests.get('https://www.12306.cn', verify=False)
print(response.status_code) # 200

# 4、通过cert直接声明证书
# 本地需要有crt和key文件(key必须是解密状态,加密状态的key是不支持的),并指定它们的路径,
response = requests.get('https://www.12306.cn',cert('/path/server.crt','/path/key'))
print(response.status_code) # 200

2.2 requests库的异常

如何判断是否出现异常呢?

2.2.1 raise_for_status()

该方法在内部判断res.status_code是否等于200,不是则产生异常HTTPError

示例:

# 1、HTTPError异常示例
import requests
from requests.exceptions import HTTPError

try:
    res = requests.post("http://127.0.0.1:8080/example/post")
    res.raise_for_status()
    # 等同于
    if res.status != 200:
        raise HTTPError

    return res

except HTTPError:
    return False

2.2.2 ReadTimeout

该异常类型,将会捕捉到因请求/响应超时的请求。

# Timeout超时异常
import requests
from requests.exceptions import ReadTimeout

try:
    res = requests.get('http://127.0.0.1:8080/example/post',timeout=0.5)
    print(res.status_code)
    return res

except ReadTimeout:
    print('timeout')

2.2.3 RequestException

该异常类型,将会捕捉到因无请求引起的异常请求。

# RquestError异常
import requests
from requests.exceptions import RequestException

try:
    res = requests.get('http://127.0.0.1:8080/example/post')
    print(res.status_code)
    return res

except RequestException:
    print('reqerror')

看到这里,大家应该明白了,requests库是一个比urilib2模块更加简洁的第三方库,它具有如下的特点:

  • 支持HTTP连接保持和连接池
  • 支持使用cookie、session保持会话
  • 支持文件上传
  • 支持自动响应内容的编码
  • 支持国际化的URL和Post数据自动编码
  • 支持自动实现持久连接keep-alive

因此,requests这个高度封装的模块,可以使我们的HTTP请求,变得更加人性化,使用它将可以轻而易举的完成浏览器请求的任何操作,充分诠释了它的口号:“HTTP for Humans”。

作者:京东物流 骆铜磊

来源:京东云开发者社区


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK