Python requests 超时机制与重试策略详解
本文深入解析 Python requests 库中的超时处理机制,区分连接超时与读取超时的定义及默认行为。通过代码示例演示如何分别设置两种超时时间,并介绍利用 HTTPAdapter 配置最大重试次数(max_retries)的方法,帮助开发者避免爬虫程序因网络波动而永久挂起或频繁报错。
在进行网络请求时,超时问题是无法完全避免的。若在使用 requests 库时未显式配置超时参数,程序可能会陷入无限等待状态,导致永远无法获得响应。
超时机制主要包含两个维度:连接超时和读取超时。
连接超时
连接超时是指客户端尝试与远程服务器端口建立连接(即调用 connect() 方法)时,Request 库所等待的最大秒数。
import time
import requests
url = 'http://www.google.com.hk'
print(time.strftime('%Y-%m-%d %H:%M:%S'))
try:
html = requests.get(url, timeout=5).text
print('success')
except requests.exceptions.RequestException as e:
print(e)
print(time.strftime('%Y-%m-%d %H:%M:%S'))
以访问被墙的网站 google 为例,由于无法建立连接,错误信息会明确提示 connect timeout(连接超时)。
2018-12-14 14:38:20 HTTPConnectionPool(host='www.google.com.hk', port=80): Max retries exceeded with url: / (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x00000000047F80F0>, 'Connection to www.google.com.hk timed out. (connect timeout=5)')) 2018-12-14 14:38:25
即使开发者未手动设置超时参数,requests 也会应用一个默认的连接超时时间。经测试,该默认值约为 21 秒。
读取超时
读取超时指的是客户端等待服务器发送数据的时间。具体而言,它是指客户端等待服务器发送字节之间的时间间隔;在绝大多数(99.9%)情况下,这等同于等待服务器发送第一个字节之前的时间。
简而言之,连接超时限制了从发起请求到连接建立之间的最大时长;而读取超时则限制了连接成功后,等待服务器返回响应数据的最大时长。
读取超时没有默认值。如果不进行设置,程序将一直处于等待状态。许多爬虫程序出现卡死且无报错信息的情况,往往就是由于未设置读取超时所致。
若为 timeout 参数指定单一数值,如下所示:
r = requests.get('https://github.com', timeout=5)
此时该 timeout 值将同时作为 connect 和 read 的超时时间。若需分别指定,则应传入一个元组:
r = requests.get('https://github.com', timeout=(3.05, 27))
黑板课爬虫闯关的第四关人为设置了 15 秒的响应等待时间,非常适合用来说明读取超时的效果。
import time
import requests
url_login = 'http://www.heibanke.com/accounts/login/?next=/lesson/crawler_ex03/'
session = requests.Session()
session.get(url_login)
token = session.cookies['csrftoken']
session.post(url_login, data={'csrfmiddlewaretoken': token, 'username': 'guliang21', 'password': '123qwe'})
print(time.strftime('%Y-%m-%d %H:%M:%S'))
url_pw = 'http://www.heibanke.com/lesson/crawler_ex03/pw_list/'
try:
html = session.get(url_pw, timeout=(5, 10)).text
print('success')
except requests.exceptions.RequestException as e:
print(e)
print(time.strftime('%Y-%m-%d %H:%M:%S'))
错误信息中会显示 read timeout(读取超时)。
2018-12-14 15:20:47 HTTPConnectionPool(host='www.heibanke.com', port=80): Read timed out. (read timeout=10) 2018-12-14 15:20:57
超时重试
面对一般性超时,我们通常不会直接返回错误,而是配置一个包含三次重连的机制。
def gethtml(url):
i = 0
while i < 3:
try:
html = requests.get(url, timeout=5).text
return html
except requests.exceptions.RequestException:
i += 1
实际上,requests 已经为我们封装好了相关功能(尽管代码量可能有所增加……)。
import time
import requests
from requests.adapters import HTTPAdapter
s = requests.Session()
s.mount('http://', HTTPAdapter(max_retries=3))
s.mount('https://', HTTPAdapter(max_retries=3))
print(time.strftime('%Y-%m-%d %H:%M:%S'))
try:
r = s.get('http://www.google.com.hk', timeout=5)
return r.text
except requests.exceptions.RequestException as e:
print(e)
print(time.strftime('%Y-%m-%d %H:%M:%S'))
max_retries 参数用于设定最大重试次数。若设置为重试 3 次,加上最初的一次请求,总共将执行 4 次尝试。因此,上述代码的运行耗时约为 20 秒,而非单一的 15 秒。
2018-12-14 15:34:03 HTTPConnectionPool(host='www.google.com.hk', port=80): Max retries exceeded with url: / (Caused by ConnectTimeoutError(<urllib3.connection.HTTPConnection object at 0x0000000013269630>, 'Connection to www.google.com.hk timed out. (connect timeout=5)')) 2018-12-14 15:34:23
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









