Python爬虫多线程并发时的503错误处理最佳实践

小白学大数据
• 阅读 0

一、503 错误产生的原因 在 HTTP 协议中,503 错误表示服务器当前无法处理请求,通常是因为服务器暂时过载或维护。在多线程爬虫场景下,503 错误可能由以下几种原因引起:

  1. 服务器负载过高:当多个线程同时向服务器发送请求时,服务器可能因负载过高而拒绝部分请求,返回 503 错误。
  2. 请求频率过快:如果爬虫的请求频率超过了服务器的处理能力,服务器可能会认为这是一种攻击行为,从而返回 503 错误。
  3. 服务器配置问题:某些服务器可能配置了特定的防护机制,如防火墙或反爬虫策略,当检测到异常请求时会返回 503 错误。
  4. 网络问题:网络不稳定或代理服务器故障也可能导致 503 错误。 二、503 错误处理的最佳实践 (一)合理控制并发线程数量 过多的并发线程会增加服务器的负载,导致 503 错误。因此,合理控制并发线程的数量是避免 503 错误的关键。可以通过设置线程池来限制并发线程的数量。
    import requests
    

def fetch_url(url): try: response = requests.get(url) response.raise_for_status() return response.text except requests.exceptions.HTTPError as e: if e.response.status_code == 503: print(f"503 error occurred for {url}") # Handle 503 error else: raise

def main(): urls = ["http://example.com/page1", "http://example.com/page2", ...] max_workers = 10 # 控制并发线程数量 with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor: futures = [executor.submit(fetch_url, url) for url in urls] for future in concurrent.futures.as_completed(futures): try: data = future.result() # Process data except Exception as e: print(f"Error: {e}")

if name == "main": main()

(二)设置合理的请求间隔
为了避免因请求频率过快导致的 503 错误,可以在请求之间设置合理的间隔时间。这可以通过在请求代码中添加 time.sleep() 来实现。
``` import time
import requests

def fetch_url(url):
    try:
        response = requests.get(url)
        response.raise_for_status()
        return response.text
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 503:
            print(f"503 error occurred for {url}")
            # Handle 503 error
        else:
            raise

def main():
    urls = ["http://example.com/page1", "http://example.com/page2", ...]
    for url in urls:
        fetch_url(url)
        time.sleep(1)  # 设置请求间隔为 1 秒

if __name__ == "__main__":
    main()

(三)使用代理服务器和用户代理 使用代理服务器可以隐藏爬虫的真实 IP 地址,减少被服务器封禁的风险。同时,代理服务器可以分散请求,降低单个 IP 的请求频率。服务器可能会根据请求的用户代理(User-Agent)来判断请求是否来自爬虫。通过设置随机的用户代理,可以降低被服务器识别为爬虫的风险。

import time
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

# 代理配置
proxyHost = "www.16yun.cn"
proxyPort = "5445"
proxyUser = "16QMSOML"
proxyPass = "280651"

# 用户代理池
user_agents = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36",
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15",
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:88.0) Gecko/20100101 Firefox/88.0"
]

def get_proxy():
    """获取认证代理"""
    return f"http://{proxyUser}:{proxyPass}@{proxyHost}:{proxyPort}"

def create_session():
    """创建带有重试机制的会话"""
    session = requests.Session()
    retry_strategy = Retry(
        total=3,
        backoff_factor=1,
        status_forcelist=[500, 502, 503, 504]
    )
    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("http://", adapter)
    session.mount("https://", adapter)
    return session

def fetch_url(url):
    """获取URL内容"""
    session = create_session()
    proxy = get_proxy()
    headers = {"User-Agent": random.choice(user_agents)}

    try:
        response = session.get(
            url,
            proxies={"http": proxy, "https": proxy},
            headers=headers,
            timeout=10
        )
        response.raise_for_status()
        print(f"成功获取: {url} [状态码: {response.status_code}]")
        return response.text
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 503:
            print(f"503错误: {url} - 服务器暂时不可用")
            # 可以在这里添加重试逻辑或记录到日志
        else:
            print(f"HTTP错误 {e.response.status_code}: {url}")
        raise
    except Exception as e:
        print(f"请求异常: {url} - {str(e)}")
        raise

def main():
    """主函数"""
    urls = [
        "http://example.com/page1",
        "http://example.com/page2",
        "http://example.com/page3"
    ]

    for url in urls:
        try:
            fetch_url(url)
            time.sleep(1)  # 请求间隔
        except Exception as e:
            print(f"处理 {url} 时出错: {e}")
            continue

if __name__ == "__main__":
    import random  # 为user_agents随机选择
    main()

(四)重试机制 当遇到 503 错误时,可以设置重试机制,等待一段时间后再次尝试请求。这可以通过 requests 库的 Session 对象和 Retry 类来实现。

from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

def fetch_url(url):
    session = requests.Session()
    retries = Retry(total=5, backoff_factor=1, status_forcelist=[503])
    session.mount("http://", HTTPAdapter(max_retries=retries))
    try:
        response = session.get(url)
        response.raise_for_status()
        return response.text
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 503:
            print(f"503 error occurred for {url}")
            # Handle 503 error
        else:
            raise

def main():
    urls = ["http://example.com/page1", "http://example.com/page2", ...]
    for url in urls:
        fetch_url(url)

if __name__ == "__main__":
    main()

三、综合实践案例 以下是一个综合运用上述最佳实践的完整代码示例:

import requests
import time
import random
from requests.adapters import HTTPAdapter
from requests.packages.urllib3.util.retry import Retry

user_agents = [
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
    "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36",
    # 添加更多用户代理
]

proxies = ["http://proxy1.example.com:8080", "http://proxy2.example.com:8080", ...]

def fetch_url(url):
    headers = {"User-Agent": random.choice(user_agents)}
    session = requests.Session()
    retries = Retry(total=5, backoff_factor=1, status_forcelist=[503])
    session.mount("http://", HTTPAdapter(max_retries=retries))
    try:
        response = session.get(url, headers=headers, proxies=random.choice(proxies))
        response.raise_for_status()
        return response.text
    except requests.exceptions.HTTPError as e:
        if e.response.status_code == 503:
            print(f"503 error occurred for {url}")
            # Handle 503 error
        else:
            raise

def main():
    urls = ["http://example.com/page1", "http://example.com/page2", ...]
    max_workers = 10  # 控制并发线程数量
    with concurrent.futures.ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = [executor.submit(fetch_url, url) for url in urls]
        for future in concurrent.futures.as_completed(futures):
            try:
                data = future.result()
                # Process data
            except Exception as e:
                print(f"Error: {e}")
            time.sleep(1)  # 设置请求间隔为 1 秒

if __name__ == "__main__":
    main()

四、总结 在 Python 爬虫多线程并发时,503 错误是一个常见的问题。通过合理控制并发线程数量、设置合理的请求间隔、使用代理服务器、添加重试机制和伪装用户代理等方法,可以有效降低 503 错误的发生概率,提高爬虫的稳定性和可靠性。在实际开发中,开发者需要根据目标网站的具体情况,灵活运用这些最佳实践方法,以确保爬虫的高效运行。

点赞
收藏
评论区
推荐文章
Caomeinico Caomeinico
3年前
这些HTTP协议状态码你知道吗?
使用ASP.NET/PHP/JSP或者javascript都会用到http的不同状态,一些常见的状态码为:200–服务器成功返回网页404–请求的网页不存在503–服务不可用1xx(临时响应)表示临时响应并需要请求者继续执行操作的状态代码。代码说明100(继续)请求者应当继续提出请求。服务器返回此代码表示已收到请求的第一部分,正在等
Wesley13 Wesley13
3年前
HTTP状态消息
1xx:信息消息:描述:100Continue服务器仅接收到部分请求,但是一旦服务器并没有拒绝该请求,客户端应该继续发送其余的请求。101SwitchingProtocols服务器转换协议:服务器将遵从客户的请求转换到另外一种协议。103Checkpoint用于PUT或者POST请求恢复失败时
Stella981 Stella981
3年前
Noark入门之线程模型
0x00单线程多进程单线程与单进程多线程的目的都是想尽可能的利用CPU,减少CPU的空闲时间,特别是多核环境,今天咱不做深度解读,跳过...0x01线程池锁最早的一部分游戏服务器是采用线程池的方式来处理玩家的业务请求,以达最大限度的利用多核优势来提高处理业务能力。但线程池同时也带来了并发问题,为了解决同一玩家多个业务请求不被
Stella981 Stella981
3年前
Spring Cloud Sleuth 分布式服务追踪
随着业务的发展,系统规模也会变得越来越大,各微服务间的调用关系也变得越来越错综复杂。通常一个由客户端发起的请求在后端系统中会经过多个不同的微服务调用来协同产生最后的请求结果,在复杂的微服务架构系统中,几乎每一个前端请求都会形成一条复杂的分布式服务调用链路,在每条链路中任何一个依赖服务出现延迟过高或错误的时候都有可能引起请求最后的失败。这时
Wesley13 Wesley13
3年前
HTTP请求错误400、401、402、403、404、405、406、407、412、414、500、501、502解析
HTTP错误400 400请求出错 由于语法格式有误,服务器无法理解此请求。不作修改,客户程序就无法重复此请求。 HTTP错误401 401.1未授权:登录失败 此错误表明传输给服务器的证书与登录服务器所需的证书不匹配。 请与Web服务器的管理员联系,以确认您是否具有访问所请求资源的权限。 40
Stella981 Stella981
3年前
Nginx负载均衡
前面我们说了反向代理,例子中用的是,两个请求请求同一个ip地址和端口,然后Nginx来根据域名调用不同的tomcat来进行请求处理及响应。反向代理主要说的是:不同的请求请求同一个Nginx服务器,Nginx服务器来决定由那个真正的业务服务器(eg:tomcat)处理某个请求。下面说负载均衡负载均衡一般是指,针对同一个(域名的)请求发送很多次,同
3A网络 3A网络
2年前
一文读懂浏览器存储与缓存机制
一文读懂浏览器存储与缓存机制浏览器存储CookieCookie是HTTP协议的一种无状态协议。当请求服务器时,HTTP请求都需要携带Cookie,用来验证用户身份。Cookie由服务端生成,存储在客户端,用来维持状态。通常Cookie由以下值构成:名称(name)值(value)域(Domain)值(value)路径(Path)
负载均衡详解
负载均衡(LoadBalancing)是一种将网络流量或请求分发到多个服务器或资源的技术,以确保这些服务器或资源能够更均匀地处理负载,提高系统的性能、可用性和可伸缩性。负载均衡通常应用于Web服务、应用程序服务器、数据库服务器以及其他网络应用中。
小白学大数据 小白学大数据
11个月前
错误处理在网络爬虫开发中的重要性:Perl示例 引言
错误处理的必要性在网络爬虫的开发过程中,可能会遇到多种错误,包括但不限于:网络连接问题服务器错误(如404或500错误)目标网站结构变化超时问题权限问题错误处理机制可以确保在遇到这些问题时,爬虫能够优雅地处理异常情况,记录错误信息,并在可能的情况下恢复执行
小白学大数据 小白学大数据
3星期前
如何通过requests和time模块限制爬虫请求速率?
一、为什么需要限制爬虫请求速率?在爬取网站数据时,网站服务器需要处理我们的请求并返回数据。如果爬虫的请求过于频繁,服务器可能会因为负载过高而崩溃,或者将我们的爬虫IP地址封禁。此外,许多网站都有反爬虫机制,例如检测单位时间内来自同一IP的请求频率。如果请求
小白学大数据
小白学大数据
Lv1
男 · 亿牛云 · python技术
宁为代码类弯腰,不为bug点提交!
文章
111
粉丝
5
获赞
18