爬虫代码重构:从脚本到可维护项目

很多开发者的爬虫生涯,都是从一段几十行的脚本 开始的。能跑就行、逻辑直出、一把梭哈,快速拿到数据是第一目标。但随着需求迭代:网站改版、反爬升级、字段增多、多站点复用、定时调度、日志排查...... 原本顺滑的小脚本,很快会变成难读、难改、难扩展的 "屎山"。

真正的生产环境,需要的不是 "能用",而是可维护、可扩展、可监控 的爬虫项目。本文就带你完成一次关键蜕变:从一次性脚本,到工程化、可维护的爬虫项目

一、先认清:脚本式爬虫的致命问题

一段典型的 "脚本爬虫" 长这样:

  • 所有代码塞在一个 .py 文件里
  • 请求、解析、存储、逻辑硬编码混在一起
  • 配置写死在代码中,改个 URL 要翻遍全文
  • 无异常处理,一报错直接崩,不知道死在哪
  • 无日志、无重试、无去重,数据质量不可控
  • 换个网站 / 换个页面,只能复制粘贴改代码

这种写法在小需求、临时任务里没问题,但一旦进入长期维护,成本会指数级上升。

二、重构目标:可维护项目的核心标准

重构不是 "重写",而是让代码变得更好。一个合格的可维护爬虫项目,应满足:

  1. 结构清晰:职责分离,一眼看懂流程
  2. 配置与代码分离:不改代码就能适配站点变化
  3. 健壮稳定:自动重试、异常捕获、降级处理
  4. 易于扩展:新增站点 / 字段只加代码不改旧逻辑
  5. 可观测:日志清晰、进度可见、方便排查
  6. 可测试:核心逻辑可单独运行验证

三、实战重构:从脚本到项目的标准路径

我们以最常见的 Python requests + parsel/lxml 为例,一步步拆解。

1. 项目结构规范化

抛弃单文件,建立标准目录结构:

plaintext

复制代码
spider_project/
├── config/           # 配置文件
│   ├── settings.py   # 全局配置:请求头、超时、重试、路径
│   └── sites.py      # 各站点规则配置:URL、XPath/选择器
├── core/             # 核心通用模块
│   ├── http.py       # 请求封装:重试、代理、请求头
│   ├── parser.py     # 解析封装:通用提取方法
│   └── storage.py    # 存储封装:MySQL/CSV/JSON/ES
├── spiders/          # 具体爬虫
│   ├── __init__.py
│   ├── blog_spider.py
│   └── product_spider.py
├── utils/            # 工具类
│   ├── logger.py     # 日志统一配置
│   ├── time_util.py  # 时间处理
│   └── validate.py   # 数据校验
├── main.py           # 项目入口
└── requirements.txt  # 依赖清单

好处:新人接手也能快速定位功能,不用在几千行里大海捞针。

2. 配置与代码解耦

把所有可变内容抽到配置里:

  • URL、列表页 / 详情页规则
  • 请求头、Cookie、超时时间
  • 数据库连接信息
  • 重试次数、并发数、间隔时间

python

运行

复制代码
# config/settings.py
BASE_HEADERS = {
    "User-Agent": "Mozilla/5.0 ...",
    "Accept-Language": "zh-CN,zh;q=0.9"
}
MAX_RETRY = 3
TIMEOUT = 10

python

运行

复制代码
# config/sites.py
BLOG_SITE = {
    "list_url": "https://xxx.com/page/{}",
    "total_page": 10,
    "title_xpath": "//h1/text()",
    "content_xpath": "//div[@class='content']//text()"
}

以后网站改了 XPath,只改配置,不动逻辑代码

3. 封装通用请求模块(核心!)

脚本里最常见的就是满屏重复的 requests.get()。统一封装 HTTP 请求,内置:

  • 自动重试
  • 随机请求头
  • 代理切换
  • 状态码判断
  • 统一异常捕获

python

运行

复制代码
# core/http.py
import requests
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry

def get_session():
    session = requests.Session()
    retry = Retry(total=3, backoff_factor=1)
    adapter = HTTPAdapter(max_retries=retry)
    session.mount("https://", adapter)
    session.mount("http://", adapter)
    return session

def fetch(url, headers=None):
    # 统一请求逻辑
    ...

所有爬虫共用这一套,改一次,全项目生效

4. 抽取通用解析函数

把 XPath/CSS 提取、文本清洗、去空格、去换行、时间格式化,抽成通用方法:

python

运行

复制代码
# core/parser.py
def extract_text(selector, xpath):
    result = selector.xpath(xpath).extract_first()
    return result.strip() if result else ""

爬虫里只写:

python

运行

复制代码
title = extract_text(selector, site_config["title_xpath"])

解析规则统一,避免每个人写得五花八门。

5. 统一存储层

不要在爬虫里直接写 INSERT INTO。封装存储接口,支持切换存储介质:

  • CSV/JSON(本地测试)
  • MySQL/PostgreSQL(生产)
  • Elasticsearch(搜索)
  • Kafka/MQ(数据流)

python

运行

复制代码
# core/storage.py
def save_item(item):
    # 统一去重、校验、入库
    ...

以后换存储引擎,爬虫代码完全不用改

6. 标准化日志系统

用日志代替 print(),记录:

  • 当前爬取 URL
  • 响应状态
  • 解析失败字段
  • 异常信息
  • 爬取速度、总量

python

运行

复制代码
# utils/logger.py
import logging
def get_logger(name):
    # 按文件/控制台输出,按级别切割
    ...

线上出问题,看日志就能定位,不用复现现场。

7. 爬虫业务只写 "业务"

经过封装,最终爬虫文件会极度干净:

python

运行

复制代码
# spiders/blog_spider.py
from config.sites import BLOG_SITE
from core.http import fetch
from core.parser import extract_text
from core.storage import save_item
from utils.logger import get_logger

logger = get_logger(__name__)

def run():
    for page in range(1, BLOG_SITE["total_page"] + 1):
        url = BLOG_SITE["list_url"].format(page)
        resp = fetch(url)
        # 解析
        items = parse_list(resp.text)
        for item in items:
            save_item(item)
        logger.info(f"页面 {page} 完成")

def parse_list(html):
    # 只写当前页面专属解析逻辑
    ...

只关注业务,不关注底层细节

8. 统一入口与可执行性

提供一个干净的入口:

python

运行

复制代码
# main.py
from spiders.blog_spider import run as run_blog

if __name__ == "__main__":
    run_blog()

配合 argparse,可以支持:

plaintext

复制代码
python main.py --spider blog --page 5

四、重构后的进阶能力

当你把爬虫改成项目结构,很多能力会自然解锁:

  1. 单元测试:请求、解析、存储可单独测试
  2. 并发 / 异步改造:只改 core/http.py,不影响业务
  3. 接入调度:配合 Celery/Apscheduler 定时任务
  4. 监控告警:统一统计失败率、响应时间
  5. 多环境切换:开发 / 测试 / 生产配置分离
  6. 代码评审与协作:多人开发不互相覆盖

五、什么时候该重构?

满足任意一条,就别犹豫了:

  • 单个爬虫文件超过 300 行
  • 修改一个字段要改 N 个地方
  • 报错要花半小时定位
  • 不敢删代码,怕崩
  • 新同事看不懂你的逻辑

能跑就行 ≠ 可以上线

六、总结

从脚本到可维护项目,本质是三件事:

  1. 职责分离:请求、解析、存储、日志互不干扰
  2. 通用下沉:重复代码封装成底层模块
  3. 可变上浮:把配置抽离,不写死在逻辑里

重构不会让爬虫 "跑得更快",但会让你维护更轻松、排错更快速、扩展更无痛

如果你是从 "脚本小子" 走向工程化的开发者,这套思路,适用于:普通 requests 爬虫、Playwright 爬虫、Scrapy 项目、甚至分布式爬虫架构。

先写对,再写快;先能用,再好用;先可维护,再谈高性能。

相关推荐
隔窗听雨眠9 小时前
大模型加爬虫上篇:技术融合与架构革新
爬虫·架构
Super Scraper13 小时前
如何批量抓取 TikTok 数据而不被封锁?完整指南
爬虫·ai·自动化·抖音·tiktok·ai agent
深蓝电商API14 小时前
自动化录屏 + 截图:打造爬虫调试的上帝视角
爬虫
tang7778914 小时前
市场调研自动化采集架构:基于住宅IP轮换的APP数据抓取与反风控方案
爬虫·动态代理ip·爬虫代理ip·爬虫动态ip·住宅代理ip·动态住宅ip
数据知道14 小时前
指纹浏览器环境的导入、导出、快照与云端同步机制
爬虫·数据采集·指纹浏览器
星川皆无恙15 小时前
大数据k-means聚类算法:基于k-means聚类算法+NLP微博舆情数据爬虫可视化分析推荐系统(新版)
大数据·人工智能·爬虫·算法·机器学习·自然语言处理·kmeans
小二·16 小时前
Rust 爬虫与数据处理实战:大规模并发抓取 + 流式处理
开发语言·爬虫·rust
在放️1 天前
Python 爬虫 · 第三方代理接入与合规使用
开发语言·爬虫·python
隔窗听雨眠1 天前
大模型加爬虫中篇:工程实践与应用场景
爬虫