You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多进程并发爬取遇urllib.error.URLError域名解析失败问题求助

解决urllib.request.urlopen并发请求导致的DNS解析失败问题

我之前也踩过类似的坑——当一次性发起几百个并发请求时,系统的DNS解析能力很容易被耗尽,再加上urllib默认的连接管理在多进程环境下的小缺陷,就会出现你碰到的urllib.error.URLError: urlopen error Temporary failure in name resolution错误。结合你的场景,给你几个实用的解决思路:

1. 显式管理HTTP连接,避免资源泄漏

urllib的urlopen返回的响应对象如果不手动关闭,会让连接长时间处于TIME_WAIT状态,占用系统资源,间接拖慢DNS解析效率。用with语句可以自动帮你关闭连接,这是最基础的优化:

from urllib.request import urlopen

def fetch_single_url(url):
    try:
        # 用with上下文管理器自动关闭连接
        with urlopen(url, timeout=10) as response:
            return response.read()
    except Exception as e:
        print(f"请求 {url} 失败: {str(e)}")
        return None

2. 降低并发数,不要一次性拉满500进程

系统的文件描述符、DNS查询配额都是有限的,500个并发请求远超大多数系统的默认承载能力。把multiprocessing Pool的进程数调低到合理范围(比如20-50,具体可以根据你的系统配置调整):

from multiprocessing import Pool

if __name__ == "__main__":
    target_urls = [...]  # 你的URL列表
    # 调整进程池大小,比如30
    with Pool(processes=30) as pool:
        results = pool.map(fetch_single_url, target_urls)

这样可以减少同时发起的DNS查询和连接请求,直接缓解系统压力。

3. 复用DNS解析结果,减少重复查询

多进程环境下,每个进程都会独立做DNS解析,导致大量重复查询浪费资源。你可以自己实现一个DNS缓存,比如用lru_cache在进程内缓存域名解析结果:

from urllib.request import urlopen
from urllib.parse import urlparse, urlunparse
from functools import lru_cache
import socket

# 缓存域名解析结果,maxsize可以根据你的域名数量调整
@lru_cache(maxsize=1000)
def resolve_domain(domain):
    return socket.gethostbyname(domain)

def fetch_single_url(url):
    try:
        parsed = urlparse(url)
        # 先从缓存获取解析后的IP,没有再去查询
        domain_ip = resolve_domain(parsed.netloc)
        # 把URL替换成IP直接访问,避免重复解析
        ip_based_url = urlunparse(parsed._replace(netloc=domain_ip))
        
        with urlopen(ip_based_url, timeout=10) as response:
            return response.read()
    except Exception as e:
        print(f"请求 {url} 失败: {str(e)}")
        return None

同一个域名只会被解析一次,大大减少DNS查询的次数。

4. 增加重试机制,应对临时解析失败

这个错误本身是“临时”的,所以可以给请求加上重试逻辑,遇到解析失败时等待几秒再重试:

from urllib.request import urlopen
import urllib.error
import time

def fetch_single_url(url, max_retries=3):
    for attempt in range(max_retries):
        try:
            with urlopen(url, timeout=10) as response:
                return response.read()
        except urllib.error.URLError as e:
            if "Temporary failure in name resolution" in str(e) and attempt < max_retries - 1:
                print(f"请求 {url} 解析失败,第 {attempt+1} 次重试...")
                time.sleep(1)  # 等待1秒后重试
                continue
            print(f"请求 {url} 最终失败: {str(e)}")
            return None

5. 系统层面优化DNS配置(可选)

如果上面的方法还不够,你可以考虑在系统层面优化:

  • Linux系统可以安装dnsmasq作为本地DNS缓存服务,减少对外部DNS服务器的请求;
  • 调整/etc/resolv.conf中的DNS服务器,换成更稳定的公共DNS(比如1.1.1.1或8.8.8.8);
  • 增大系统的DNS缓存TTL值,延长解析结果的缓存时间。

先从前面几个代码层面的优化开始尝试,尤其是显式关闭连接和降低并发数,这两个改动最小但效果最明显。

内容的提问来源于stack exchange,提问作者softmarshmallow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:00:48