如何绕过网站抓取时的速率限制

Web 抓取是从 Internet 收集数据的重要工具。但是,有时,由于速率限制,您在网络抓取时会被阻止。那么速率限制是如何工作的,你的爬虫又如何避免呢?

在本文中,我们将介绍什么是速率限制以及您在网络抓取时如何被阻止。然后我们将看看我们可以做些什么来避免速率限制。

Web 抓取中的速率限制是什么?

web_scraping_rate_limit

那么,限速是什么意思?

速率限制是您可以在特定时间窗口内发送的请求的硬限制。对于 API,它是您可以进行的 API 调用的最大数量。因此,当资源受到速率限制时,您不能发送超过定义限制的请求。如果超出限制,您会收到错误响应,例如:

  • 放慢速度,来自此 IP 地址的请求过多
  • IP 地址已达到速率限制

CloudflareAkamaiDatadome等 WAF 服务提供商使用速率限制主要是为了安全。同时,像亚马逊这样的 API 提供商使用速率限制来控制数据流并防止过度使用。

那么它是怎样工作的?假设您在 Web 服务器上受到速率限制。当您的爬虫超过速率限制时,网络服务器会响应429: Too Many Requests.

不同的限速方法。但我们对实践中的速率限制应用感兴趣。所以这里是流行的速率限制类型。

  • 速率限制:最流行的速率限制方式。简单地将请求数与用户的 IP 地址相关联。
  • API 速率限制:API 提供商通常要求您使用 API 密钥。提供商可以限制您在特定时间范围内可以进行的 API 调用次数。
  • 地理速率限制:也可以为特定地区和国家设置速率限制。
  • 基于用户会话的速率限制:WAF 供应商(如 Akamai)设置会话 cookie,然后限制您会话的请求速率。
  • 基于 HTTP 请求的速率限制:Cloudflare 支持特定 HTTP 标头和 cookie 的速率限制。也可以通过 TLS 指纹实施速率限制。

为什么 API 速率受限?

许多 API 是有速率限制的,不会使 Web 服务器过载。API 速率限制还可以保护 API 免受恶意机器人和 DDoS 攻击。这些攻击会阻止合法用户使用 API 服务或完全关闭其服务。

为什么网站使用速率限制?

主要原因是为了防止服务器过载并减轻潜在的攻击。然而,即使您的意图不是恶意的,您也可能在网络抓取时陷入速率限制。原因是为了控制服务器端的数据流。

在网络抓取时绕过速率限制

您可以做些什么来避免网络抓取中的速率限制?以下是您可以使用的流行方法和技巧!

  • 使用代理服务器
  • 使用特定的请求标头
  • 更改 HTTP 请求标头

最流行的速率限制方法是基于 IP 的速率限制。因此,最好使用代理服务器,因为大多数平台都实施基于 IP 的速率限制

在请求中使用特定标头

我们可以使用多个标头在后端欺骗 IP。当 CDN 传送内容时,您可以尝试使用这些标头

  • X-Forwarded-Host:这是一个标头,用于在 Host HTTP 请求标头中标识客户端请求的原始主机。使用广泛的主机名列表可以绕过速率限制。您可以在此标头中传递 URL。
  • X-Forwarded-For:标识通过代理服务器连接到 Web 服务器的客户端的原始 IP 地址。它需要指定用于连接的代理服务器的 IP 地址。传递单个 IP 地址或使用 IP 地址列表进行暴力破解是可能的。

下面的标头指定客户端的 IP 地址。但是,它们可能不会在每项服务中实施。您可以更改这些标头上的 IP 地址并试试运气!

  • X客户端IP
  • X-Remote-IP
  • X-远程地址

更改 HTTP 请求标头

发送带有随机 HTTP 标头的请求可用于绕过速率限制。许多网站和 WAF 供应商使用 HTTP 标头来阻止恶意机器人。您可以随机化标题User-Agent以避免速率限制。这是网络抓取的最佳实践。您可以从我们的文章中了解更多信息,了解避免被屏蔽的 10 个最佳技巧!

如果您使用的是 Python,您可以查看我们的指南以避免像专家一样在 Python 中阻塞

终极解决方案:代理服务器

当您使用代理服务器时,您发送的请求将被路由到代理服务器。然后代理服务器得到响应并将数据转发给你。您无需处理限速代理,因为您始终可以使用另一个代理。因此,使用代理服务器是IP限速的最终解决方案。

虽然有公共和免费使用的代理服务器,但这些服务器通常被 WAF 供应商(例如 Cloudflare)和网站阻止。您可以在许多网站上找到免费的公共代理服务器。但 WAF 服务提供商也可以使用这些网站并阻止它们。

有两种类型的代理服务器。

  • 住宅代理:IP 地址由 ISP 分配。它们比数据中心代理可靠得多,因为它们附加到实际地址。主要缺点是价格;高质量的代理服务器成本更高。
  • 数据中心代理:数据中心代理是商业分配的。它们通常没有唯一地址,可以被网站和 WAF 服务标记。因此,它们比住宅代理服务器更实惠但可靠性更低。

住宅代理是行业标准,因为它们更可靠。但您也可以使用智能轮换代理,它会在您每次发送请求时自动使用随机驻留代理服务器。

Python 中的代理轮换

但是,有一个问题:如果该 IP 被阻止怎么办?

您可以构建代理服务器列表并在发送请求时轮换这些服务器!由于这将允许您使用多个 IP 地址,因此您不会因为 IP 速率限制算法而被阻止。

但是,如果存在身份验证会话,则无需在每个请求中更改代理服务器。您的会话也可以被跟踪,因此为每个请求轮换代理服务器不是一个合适的解决方案。

在开始之前,让我们先获取代理列表

大多数公共代理服务器寿命短且不可靠。因此,当您尝试使用以下某些服务器时,它们可能无法正常工作。

138.68.60.8:3128 
54.66.104.168:80 
80.48.119.28:8080 
157.100.26.69:80 
198.59.191.234:8080 
198.49.68.80:80 
169.57.1.85:8123 
219.78.228.211:80 
88.215.9.208:80 
130.41.55.190:8080 
88.210.37.28:80 
128.199.202.122:8080 
2.179.154.157:808 
165.154.226.12:80 
200.103.102.18:80

第一步,将代理服务器保存在文本文件 ( proxies.txt) 中。之后,我们将从ip:port文件中读取对并检查它们是否正常工作。然后,我们将编写简单的同步函数来从随机代理服务器发送请求。

如果您的应用程序大规模工作,您将需要使用异步代理旋转器。您可以查看我们关于在 Python 中构建成熟的代理旋转器的指南。

为了检查代理服务器,我们将向httpbin发送请求。如果代理服务器不工作,我们将在响应中收到错误消息。

现在,让我们开始吧!

首先,我们需要从以下位置读取代理proxies.txt

proxies = open("proxies.txt", "r").read().strip().split("n")

我们将使用该requests模块向 URL 发送请求。不幸的是,公共代理通常不支持 SSL。所以我们将使用 HTTP 而不是 HTTPS。

get()函数使用给定的代理服务器将 GET 请求发送到目标 URL。当出现错误时,返回的状态码会超过400。400到500之间的状态码表示客户端错误,超过500表示服务器端错误。如果是这种情况,该函数将返回None。最后,如果没有错误,它会返回实际的响应。

def get(url, proxy): 
    """ 
    Sends a GET request to the given url using given proxy server. 
    The proxy server is used without SSL, so the URL should be HTTP. 
 
    Args: 
        url - string: HTTP URL to send the GET request with proxy 
        proxy - string: proxy server in the form of {ip}:{port} to use while sending the request 
    Returns: 
        Response of the server if the request sent successfully. Returns `None` otherwise. 
 
    """ 
    try: 
        r = requests.get(url, proxies={"http": f"http://{proxy}"}) 
        if r.status_code < 400: # client-side and server-side error codes are above 400 
            return r 
        else: 
            print(r.status_code) 
    except Exception as e: 
        print(e) 
 
    return None

现在,可以检查我们的代理。该函数通过向httpbincheck_proxy()发送 GET 请求返回 True 或 False 。我们可以从我们的代理列表中过滤掉可用的代理服务器。

def check_proxy(proxy): 
    """ 
    Checks the proxy server by sending a GET request to httpbin. 
    Returns False if there is an error from the `get` function 
    """ 
 
    return get("http://httpbin.org/ip", proxy) is not None 
 
available_proxies = list(filter(check_proxy, proxies))

之后,我们可以使用该random模块从可用的代理服务器中随机选择一个代理服务器。然后,我们将使用该get函数发送请求!

结论

恭喜,您现在有一个片段可以从随机 IP 地址发送请求!

作为我们在文章中所涵盖内容的总结:

  1. 虽然有不同的方法,但最流行的一种是 IP 速率限制
  2. 可以通过使用不同的 HTTP 标头来传递硬限制
  3. 最方便的解决方案通常基于代理轮换
  4. 公共和免费代理服务器不可靠,更好的选择是使用住宅代理服务器。

然而,这种简单的实现对于扩展应用程序来说是不够的。有很大的改进空间。因此,再次查看我们的 Python 指南以了解可扩展的代理轮换

类似文章