直接写函数的爬虫难维护是因为每次换目标网站都要同步修改请求逻辑、解析规则和异常处理三处代码,极易引发连锁错误;封装成类可将变化点(如base_url、session、selectors等)抽为实例属性,实现解耦与复用。

为什么直接写函数的爬虫难维护
因为每次换目标网站,都要改请求逻辑、解析规则、异常处理三块代码,一改就崩。核心问题不是“爬不到”,而是“改一处,崩三处”——比如把 requests.get() 换成 session.get(),结果所有 headers 和 cookies 都没传进去;又或者 XPath 表达式写死在解析函数里,换个页面结构就得全局搜索替换。
封装成类的关键:把变的东西抽成实例属性
真正该封装的不是“怎么发请求”,而是“谁在发、发给谁、怎么认得回数据”。用类不是为了炫技,是让变化点有明确落脚处:
-
self.base_url存根域名,后续所有urljoin()都基于它 -
self.session统一管理 cookies、headers、重试策略,不用每个方法都传 -
self.parser可选设为"lxml"或"html.parser",解析引擎切换不碰业务逻辑 -
self.timeout和self.retries作为可调参数,调试时临时加大,上线再缩回
示例:初始化时就定好行为边界,而不是等报错才去补 try/except
class BaseSpider:
def __init__(self, base_url, timeout=10, retries=3):
self.base_url = base_url.rstrip('/')
self.session = requests.Session()
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36'
})
self.timeout = timeout
self.retries = retries
解析逻辑必须和请求逻辑解耦
很多人把 xpath 或 cssselect 写在 parse_page() 里,结果一换网站就要重写整个方法。正确做法是把选择器定义成类属性或配置字典,让 parse() 只做通用提取动作:
- 用
self.selectors字典存各字段对应的 CSS 选择器,如{"title": "h1", "price": ".price"} -
parse()方法只循环这个字典,统一调用response.css()或etree.xpath(),返回结构化字典 - 若某网站用 JSON API,就重写
parse(),但fetch()完全不动
这样新增一个站点,只需继承并覆盖 selectors 或 parse(),不用动网络层
最容易被忽略的坑:状态残留和连接复用失控
类封装后,self.session 是长期存在的,但很多人忘了它会自动携带上一次的 cookies、甚至 auth 凭据。爬多个无关站点时,可能 A 站登录态污染 B 站请求,导致 403 或返回缓存页。
- 敏感操作(如登录)后,显式调用
self.session.cookies.clear() - 不同业务线用不同类实例,别共用一个对象
- 如果用
aiohttp,ClientSession必须用async with或手动.close(),否则连接池撑爆 - 日志里加
self.__class__.__name__,方便排查是哪个爬虫实例出的问题
面向对象不是加个 class 就完事,关键是把“谁负责什么状态”划清楚——状态在哪,bug 就藏在哪。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











