python

关注公众号 jb51net

关闭
首页 > 脚本专栏 > python > Python Requests.post()请求失败retry设置

Python Requests.post()请求失败时的retry设置方式

作者:程序猿-张益达

这篇文章主要介绍了Python Requests.post()请求失败时的retry设置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

Python Requests.post()请求失败retry设置

1. 问题描述

通常,我们在做爬虫工作或远程接口调用的过程中,往往由于访问频率过快等原因遇到连接超时的报错问题,利用最近调用api.ai.qq.com某个接口举例如下:

Traceback (most recent call last):
<class 'Exception'> : HTTPSConnectionPool(host='api.ai.qq.com', port=443):
Max retries exceeded with url: /fcgi-bin/nlp/nlp_textpolar (Caused by ProxyError('Cannot connect to proxy.', OSError('Tunnel connection failed: 504 Gateway Time-out',)))

用代码格式显示更为清晰:

Traceback (most recent call last):
<class 'Exception'>  :  HTTPSConnectionPool(host='api.ai.qq.com', port=443):
 Max retries exceeded with url: /fcgi-bin/nlp/nlp_textpolar (Caused by ProxyError('Cannot connect to proxy.', 
OSError('Tunnel connection failed: 504 Gateway Time-out',)))

之前写爬虫时候为了快速解决问题就写了一堆的while与try来实现链接的重新访问,这种方式可以在一定程度上解决问题,但回看自己的代码时候总觉得奇丑无比,今天在做一个远程接口调用时候又遇到这个报错,想着python的requests模块中一定有内置方法来解决这个问题,于是便尝试了一下,但同时又引发了另一个小问题,遂决定记录一下。

2. 解决方案

其实,我们在http请求中遇到连接超时导致访问中断的情况时,往往不需要自己来写重访问策略,requests模块中有对应的重访问设置,可以通过设置最大重访问次数,当一次访问失败时,会自动进行重新访问,实现方式(简化)如下:

import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util import Retry
s = requests.Session()
s.mount('https://', HTTPAdapter(max_retries=Retry(total=5)))
resp_get = s.get(url=http_url, data={'key':'value'})
resp_post = s.post(url=http_url, data={'key':'value'})

可以看到,我们设置最大重访问次数为5。但此时会发现,如果我们使用的是 get() 方法的话则没有问题,但如果使用 post() 方法的话仍然会报错。

通过查资料发现,python的requests模块使用的urllib3,而urllib3默认对 post() 方法不设置重访问,因此需要我们手动指定才可以,修改如下:

import requests
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util import Retry
s = requests.Session()
s.mount('https://', HTTPAdapter(max_retries=Retry(total=5, method_whitelist=frozenset(['GET', 'POST'])))) # 设置 post()方法进行重访问
resp_get = s.get(url=http_url, data={'key':'value'})
resp_post = s.post(url=http_url, data={'key':'value'})

这样再使用requests.post()时几乎不会出现超时中断的情况了!

python requests.post请求404

问题场景

有时候,在编写一段http接口请求程序时,发现代码中的header头和请求体中都是原网页中一样,但是,在实际请求时,接口却返回404,代码如下

header = {
    # ':authority': 'm.ctrip.com',
    # ':method': 'POST',
    # ':path': '/restapi/soa2/20405/getPCSightList',
    # ':scheme': 'https',
    'accept': '*/*',
    'accept-encoding': 'gzip, deflate, br',
    'accept-language': 'zh-CN,zh;q=0.9',
    'authorization': 'xx',
    'cache-control': "no-cache",
    'content-length': '373',
    'content-type': 'application/json;charset:utf-8;',
    'cookies': 'xx',
    'origin': 'https://www.tripadvisor.cn',
    'pragma': 'no-cache',
    'referer': 'https://www.tripadvisor.cn/Attractions-g60763-New_York_City_New_York-Vacations.html',
    'sec-ch-ua': "'.Not/A)Brand';v='99', 'Google Chrome';v='103', 'Chromium';v='103'",
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': 'macOS',
    'sec-fetch-dest': 'empty',
    'sec-fetch-mode': 'cors',
    'sec-fetch-site': 'cross-site',
    'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36',
    'x-ta-uid': 'cd58b674-7dba-484a-a908-3239120cd728'
}
url = 'https://m.ctrip.com/restapi/soa2/20405/getPCSightList'
data = {"geoId":60763,"pageIndex":1,"pageSize":30,"travelRanking":"false","needSelectedFilters":"true","filters":[{"type":"subcategory","param":""},{"type":"subtype","param":""},{"type":"neighborhood","param":""},{"type":"travelerRating","param":""},{"type":"awards","param":""},{"type":"waypointairport","param":""},{"type":"waypointstation","param":""},{"type":"other","param":""}]}
response = requests.post(url=url, data=data, headers=header)
print(response.status_code)

运行结果为

问题分析

既然404,那就排查问题,data和header都是直接从复制浏览器中复制过来的,不会有问题,那有问题的必然是request.post中的方法问题

从源码中可以看到,request.post可以接受两个参数,一个是data,还有一个是json,

data是以字典的形式发送body,json则是以json数据格式发送body

通过这两个注释可以很明显的发现,requests.post在发送请求时,会根据当前传递的参数来选择不同的方式,可以理解为一种是表单形式,还有一种是json格式

通过postman来测试两种不同请求下的情况

form表单

Json数据

通过postman测试可以发现,当前服务后端接口仅接受json格式的数据,即content-type为application.json

问题解决

将原先代码中data替换成json

response = requests.post(url=url, json=data, headers=JsonHeader)

这个时候肯定有人会问,那我使用data传递数据时,将header头中的content-type指定为application/json不就行了,但其实是不行的,就算自己指定了,最后request.body的值也是类似于key1=value1&key2=value2这种形式

心得

1.当request.post使用json来传递参数时,即使不指定content-type类型,也会默认指定application/json

2.使用data传递参数时,将会以表单的形式进行提交,并且后续将通过urlencode转换成字符串,及key1=value1&key2=value2的形式

3.使用data时,即使指定了content-type也不会生效,后续将会被默认替换掉

因此,需要根据自己实际情况来分析当前接口接收数据时使用的是什么格式,但目前一般的网站都开始采用application/jsond的数据格式

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

您可能感兴趣的文章:
阅读全文