Python的requests库被广泛用于各种用途,其中最常见的是网页抓取。与内置的HTTP请求库(urllib)相比,requests库更加简化,使得与服务器和网站的连接变得更加容易。
网页抓取项目通常使用requests库,因为它既简单又高效。此外,它也更容易进行故障排除,这在抓取过程中频繁出现请求失败时尤为有用。
开始使用Requests库
本指南假设你已经具备一些Python基础知识并使用了一个IDE。一旦这些准备工作就绪,你需要安装requests库。(本文中我们以IPRoyal为例)
pip install requests
你的IDE应该会自动下载、解压并安装requests库,然后你就可以用它来发送请求了。
首先,你需要像使用其他Python库一样导入它。
import requests
发送请求很简单,因为它只是对“get”方法(或您需要的任何其他方法)的简单调用。
import requests def send_get_request(URL): r = requests.get(URL) print(r.status_code) send_get_request('https://iproyal.com')
打印响应的状态码稍后将非常重要,因为这些代码可以作为优化失败请求的指示器。您可以通过点击右上角的绿色箭头(对于PyCharm)来测试它。
失败请求的响应类型
所有成功和失败的服务器连接尝试都会返回某种形式的HTTP状态码。我们将忽略成功的状态码,因为你不需要重试它们。
403 Forbidden(禁止访问)
你的目标服务器理解了请求,但没有适当响应,因为你没有权限访问该文档(或整个服务器)。这些问题通常难以解决,因为403状态码通常在你需要凭证或已被禁止访问时返回。
如果你有凭证,可以将它们包含在你的GET请求中。
import requests def send_get_request(URL, credentials): r = requests.get(URL, auth=credentials) print(r.status_code) login_details = ('username', 'password') send_get_request('https://iproyal.com', login_details)
将“login_details”对象的值替换为您的用户名和密码应该允许您访问受保护的文档。
请注意,它只适用于选定的几个网站。现在大多数人使用更复杂的登录方式。
429 Too Many Requests(请求过多)
这是网页抓取时最常见的HTTP错误响应之一,429状态码表示你向同一端点发送了过多请求。
切换代理或实施重试失败请求的策略是最佳选择。
500 Internal Server Error(内部服务器错误)
服务器端出现了故障。简单的重试可能会立即或在几分钟内解决问题。
502 Bad Gateway(错误网关)
与500内部服务器错误几乎相同——上游服务器出现问题,导致请求失败。稍后重试可能会解决问题。
503 Service Unavailable(服务不可用)
表示服务器可能完全宕机或不可用。虽然你可以重试失败的请求,但只有在管理员修复问题后才能解决。
504 Gateway Timeout(网关超时)
表示网络问题,可能由任一端引起。逐渐增加延迟时间进行重试可能会解决问题。
实施失败请求重试策略
Python的requests库提供了所有你需要的工具,能够有效地处理大多数失败的请求。在上述HTTP状态码列表中,只有403和429需要独特的处理方法,尽管429也可以像其他状态码一样解决。
创建Python请求重试策略有两种方法,一种是在设定的间隔内进行简单循环,另一种是使用递增的延迟时间。前者的优点是解决速度更快,但也更容易被检测到。
使用循环重试失败请求
import requests import time def send_get_request(URL, retry): for i in range(retry): try: r = requests.get(URL) if r.status_code not in [200, 404]: time.sleep(5) else: break except requests.exceptions.ConnectionError: pass print(r.status_code) send_get_request('https://dashboard.iproyal.com/login', 5)
由于我们将使用sleep函数来创建延迟,因此需要导入time库,这一步紧跟在导入Python requests库之后,虽然顺序并不重要。
在我们的函数中,我们现在包括一个“retry”参数,用于指定我们将重试失败请求的次数。
此外,包含了一个for循环,它接受重试次数并将其用作范围。包含一个“if”语句来验证是否接收到200或404状态码。如果都不是,那么函数将休眠5秒并重复该过程。
如果接收到200或404状态码,函数将停止。此外,如果发生连接错误,它将什么也不做,绕过常规的Python requests错误处理。
最后,如果超时导致问题,你可以通过添加参数“(timeout = N)”来设置自定义的Python requests.get超时函数。
使用HTTPAdapter重试失败请求
对于第二种策略,我们需要导入的不仅仅是Python requests库。
import requests from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry
HTTPAdapter将允许我们将失败的请求重试策略挂载到会话中。我们的重试策略将由“urllib3”重试实用程序定义。
import requests from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry def send_get_request(URL): sess = requests.session() retries = Retry(total = 5, backoff_factor = 1, status_forcelist = [429, 500, 502, 503, 504]) sess.mount('https://', HTTPAdapter(max_retries=retries)) get_URL = sess.get(URL) print(get_URL.status_code) send_get_request('https://iproyal.com')
我们的函数现在以一个会话开始,而不是直接发送请求,这是当前失败请求策略所必需的。
接下来,我们定义一个带有几个参数的重试对象。首先,我们将Python重试的总次数设置为5,退避因子设置为1,并指定哪些状态码应被重试。退避因子是一个更复杂的sleep函数,其定义如下:
{backoff factor} * (2 ** ({retry number} - 1))
我们的第一次重试将是立即进行的,但之后的重试将会在越来越长的间隔内进行。
然后,我们将我们的会话挂载到HTTPAdapter上,它将执行所有必要的重试。之后的步骤基本上与其他策略相同。
最后,任何Python请求在继续之前都会等待响应。如果你想并行发送多个请求,则需要使用异步编程。
使用代理绕过429
在你的网页抓取项目中集成代理时,有一种独特的方法可以避免429(请求过多),而不是使用Python请求重试策略。
由于429状态码是分配给IP地址的,你可以在收到该HTTP错误码时切换代理来完全避开它。只要你有按需付费的住宅代理,你就可以不断切换IP地址来避免429。
你也可以设置一个失败请求重试策略,作为应对其他错误码的备用方案。
import requests from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry def send_get_request(URL): sess = requests.session() proxies = {"http" : "http://USER:PASS@HOST:PORT"} retries = Retry(total = 5, backoff_factor = 1, status_forcelist = [500, 502, 503, 504]) sess.mount('https://', HTTPAdapter(max_retries=retries)) get_url = sess.get(URL, proxies=proxies) if get_url.status_code == 429: sess.get(URL, proxies=proxies) print(get_url.status_code) send_get_request('https://iproyal.com')
由于我们使用的是轮换的住宅代理,如果收到429错误,只需向相同的端点发送一个新的请求。轮换代理会自动提供一个新的IP地址。
对于粘性会话,你应该生成一个更大的代理列表并将其放入字典对象中,然后使用if语句在收到429错误时切换到新的IP地址。
总 结
这些基本策略应该可以让你自动解决大多数HTTP错误码。避免大多数常见HTTP错误有两种策略。你可以设置一个基本循环来重试失败的请求:
import requests import time def send_get_request(URL, retry): #defines a function to send get requests with two arguments for i in range(retry): #sets a range for the amount of retries try: r = requests.get(URL) if r.status_code not in [200, 404]: time.sleep(5) #tries to retrieve the URL, if 200 or 404 is not received, waits 5 seconds before trying again else: break #stops function if 200 or 404 received except requests.exceptions.ConnectionError: pass print(r.status_code) send_get_request('https://iproyal.com', 5)
或者你可以使用HTTPAdapter Python请求重试策略,这可能会慢一点,但不易检测:
import requests from requests.adapters import HTTPAdapter from requests.packages.urllib3.util.retry import Retry def send_get_request(URL): #defines a get request function with one argument sess = requests.session() #sets a session object retries = Retry(total = 5, backoff_factor = 1, status_forcelist = [429, 500, 502, 503, 504]) #sets the retry amount to 5, backoff_factor to 1, and sets specific HTTP error codes to be retried on sess.mount('https://', HTTPAdapter(max_retries=retries)) #mounts HTTPAdapter to the session get_URL = sess.get(URL) print(get_URL.status_code) send_get_request('https://iproyal.com')
最后,对于429,每次收到错误代码时都可以切换IP地址。只需要一个if语句和一个新的status_forcelist。