用Python写爬虫的常见陷阱与避坑指南

你写了一个爬虫,兴冲冲地运行,却只得到一堆空白页面或者被网站封了IP。这不是你的代码有问题,而是你踩入了常见的陷阱。爬虫世界看似简单——只要requests.getBeautifulSoup就能搞定,但真实场景里藏着无数坑:从请求头缺失到动态渲染,从反爬策略到法律红线,每一步都可能让你的爬虫“暴毙”。本文不讨论基础语法,只专注那些真正让你熬夜debug的坑,以及如何优雅避开。

请求头伪装:被一眼识破的“裸奔”

很多新手爬虫的请求头就一个User-Agent,甚至默认的python-requests/2.x都没改。你这是在告诉网站:“快来封我,我是一个机器人。”服务器通过User-Agent、Referer、Accept-Language等数十个字段来区分人与机器,缺一个都可能触发反爬。

避坑指南:建一个请求头池,随机切换。不仅要换UA,还要带上Referer(模仿从搜索引擎点击过来)、Accept-Encoding(支持gzip)、Cookie(如果登录态需要)。使用fake_useragent库生成真实浏览器UA,但别直接用random.choice(fake_useragent),因为某些库的生成列表陈旧且集中。更好的做法是手动收集20个主流浏览器UA,存成列表轮换。另外,某些网站会检查Sec-Fetch-相关头,比如Sec-Fetch-Site: nonecross-site请求放行率不同,用浏览器开发者工具抓一个真实请求,逐字段复制。

动态加载内容:你看到的不是HTML

requests爬到的页面和浏览器看到的可能完全两码事。现代网站大量使用JavaScript渲染,数据通过XHR或Fetch异步获取。你辛苦写解析器解析到的只是骨架,核心数据躺在API里。

避坑指南:三步走。第一步,打开浏览器开发者工具Network面板,刷新页面,筛选XHR/Fetch请求,寻找返回JSON数据的接口。很多时候直接调API比渲染页面快10倍。第二步,如果数据必须从渲染后DOM中拿(比如某些图表库渲染),考虑使用无头浏览器如Selenium或Playwright。千万别一上来就上Selenium,它慢且容易被检测。Playwright支持更精细的浏览器指纹控制。第三步,分析API的参数签名,许多网站对参数做哈希或加时间戳校验(如知乎的x-zse-96)。你需要逆向前端JS逻辑,或者用pyexecjs运行还原后的签名函数。

频率控制与IP封锁:莽撞的代价

不加延时直接循环请求,99%的网站会在几十个请求后给你返回403或验证码。更严重的是,你的公网IP可能被永久拉入黑名单,影响其他正常上网。

避坑指南:使用指数退避策略,失败后休眠时间递增(1秒、2秒、4秒、8秒...),最大上限比如60秒。用time.sleep(random.uniform(1,3))随机化间隔,防止规律性访问。如果必须大规模爬取,购买高质量代理池(不是免费透明代理),检查代理的匿名级别:透明代理会在X-Forwarded-For透露真实IP,匿名代理隐藏但会添加特定头,高匿代理完全不暴露。代理池要维护动态白名单,连续返回503的代理迅速剔除。另外,使用requestsSession复用连接,别每次新建TCP,减少连接建立导致的流量特征。

编码与字符处理:中文变成乱码

requests自动猜测编码有时不靠谱,尤其当网页声明charset与实际不符时。你拿到一段\u2019½的乱码,然后尝试各种decode,浪费大量时间。

避坑指南:强制指定编码response.content拿到原始字节,用chardet检测实际编码,再用.decode(detected_encoding)转换成字符串,避免依赖response.encoding。对于HTML页面,可以从<meta>标签中解析charset,但很多网站meta不起作用,比如某些用UTF-8却声明gb2312。保险做法是:先用response.apparent_encoding获取最可能的编码,若返回结果仍有乱码,尝试常用的utf-8gbkgb2312shift-jis等。对于JSON响应,requests会自动处理,但如果你自己从网页JavaScript变量中提取JSON字符串,务必用json.loads前先repr()查看是否有非法转义。

数据解析:XPath/CSS选择器的脆弱性

BeautifulSoupfind_all('div', class_='content')一次性解析,过几天网站改版,class名称变成content__new,你的爬虫直接废掉。依赖页面结构的选择器极其脆弱,尤其当网站采用前端框架动态生成类名时。

避坑指南:优先使用CSS选择器的属性值匹配,比如a[href='/detail/']通过URL模式定位,比依赖class更稳定。对于经常变化的文本,提取父容器后再用正则匹配特定模式(如价格“¥\d+.\d{2}”)。建立数据校验与容错机制:每个字段提取后检查是否为None,若是则用默认值或告警日志。使用parser库如lxml比BeautifulSoup快很多,但lxml对不规范的HTML容错性较差,可以先尝试用html5lib解析再转成lxml树。对于表单数据,不要用正则匹配所有选项,先用浏览器提取典型内容,编写容错分支。

异常处理:脚本因一个错误而崩溃

网络波动、服务器返回500、连接超时、SSL证书错误……爬虫运行时间越长,异常种类越多。最常见的错误是只捕获Exception并打印,然后继续循环,导致后续请求全部失败而不自知。

避坑指南:精细区分异常类型并制定恢复策略。比如ConnectionError重试3次,Timeout立即换代理重试,HTTPError中403直接休眠更久,444(连接断开)重建session。使用tenacityretrying库实现重试装饰器,指定stop_max_attempt_number=5, wait_exponential_multiplier=2。同时记录详细的请求信息(URL、时间、代理IP)到日志文件,方便事后排查。注意requeststimeout参数必须设置,不设可能被网站拖死(建立连接后不返回数据)。设置(connect, read)两个超时值,比如(3, 10)

验证码与滑动验证:人类特有的门槛

任何频率控制不完善的爬虫迟早会遇到验证码。从简单的图片识别到酷炫的滑动拼图,甚至无感行为验证。很多爬虫死于验证码,原因是太依赖自动打码服务或模拟浏览器,忽略了行为真实性的模拟。

避坑指南:尽量绕开验证码。分析触发条件:是否因为同一IP短时间内大量请求同一频道?如果是,降低频率、使用不同路径跳转、随机化点击间隔。对于滑块验证,不要用固定像素平移,需要模拟人类手拖动的随机加速度曲线(开始慢、中间快、结尾微调)。使用seleniumActionChainsPlaywrightmouse.move加上贝塞尔曲线轨迹。图片验证码可以接入第三方打码平台(如超级鹰、图鉴),但成本高且有延迟。终极方案是使用云服务商提供的指纹浏览器+真人代理,比如使用puppeteer-extra-plugin-stealth伪装全浏览器特征,但维护成本高。

编码与反爬升级:对抗永无止境

网站工程师也在进化。除了上述策略,还有字体反爬(将数字显示为自定义字体映射)、CSS偏移(文字用层叠覆盖打乱顺序)、文本图片化(直接截图)、点击流分析(监控鼠标轨迹与JS事件)。你永远无法100%模拟真实用户,因为浏览器也存在细微特征(WebGL指纹、Canvas指纹、时区差异)。

避坑指南:优先选择提供开放API或RSS的网站,合法合规。对于必须要爬的动态反爬,采用浏览器自动化+开发者工具注入:用Playwright打开页面,在page.evaluate中修改window.navigator.webdriver标志位,覆盖navigator.plugins个数,删除navigator.languages中的常量。使用undetected-chromedriver 专门绕过WebDriver检测。另外,避免爬取高价值核心数据(如实时行情、绝版资源),这类网站反爬投入巨大,一次突破可能很快被补上。

数据存储与去重:爬虫越写越大,数据越来越乱

爬取量上去后,多线程/异步并发写入数据库出现重复、死锁、数据不一致。忘记设置唯一索引导致几十万条重复记录,后期清洗成本远超爬取成本。

避坑指南:存储前必须去重。简单爬虫用set缓存URL指纹(对URL做MD5),但内存有限。大规模爬虫使用RedisBloomFilterRedisSADD操作原子性,BloomFilterpybloom_live库实现,误判率设置0.1%。写入数据库时使用ON DUPLICATE KEY UPDATE(MySQL)或INSERT IGNORE,避免先查后写。对于文件存储(如CSV、JSONLines),禁止追加写入的方式,用批处理写入,并在写入前检查该行是否已存在(低效但保险)。另外,注意文件编码统一用UTF-8,Windows默认编码可能写入乱码。

法律法规与道德:爬虫最大的陷阱不是技术

你可能会踩到《网络安全法》《数据安全法》《个人信息保护法》的红线。即使技术再完美,未经授权爬取个人隐私、商业机密、受版权保护的内容,都可能面临法律诉讼甚至刑事责任。

避坑指南:爬取前先读robots.txt,遵守Disallow规则是基本礼貌,虽然它无法律强制力,但法院会参考。不爬取需要登录才能访问的页面,除非获得授权。不爬取个人邮箱、手机号、身份证等敏感信息。如果爬取公开数据用于学术研究或非商业用途,注意控制爬取频次,不要对服务器造成负担。存储数据时对个人标识做脱敏(哈希处理)。最后,保留请求日志和响应数据,万一被投诉,可以证明你的爬虫行为合理合法。不要抱有侥幸心理——国内已有爬虫开发被判刑的案例(如“爬虫抓取简历”案)。

异步与并发:多线程不是万能药

初学爬虫用threading或者multiprocessing跑,结果发现目标网站限制了并行连接数,或者GIL导致CPU密集型解析效率低。盲目使用多线程反而会触发反爬阈值,造成大量连接超时。

避坑指南:对于I/O密集型(网络请求),使用asyncio + aiohttp更轻量,单线程事件循环处理数千个并发请求。但要注意控制并发数,用asyncio.Semaphore限制同时进行的请求数量(比如10个)。对于CPU密集型解析(如解析数千个HTML页面),才考虑使用concurrent.futures.ProcessPoolExecutor,利用多核加速。无论如何,不要将请求和解析混在同一个线程/协程内阻塞等待,可以用生产者-消费者模式:一个协程负责请求,放入队列,另一个进程负责解析。另外,使用queue.Queue时注意大小限制,防止内存爆炸。

调试与日志:没有日志的爬虫是盲人

很多人在开发时用print调试,上线后发现出问题不知道哪一步失败。没有日志的爬虫就像在黑暗里开车,撞了都不知道撞的啥。

避坑指南:使用logging模块替代print,设置DEBUG级别打印请求详情,INFO级别打印批处理进度,WARNING级别打印重试次数,ERROR级别打印不可恢复的错误。日志输出到文件并轮转RotatingFileHandler按大小或时间分割,方便回溯。每个请求记录唯一ID(如trace_id),以便关联请求和响应。对于异常堆栈,捕获后用logger.exception(msg)自动记录traceback。这样你就能在第二天看到日志里“2025-04-06 02:15:33, ERROR - [trace_abc123] 请求xxx超时3次后放弃”的痕迹,定位问题只需一秒。

最后的忠告:保持谦逊与持续学习

爬虫技术领域的变化比前端还快。今天有效的反爬绕过技巧,明天可能就被补上。你永远不能依赖一个固定的“避坑清单”,因为网站也在学习你的行为。好的爬虫开发者总是建立模块化架构:请求层、解析层、存储层、监控层分离,每一层都能单独升级。每次遇到新坑,都把它写成测试用例,集成到你的爬虫框架中。另外,多阅读开源爬虫框架(如Scrapy)的源码,学习别人如何设计重试、去重、中间件。别做“只写爬虫”的程序员,要做理解HTTP协议、浏览器渲染、网络拓扑的工程师。

真正的避坑指南不是列出所有陷阱,而是教会你如何在每个未知陷阱面前,冷静分析、理性应对。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值