写好 README 和注释:让你的爬虫代码不再是一次性的
很多开发者写爬虫时都有过类似经历:花半天时间调通请求、破解反爬、写完解析逻辑,跑通导出数据后就把代码丢在一边。等到几个月后目标网站改版、需要新增字段或者换环境重跑时,打开项目却一脸茫然 —— 入口文件是哪个?配置里的加密参数是干嘛的?这段奇奇怪怪的兼容逻辑当初是为了绕开什么坑?最终只能推倒重写,代码彻底沦为 “一次性脚本”。
爬虫项目天然依赖外部网站结构、反爬策略和运行环境,可维护性本就弱于普通业务代码。而一份规范的 README 和高质量的代码注释,正是破解 “一次性爬虫” 的核心手段。前期花 10% 的时间做好文档,后期能节省 90% 的维护成本。
一、为什么爬虫代码特别容易变成 “一次性资产”?
相比常规后端或前端项目,爬虫的可维护性天生更差,核心有三点原因:
- 强外部依赖:爬取逻辑完全绑定目标网站的 DOM 结构、接口参数和反爬规则,网站一次改版就能让整套解析逻辑失效,没有文档的话连改哪里都找不到。
- 环境碎片化:从请求库、解析库到浏览器驱动、验证码识别工具、代理服务,依赖零散且版本敏感,换台机器经常报一堆错,却没人知道该装什么版本。
- 重实现轻规范:多数爬虫以 “跑通拿到数据” 为目标,开发时优先堆功能,注释和文档全靠 “脑子记”,但爬虫里的反爬 trick、参数加密、异常兼容都是强经验属性,时间一长必然遗忘。
本质上,缺少标准化文档的爬虫,不是可复用的项目,只是一次性的临时脚本。
二、一份合格的爬虫 README,该写些什么?
README 是项目的 “使用说明书”,对爬虫项目而言,它的核心目标是:让一个完全没接触过这个项目的人,照着文档就能把项目跑起来、改得动、出了问题能排查。
一份完整的爬虫 README,建议包含以下 8 个模块:
1. 项目概述与适用范围
开门见山说明项目价值和边界,避免后续维护者搞错用途。
- 明确爬取的目标站点、数据类型(商品信息、新闻、评论等)和业务用途
- 注明适配的页面版本 / 上线时间,提醒 “目标网站改版后可能失效”
- 说明不支持的场景,比如 “仅适配 PC 端网页,不包含 APP 接口”
2. 环境与依赖说明
这是爬虫项目最容易踩坑的部分,必须写细。
- 运行环境要求:Python 版本、操作系统兼容性、是否需要图形界面(Selenium 类爬虫)
- 第三方依赖:列出核心库及推荐版本,同时提供
requirements.txt,注明 “建议使用虚拟环境安装” - 特殊依赖说明:ChromeDriver/GeckoDriver 的对应版本、OCR 工具、验证码识别服务、代理客户端等非 pip 安装的依赖
- 权限要求:是否需要配置 Hosts、是否需要企业内网环境等
3. 快速开始:从 0 到运行的完整步骤
给出可直接复制执行的命令,降低上手门槛。
- 安装依赖的完整命令
- 配置文件的复制与修改步骤(比如复制
config.example.py为config.py) - 启动命令与入口文件,附带最简运行示例
- 首次运行的预期输出,说明怎么判断跑成功了
4. 配置项详解
爬虫的可配置项非常多,只放代码里别人根本不知道每个参数是干嘛的。
- 请求配置:并发数、请求间隔、超时重试次数、UA 轮换规则、请求头默认值
- 身份配置:Cookie/Token 的获取方式、有效期、更新入口;账号密码的配置位置
- 代理配置:代理池地址、代理格式、失败重试策略、是否支持断点续爬
- 存储配置:输出文件路径 / 格式、数据库连接信息、数据表字段说明
5. 爬取逻辑与数据说明
让维护者不用读完整套代码,也能知道数据是怎么来的。
- 整体爬取流程:例如 “分类列表页→商品详情页→评论分页→数据清洗→写入 MySQL”
- 核心字段说明:列出主要输出字段的含义、来源页面、是否做了清洗转换
- 爬取量级与耗时参考:比如 “全量爬取约 10 万条数据,单 IP 耗时约 8 小时”
6. 反爬与异常处理说明
爬虫里最有价值的经验,往往都在反爬和异常处理里。
- 已适配的反爬措施:UA 轮换、IP 代理池、滑块验证绕过、请求签名生成、无头浏览器指纹伪装等
- 常见异常触发场景:IP 封禁、验证码弹出、页面结构变化、接口限流
- 降级与兜底逻辑:比如 “详情页请求失败 3 次后跳过,记录到失败日志”
- 断点续爬的实现方式:是否支持中断后从上次位置继续
7. 维护记录与已知问题
让后续维护者知道项目的迭代历史和遗留坑点。
- 版本更新日志:每次改版对应的修改点、适配的网站版本
- 已知问题:比如 “部分冷门分类页结构不一致,暂未适配”
- 待优化项:后续可以改进的方向
8. 常见问题(FAQ)
把踩过的坑沉淀下来,避免后人重复踩。
- 运行报错的高频排查步骤
- 爬取数据为空的定位方法
- 触发反爬后的应急处理方案
三、代码注释:写在点子上,而非写满屏
好的注释不是越多越好,而是在关键位置说清 “为什么这么写”。爬虫代码有大量业务特有的逻辑和踩坑经验,这些才是注释的核心价值。
3.1 三层注释体系,覆盖不同粒度
1. 文件级头部注释
每个爬虫脚本的开头,都应该有一段总览注释,说明这个文件的核心作用。
python
运行
# -*- coding: utf-8 -*- """ 商品详情页爬虫脚本 功能:根据商品ID列表,批量爬取商品基础信息与价格数据 创建时间:2026-01-15 最后更新:2026-05-20 适配页面:xxx商城PC端商品详情页 v202604版 依赖:requests 2.31.0, lxml 4.9.3 注意:商品价格接口需携带sign签名,签名规则见函数get_request_sign """2. 函数 / 类级注释(Docstring)
核心函数必须写清功能、入参、返回值和注意事项,推荐遵循 Google 风格或 NumPy 风格。
python
运行
def parse_goods_detail(html_text): """解析商品详情页HTML,提取结构化数据 Args: html_text: 商品详情页的HTML文本字符串 Returns: dict: 包含商品ID、标题、价格、库存等字段的字典 字段解析失败时对应值为None,不会抛出异常 Notes: 价格字段位于script标签内的JSON数据中,不在DOM节点直接展示 对应页面class: .goods-info 模块 """3. 行内关键逻辑注释
只注释 “代码本身读不出来的信息”,解释原因而非复述代码。
- 错误示范:
# 发送get请求(废话,看代码就知道) - 正确示范:
# 增加2秒随机延时,绕过列表页频率检测
3.2 爬虫代码里必须加注释的 7 个位置
- 加密 / 签名参数生成逻辑:说明参数来源、加密算法、对应网站的校验规则,这是爬虫里最容易忘的部分。
- DOM 解析规则:标注对应的页面区域和节点特征,网站改版时能快速定位修改位置。
- 反爬绕过逻辑:说明这段代码是为了绕过什么检测,比如 “添加 navigator.webdriver 覆盖,绕过无头浏览器识别”。
- 异常捕获分支:说明为什么捕获这个异常,对应的业务场景是什么。
- 数据清洗规则:解释字段的处理逻辑,比如 “去除价格中的 ¥ 符号和千分位逗号,统一转为单位为元的浮点数”。
- 限速 / 延时逻辑:说明限速的依据,比如 “详情页限速 1 秒 / 次,实测该阈值下不会触发验证码”。
- 临时兼容逻辑:标注临时方案的原因和移除条件,比如 “临时兼容旧版页面结构,2026 年 6 月全站改版后可删除”。
3.3 避开这些注释误区
- 废话注释:对一眼就能看懂的代码做字面解释,纯属于冗余。
- 留存废弃代码:不要用注释把不用的代码包起来留着,版本管理工具会帮你记录历史。
- 只写实现,不写原因:爬虫里很多写法是踩坑踩出来的,不写原因下次自己都不敢改。
- 代码更新,注释不动:修改逻辑后忘记同步注释,错误的注释比没有注释更害人。
四、实战:规范的爬虫代码与 README 模板
4.1 带规范注释的爬虫示例
python
运行
# -*- coding: utf-8 -*- """ xxx商城商品列表爬虫 功能:按分类爬取商品基础信息,输出为CSV文件 创建时间:2026-03-10 最后更新:2026-06-18 适配站点:xxx商城PC端分类列表页 注意:分类接口携带加密参数timestamp+sign,签名规则为MD5(timestamp+secret_key) """ import time import hashlib import requests from lxml import etree import csv # 配置区 BASE_URL = "https://example.com/api/goods/list" SECRET_KEY = "your_secret_key_here" REQUEST_INTERVAL = 2 # 请求间隔2秒,实测为不触发限流的临界值 DEFAULT_HEADERS = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...", "Referer": "https://example.com/category" # 必须携带,否则接口返回403 } def generate_sign(timestamp): """生成接口请求签名 签名规则:将时间戳与固定密钥拼接后做MD5加密,对应后端校验逻辑 Args: timestamp: 10位时间戳字符串 Returns: str: 32位小写MD5签名值 """ raw = f"{timestamp}{SECRET_KEY}" return hashlib.md5(raw.encode("utf-8")).hexdigest() def fetch_goods_list(category_id, page): """获取指定分类、指定页码的商品列表数据 Args: category_id: 分类ID page: 页码,从1开始 Returns: list: 商品字典列表,请求失败返回空列表 """ timestamp = str(int(time.time())) params = { "category_id": category_id, "page": page, "page_size": 20, "timestamp": timestamp, "sign": generate_sign(timestamp) } try: resp = requests.get(BASE_URL, params=params, headers=DEFAULT_HEADERS, timeout=10) resp.raise_for_status() data = resp.json() return data.get("data", {}).get("list", []) except requests.exceptions.RequestException as e: # 网络异常或接口报错时返回空,不中断整体爬取,失败信息可在日志中排查 print(f"第{page}页请求失败: {e}") return [] def save_to_csv(goods_list, output_path): """将商品数据写入CSV文件 Args: goods_list: 商品数据列表 output_path: 输出文件路径 """ fields = ["goods_id", "title", "price", "sales", "shop_name"] with open(output_path, "w", newline="", encoding="utf-8-sig") as f: writer = csv.DictWriter(f, fieldnames=fields) writer.writeheader() writer.writerows(goods_list) def main(category_id, max_page, output_path): """主爬取流程:循环翻页爬取,最终保存为CSV Args: category_id: 目标分类ID max_page: 最大爬取页数 output_path: 结果输出路径 """ all_goods = [] for page in range(1, max_page + 1): print(f"正在爬取第 {page}/{max_page} 页") goods = fetch_goods_list(category_id, page) if not goods: break all_goods.extend(goods) time.sleep(REQUEST_INTERVAL) save_to_csv(all_goods, output_path) print(f"爬取完成,共获取 {len(all_goods)} 条商品数据") if __name__ == "__main__": main(category_id=1001, max_page=10, output_path="./goods_result.csv")4.2 通用爬虫 README 模板
markdown
# 项目名称:xxx商城商品爬虫 ## 项目概述 - 目标站点:xxx商城 PC 端 - 爬取内容:分类下商品基础信息(ID、标题、价格、销量、店铺名) - 业务用途:竞品价格监控数据采集 - 适配版本:2026年4月页面改版后接口 ## 环境依赖 - Python 3.9+ - 核心依赖: - requests==2.31.0 - lxml==4.9.3 - 安装命令: ```bash pip install -r requirements.txt快速开始
- 复制
config.example.py为config.py,填入你的SECRET_KEY - 运行入口脚本:
bash
运行
python goods_spider.py - 结果默认输出到
./output/goods_result.csv
配置说明
表格
| 配置项 | 说明 | 默认值 |
|---|---|---|
| SECRET_KEY | 接口签名密钥 | 无,需自行配置 |
| REQUEST_INTERVAL | 单页请求间隔(秒) | 2 |
| MAX_PAGE | 最大爬取页数 | 10 |
| OUTPUT_PATH | 结果输出路径 | ./output/ |
爬取逻辑
- 按分类 ID 调用商品列表接口,分页循环爬取
- 接口请求携带 timestamp + sign 签名,签名规则为 MD5 (timestamp + secret_key)
- 单页请求失败自动跳过,不中断整体任务
- 爬取完成后统一写入 CSV 文件
反爬说明
- 已适配:请求头 Referer 校验、接口签名校验、请求频率限制
- 注意事项:
- 请求间隔低于 1 秒大概率触发 IP 限流
- 单日单 IP 爬取量建议不超过 5 万条,否则可能触发验证码
维护记录
- v1.2 2026-06-18:适配新版接口签名规则
- v1.1 2026-04-25:新增失败重试机制,优化输出字段
- v1.0 2026-03-10:初始版本,支持基础商品列表爬取
常见问题
- 接口返回 403:检查 Referer 是否正确,SECRET_KEY 是否过期
- 输出数据为空:确认分类 ID 是否有效,页面是否改版
- 频繁请求失败:调大 REQUEST_INTERVAL,或配置代理 IP
五、让文档持续生效的几个习惯 文档和注释不是写完就一劳永逸,需要跟着项目一起迭代。 1. **代码变更,文档同步**:改了解析规则、换了签名算法、加了新配置项,顺手更新README和对应注释。 2. **踩过的坑,立刻沉淀**:每次排查完问题,把原因和解决方案加到FAQ里,下次直接复用。 3. **日志与注释互补**:运行时的状态、错误信息用日志输出,设计层面的逻辑、踩坑经验用注释说明。 4. **模块化拆分配合文档**:把请求、解析、存储、反爬拆分成独立模块,每个模块单独维护注释,整体结构更清晰。 爬虫从来都不是一锤子买卖的“跑通就行”。一份清晰的README和精准的注释,本质上是把你的开发经验沉淀成了可复用的资产。当下次再打开这个项目时,你不用再对着一堆裸代码逆向推理自己当初的思路,也不用再因为一点点改动就推翻重写——这才是工程化写爬虫的意义。