云计算百科
云计算领域专业知识百科平台

05-Python爬虫工程化完善

前面4篇学会了发请求、提数据、抓表格、存CSV。能跑通一个小爬虫了,但跑多了就会发现:

  • 跑了半小时,最后一页网络超时崩了,前面数据全白干
  • 请求太快被网站封了 IP
  • 页面结构偶尔变一下,程序直接报错退出
  • 网络不稳定,程序卡在那里一动不动

今天这篇是数据采集部分的收尾——把前面的知识串起来,加上几个关键的"防护盾",让你的爬虫遇到问题不崩溃、失败之后能重试、被封之前懂克制。

今天教五招,全是干货。


第一招:超时设置 timeout

爬虫跑着跑着突然不动了,也不报错,大概率是网络问题——请求发出去了,但服务器一直不响应。requests 默认没有超时限制,会一直等。

给它设一个"耐心上限":

import requests

# 最多等10秒,超过就报错
response = requests.get('https://example.com', timeout=10)

超时还能分开设置:连接超时和读取超时。

# 连接超时5秒,读取超时10秒
response = requests.get('https://example.com', timeout=(5, 10))

新手不用纠结太细,统一 10 秒基本够用。


第二招:异常捕获 try-except

光设超时还不够——超时了程序会报错退出。我们希望的是:这一页失败了没关系,记下来,继续爬下一页,别整个程序崩了。

最常见的几种异常:

异常类型什么时候触发
requests.exceptions.Timeout 请求超时
requests.exceptions.ConnectionError 连不上服务器
requests.exceptions.RequestException 所有请求异常的老祖宗(兜底用)

import requests

try:
response = requests.get(url, timeout=10)
response.raise_for_status() # 状态码不是200也会报错
print("请求成功")
except requests.exceptions.Timeout:
print(f"请求超时:{url}")
except requests.exceptions.ConnectionError:
print(f"连接失败:{url}")
except requests.exceptions.RequestException as e:
print(f"请求出错:{url},错误信息:{e}")

注意:response.raise_for_status() 很重要。它检查 HTTP 状态码,404、500 这种错误也会抛出异常。不然你拿到一个 404 页面还以为成功了。


第三招:请求休眠 time.sleep

爬虫爬太快,服务器压力大;而且请求太快容易被识别成爬虫、封 IP。加个休眠,既是礼貌,也是自保。

import requests
import time

for url in urls:
try:
response = requests.get(url, timeout=10)
except Exception as e:
print(f"爬取失败:{url},{e}")

# 每次请求后休眠1秒
time.sleep(1)

进阶:随机休眠更像真人。 固定时间也容易被识别——哪有真人每次都精确等 1 秒?

import random
import time

# 随机休眠1到3秒
time.sleep(random.uniform(1, 3))


第四招:User-Agent 伪装 + 请求头完善

很多网站检查请求头,发现你是 python-requests 直接就拒了。

headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Referer': 'https://example.com/', # 从哪个页面点过来的
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'zh-CN,zh;q=0.9',
}

response = requests.get(url, headers=headers, timeout=10)

记住一个原则:伪装得越像浏览器,越不容易被封。


第五招:重试机制

有时候失败只是暂时的——网络抖一下、服务器刚好忙不过来,重试一下说不定就成了。

import requests
import time

def fetch_url(url, headers, max_retries=3):
"""
带重试的请求函数
max_retries: 最多重试次数
"""

for attempt in range(1, max_retries + 1):
try:
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
return response # 成功就返回
except requests.exceptions.RequestException as e:
print(f"第 {attempt} 次请求失败:{e}")
if attempt < max_retries:
# 重试间隔递增:第1次等2秒,第2次等4秒
wait_time = attempt * 2
print(f"等待 {wait_time} 秒后重试…")
time.sleep(wait_time)
else:
print(f"已重试 {max_retries} 次,最终失败")
return None

三个设计细节:

  • 最多重试 3 次:不能无限重试,不然死循环
  • 间隔递增:越失败越有耐心,指数退避的简化版
  • 失败返回 None:让调用方决定怎么处理失败

  • 五招合体:完整健壮爬虫模板

    五招都教完了,把它们全部整合成一个可以直接拿去改的完整模板:

    import requests
    import time
    import random
    import csv
    from bs4 import BeautifulSoup

    # ========== 配置区 ==========
    BASE_URL = 'https://example.com/list?page=' # 目标网站基础URL
    TOTAL_PAGES = 10 # 总页数
    OUTPUT_FILE = 'data.csv' # 输出文件名

    HEADERS = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
    'Referer': 'https://example.com/',
    }

    MAX_RETRIES = 3 # 最大重试次数
    SLEEP_MIN = 1 # 最短休眠时间(秒)
    SLEEP_MAX = 3 # 最长休眠时间(秒)
    TIMEOUT = 10 # 请求超时时间(秒)

    # ========== 核心函数 ==========
    def fetch_page(url):
    """带重试的页面请求函数"""
    for attempt in range(1, MAX_RETRIES + 1):
    try:
    response = requests.get(url, headers=HEADERS, timeout=TIMEOUT)
    response.raise_for_status()
    response.encoding = 'utf-8'
    return response
    except requests.exceptions.Timeout:
    print(f"第{attempt}次请求超时:{url}")
    except requests.exceptions.ConnectionError:
    print(f"第{attempt}次连接失败:{url}")
    except requests.exceptions.RequestException as e:
    print(f"第{attempt}次请求异常:{e}")

    if attempt < MAX_RETRIES:
    time.sleep(attempt * 2) # 重试间隔递增
    else:
    print(f"{url} 重试{MAX_RETRIES}次后仍失败,跳过")
    return None

    def parse_data(html_text):
    """解析页面提取数据(根据实际网站结构修改)"""
    soup = BeautifulSoup(html_text, 'html.parser')
    items = []
    for item in soup.select('.item'):
    items.append({
    '标题': item.select_one('.title').get_text(strip=True),
    '日期': item.select_one('.date').get_text(strip=True),
    })
    return items

    def save_to_csv(data_list, file_name, mode='a'):
    """保存数据到CSV"""
    if not data_list:
    return
    fieldnames = data_list[0].keys()
    with open(file_name, mode, newline='', encoding='utf-8-sig') as f:
    writer = csv.DictWriter(f, fieldnames=fieldnames)
    if mode == 'w':
    writer.writeheader()
    writer.writerows(data_list)

    # ========== 主程序 ==========
    def main():
    print("爬虫开始运行")
    all_data = []
    failed_pages = [] # 记录失败的页面

    for page in range(1, TOTAL_PAGES + 1):
    url = BASE_URL + str(page)
    print(f"\\n正在爬取第 {page} 页:{url}")

    response = fetch_page(url)
    if response is None:
    failed_pages.append(page)
    time.sleep(random.uniform(SLEEP_MIN, SLEEP_MAX))
    continue

    try:
    items = parse_data(response.text)
    all_data.extend(items)
    print(f" 提取到 {len(items)} 条数据")
    except Exception as e:
    print(f" 解析失败:{e}")
    failed_pages.append(page)

    if page < TOTAL_PAGES:
    time.sleep(random.uniform(SLEEP_MIN, SLEEP_MAX))

    # 收尾
    print(f"\\n爬取完成!成功 {TOTAL_PAGES – len(failed_pages)} 页,失败 {len(failed_pages)} 页")
    print(f"共获取 {len(all_data)} 条数据")

    if all_data:
    save_to_csv(all_data, OUTPUT_FILE, mode='w')
    print(f"数据已保存到 {OUTPUT_FILE}")
    else:
    print("没有获取到任何数据")

    if __name__ == '__main__':
    main()

    模板的特点:

    • ✅ 配置区和代码区分开,改参数不用翻代码
    • ✅ 每个函数只干一件事,清晰好维护
    • ✅ 失败的页面会记录,方便补爬
    • ✅ 有进度提示,运行过程不黑盒
    • ✅ 最后有统计汇总

    以后要爬别的网站,改改配置区和 parse_data 函数就行。


    新手常见错误(避坑)

    坑反面教材正确做法
    try-except 写太宽 except: pass 只捕获预期异常,异常信息打印出来
    休眠太短/太长 0.1秒=没加;10秒=太慢 小网站2-3秒,大网站1秒左右
    重试没有次数限制 while True 设最大重试次数,3次够了
    所有页面同一个UA 太明显 量大时准备UA列表随机选

    # UA列表随机选
    USER_AGENTS = [
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/120.0.0.0',
    'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 Version/17.0',
    'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
    ]
    headers = {'User-Agent': random.choice(USER_AGENTS)}


    真实运行效果

    五招合体的完整模板,用本地模拟数据跑一遍(模拟第 2 页第一次请求超时、重试后成功):

    可以看到:第 2 页模拟了一次"超时→自动重试→成功"的完整过程,失败的页面被记录,最终成功页数、失败页数、总数据量一目了然,数据正常写入 CSV。这就是工程化爬虫该有的样子——出问题不崩、失败能重试、收尾有统计。


    总结

    到这里,数据采集部分的5篇就全部学完了:

    • 第1篇:requests 发请求
    • 第2篇:BeautifulSoup 提取数据
    • 第3篇:表格数据抓取
    • 第4篇:数据存 CSV + 翻页
    • 第5篇(本篇):工程化完善,让爬虫"扛造"

    5篇下来,你已经能独立写出一个健壮、不容易崩、懂礼貌的小爬虫了。这比网上那种"三行代码爬XX"的玩具爬虫实用多了。

    完整可跑工程版(健壮爬虫模板 robust_spider.py)在配套资源包里。

    接下来进入 Pandas 的世界:数据怎么清洗、怎么分析、怎么发现价值。


    梅雅达编程工作室 · Python数据实战系列第5篇
    五招下来,爬虫从"能跑"变成"耐造"了。数据采集收尾,下一篇进 Pandas 的世界。

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 05-Python爬虫工程化完善
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!