资讯详情

资讯详情

复杂网页爬虫实战:电商数据采集与反爬策略解析

以前帮朋友做电商选品分析的时候最先遇到的一个拦路虎就是怎么把商品数据快速拿到手。电商页面看起来简单真下手去写爬虫的时候你会发现它跟普通的静态博客完全是两个物种——商品详情、价格、SKU、评论这些信息往往不是一次性渲染出来的而是通过异步接口分多次加载甚至整张价格表都做了字体反爬。你拿 requests 直接请求页面拿到的只是一堆接口占位符和空标签。这篇文章整理的是我在这类“复杂网页数据采集”项目里的完整落地经验会从需求拆解讲到工具选型再一路拆到具体的处理策略、数据清洗思路和反爬应对方案。适合那些遇到“页面能打开、代码却采集不到有效字段”这类问题的开发者也适合准备入行爬虫、对动态网页和信息提取有好奇心的新手。我会尽量少讲空泛的概念多给可直接抄走的代码和思路。1. 项目拆解与整体设计思路1.1 先把“复杂页面”这件事说透电商复杂网页和普通网页最大的区别在于它不是一个文档而是一个应用。你看到的每一个商品卡、每一段价格、每一格库存状态背后可能对应着好几个独立接口。前端为了追求加载速度和交互体验会把首屏内容先渲染出来剩下的数据等用户滚动、点击再请求。这就导致你直接用 requests 去拉 HTML 源码经常看到的是 “加载中” 之类的水印值转头发现数据并不在源代码里。我通常会把复杂页面分成三类来理解接口动态加载型数据在 XHR 请求里页面 HTML 只是空壳需要找接口规律。浏览器渲染型数据由 JavaScript 渲染生成接口可能加密必须依赖真浏览器环境。混合加固型既有接口加密又有前端脚本检测比如计算环境指纹、检测 webdriver 标识。电商网站大多数都处于第二种和第三种之间。明白了这一点你就不会再用“发个包拿 HTML”的老思路去硬碰硬而是会提前把工具链锁定在浏览器自动化方向。1.2 需求拆解先定目标再动手很多爬虫项目翻车不是技术不行而是需求根本没有拆清楚。做电商数据采集第一步不是写代码而是把“我要什么”拆到直接变成字段级需求。比如一个商品采集任务至少要确认四件事采集对象具体是哪个平台、哪个类目、哪些关键词结果页。需要哪些字段标题、价格、销量、店铺名、评论数还是详情页里的规格参数。数据深度只抓列表页还是需要点进每个商品的详情页。数据更新频率是一次性存量采集还是每周定时增量。把这些写清楚之后再去设计技术方案。很多人上来就写大而全的爬虫框架最后发现某个关键字段根本拿不到等于白干。我自己习惯先人工打开目标页面打开浏览器开发者工具先把接口和页面结构摸一遍再去动键盘。提示一开始不要急着上 Scrapy 这类重型框架。复杂网页项目建议先用脚本把单页面跑通确认字段都能稳定取到之后再考虑框架化和并发加速。1.3 技术路线选型从单页脚本到分布式扩展技术路线没有银弹只有匹配需求的选择。单页采集、中小规模数据量用 Playwright 或 Selenium 脚本就够了。需要每天跑成千上万条数据再考虑 Scrapy 加 Playwright 集成或者引入消息队列做分布式调度。我做第一版的时候只用了 Playwright 加 pandas流程非常简单访问列表页 → 翻页 → 解析所有商品节点 → 进入详情页补充字段 → 存 CSV。后来数据量上来才把其中比较稳定的部分抽出来放到 Scrapy 里用 Item Pipeline 做入库和去重。方案演进的顺序很重要不要一上来就搭 Mesos、Kafka 那套小项目撑不起来维护成本还高。2. 工具选型与环境准备2.1 浏览器自动化的老面孔与新选择传统做动态页面爬取最常用的是 Selenium。它确实是工程化的老牌方案社区资料全遇到问题基本都能搜到答案。但它的缺点也很明显每次都要拉起一个完整的浏览器实例资源占用高启动速度慢而且自动化特征比较明显容易被网站的风控系统识别。后来我接触了 Playwright它由微软维护API 设计更现代化支持自动等待、拦截请求、直接操作浏览器上下文。拿它来采集电商页面最舒服的一点是它把“等待元素出现”这个动作做成了内置行为不用像 Selenium 那样频繁自己写WebDriverWait配合expected_conditions。再加上它自带无头模式、UA 伪装和浏览器指纹处理能力一次性把很多反爬问题解决在源头。除了这两个Python 生态里还有几个新势力的选择值得关注。DrissionPage把 requests 的快速和浏览器 automa 的稳定性结合在一起适合需要在普通请求和浏览器渲染之间切换的场景代码写法也更贴近 Python 习惯。curl_cffi擅长模拟浏览器 TLS 指纹对某些从 TLS 层做校验的网站有奇效。Playwright-Stealth给 Playwright 加了一层反检测补丁专门处理 webdriver 标识、自动化相关属性暴露这类问题。2.2 我最终选择的组合方案这个项目里我用的核心组合是 Playwright Python配套 requests 处理纯接口场景。为什么不用 Scrapy因为当时首版目标只需要采集约几万条商品数据而且页面动态渲染比重高Scrapy 的优势发挥不出来。用小脚本快速验证才是最关键的目标。实际环境大体如下Python 3.10Playwright 1.40 左右版本pandas 做表格输出openpyxl 用来写 Excel平时调试用 Jupyter正式跑用命令行脚本环境安装可以用下面这组命令完成pip install playwright pandas openpyxl playwright install chromium安装后建议先用下面的代码验证浏览器能正常启动from playwright.sync_api import sync_playwright with sync_playwright() as p: browser p.chromium.launch(headlessFalse) page browser.new_page() page.goto(https://example.com) print(page.title()) browser.close()headless 参数建议在调试阶段设成 False方便肉眼看页面表现确认一切稳定再切回 True。2.3 为什么我不建议每个项目都上分布式有些文章特别喜欢渲染“分布式爬虫”这种词好像不搞一套分布式架构就不叫专业爬虫。这里我说句大实话电商复杂页面的瓶颈往往不在并发不够而在反爬触发概率。你单机跑 100 个并发请求很可能跑到第 30 个就触发验证码但你把并发控制在 3 到 5可能稳稳跑一整天也没事。分布式解决的是规模问题不是绕过问题。先把单机的频率控制、请求伪装、重试策略做好永远比堆机器更重要。3. 核心实现从请求到结构化数据的完整流水线3.1 请求上下文管理与cookies预热电商网站的数据采集第一步不是写提取逻辑而是先把浏览器的 “人味” 做足。一个刚启动的全新浏览器实例cookie 是空的localStorage 是空的连字体列表都和正常用户不一样。这种干净环境很容易被识别。我习惯先创建一个持久化上下文目录把登录后的 cookie 和缓存存下来后续每次启动都复用。Playwright 里可以直接指定user_data_dirfrom playwright.sync_api import sync_playwright with sync_playwright() as p: context p.chromium.launch_persistent_context( user_data_dir./user_data, headlessFalse, viewport{width: 1366, height: 768}, user_agentMozilla/5.0 ... Chrome/120.0 Safari/537.36, localezh-CN, ) page context.new_page() page.goto(https://example.com)首次运行时我手动在页面里完成一次登录甚至模拟几次正常的浏览行为让网站给这个上下文打上“可信用户”的标签。之后的采集任务就复用这个 context能有效降低被强制验证的概率。这个操作做起来非常简单却经常被忽视。提示重试机制里要引入随机等待不要用固定time.sleep(2)。更符合人体操作习惯的是在 1 到 3 秒之间随机分布间隔太规律反而像机器。3.2 请求拦截与响应等待策略采集动态网页时页面加载并不等于数据加载完成。你打开商品列表页面框架先出现接口可能几百毫秒后才返回数据图片可能更慢。如果不等数据就解析拿到手的就是空值。Playwright 的page.expect_response方法特别适合处理这种场景。我可以在触发滚动或者点击“加载更多”之前先注册一个等待条件等指定接口返回了再继续执行。这个方法比固定等几秒可靠得多因为网络波动大的时候固定等待不是早了就是晚了。示例with page.expect_response(lambda resp: api/item/list in resp.url and resp.request.method GET): page.click(text加载更多) response page.expect_response(...) data response.json()还有一种方式是直接拦截响应体把原本要由页面处理的 JSON 内容提前拿住。我在某些需要直接拿价格、库存的场景里会在页面脚本执行前就拦截接口响应用响应 JSON 作为主要数据源用页面上的 DOM 文本做二次校验。这样两头保险能明显减少字段丢失。3.3 动态加载与无限滚动处理电商列表页很多都是无限滚动设计。它们不在 URL 上翻页而是靠你不断往下滚触发接口去加载下一页数据。这种情况下最直接的方式是用循环模拟滚动然后监听接口返回直到满足结束条件。核心逻辑可以是这样获取当前页面的 DOM 节点数量。执行鼠标滚轮滚动到页面底部。等待新节点出现或者接口返回。判断是否还有“加载更多”按钮。重复直到没有新数据或者达到最大页数。用小步滚动替代一次滚到底是因为有些网站对极端快速的滚动会有行为检测。正常用户的滚动速度是渐进的所以我在代码里用mouse.wheel(0, 600)每次滚 600 像素中间间隔 0.3 秒。这个方法跑下来触发反爬的频率要比一次滚动到底低很多。3.4 数据提取从页面或接口中拿字段的三种思路数据提取部分我通常按照页面复杂度分成三层思路。第一层静态页面字段直接走 XPath 或 CSS 选择器。这一层最简单适合标题、店铺名、销量这类稳定结构。XPath 的容错能力比 CSS 更强比如包含匹配、按文本定位元素这些在电商页面里非常常用。title page.locator(xpath//div[classitem-title]/a).inner_text()第二层动态字段在接口里。打开开发者工具切到 Network 面板刷新页面后看 XHR 或 Fetch 请求找到返回 JSON 的那个接口直接请求参数解析 JSON。速度上远比浏览器渲染快。价格、库存、SKU 这类数据接口一般会返回结构化字段拿过来就是干净的 JavaScript 对象不用做字符串清洗。第三层页面渲染和接口都对不上的情况。这种只能用 OCR 或图像识别上衣场景主要是滑块验证码和字体反爬。我建议优先考虑绕开比如寻找对应的内部接口、分析字体文件映射关系。真正落到 OCR 的部分尽量少识别率不稳定后期维护成本极高。3.5 数据清洗与落库采集不是终点拿到数据之后还要清洗。我在项目里经历了几个典型坑价格字段经常是 “¥ 199.00”前面有货币符号中间有空格。销量字段可能是 “5000 人付款”我需要把“人付款”剥离掉。评论数可能是 “1.2万”要把中文单位转成整数。部分字段在页面显示为“暂无”解析后是空字符串。所以我在 pipeline 阶段专门写了一个清洗函数用正则表达式标准化数字和单位。清洗后的数据再转成 pandas DataFrame 做初步统计最后写入 Excel 或者 MySQL。import re def clean_price(raw): if not raw: return None return float(re.sub(r[^0-9.], , raw)) def clean_sales(raw): if not raw: return 0 if 万 in raw: return int(float(re.sub(r[^0-9.], , raw)) * 10000) return int(re.sub(r[^0-9], , raw))注意入库一定要做去重。电商商品数据列表页翻页时经常出现同一商品被多个入口重复收录我直接按商品ID 采集时间作为联合主键避免重复数据污染分析结果。4. 常见问题与排查技巧实录4.1 元素定位失败元素明明存在但取不到值这个问题的出现频率最高。原因是页面用了 Shadow DOM 或 iframe。两种结构的 DOM 树跟普通页面不一样常规的page.locator找不到内部元素。排查方法很简单去开发者工具里看元素节点如果看到#shadow-root标签然后展开里面还有元素结构那就说明用了 Shadow DOM。此时可以用 Playwright 的 pierce 选择器直接穿透 Shadow DOM 操作内部元素。如果是 iframe则要切换 frame 上下文。Playwright 里可以用page.frame_locator定位到 iframe 内部的元素不需要手动切换上下文。frame page.frame_locator(iframe[src*comment]) comment_list frame.locator(.comment-item)4.2 网页卡在滑块验证或行为验证滑块验证是电商平台最常见的反爬手段。遇到这个我第一反应不是非得“绕过”而是先看能不能通过调整行为把它触发概率降下来。实践证明下面几个动作特别有效不要用 headlessTrue改用 headlessFalse 配合虚拟显示器。减少请求频率调低并发数。在关键动作之间加入随机鼠标移动轨迹不用瞬间跳转。避免固定 UA要使用和浏览器版本匹配的真实 UA。如果确实被限制那就用页面上的点击操作去完成滑块。Playwright 模拟滑块动作的核心在于轨迹模拟不要用匀速直线那样反而假。我自己写过一个函数采用“先慢后快再慢”的加速度轨迹识别通过的占比有明显提升。4.3 数据采集过程中页面崩溃或浏览器内存暴涨长任务跑久了浏览器实例占用的内存会一直涨。多标签页加上渲染缓存很容易把本地电脑拖到卡顿。这个问题我在连续采集几万条数据时遇到过最后靠“定时重启浏览器”解决。具体做法是每采集 500 或 1000 条数据主动关闭当前 context重新启动一个新的 context。这样缓存不会无限累积内存占用能保持在一个稳定水平。另外如果采集过程中某个商品详情页跳转异常不要立刻报错终止。更稳妥的做法是记录当前状态把该 URL 放进失败队列跑完一轮之后重新重试。我用一个简单的列表维护失败队列重试超过三次才丢弃并记录日志。完整跑完的任务失败率基本能控制在 0.5% 以下。4.4 反爬识别升级检测到自动化特征有些网站会在请求链路或者浏览器对象上做手脚检测navigator.webdriver是否为 true或者检查浏览器窗口尺寸、触控支持、语言设置等特征。Playwright 默认的 headless 模式更容易暴露这些特征。我的应对经验是使用 launch_persistent_context 替代普通 launch视觉上更像普通用户。手动给 context 设置permissions、geolocation、timezone_id、locale这些参数让环境一致性更强。引入 stealth.js 一类的脚本在页面加载前先执行覆盖掉典型的自动化属性。尽可能复用正常用户的 cookie 和指纹信息。另外还有一个经验特别想分享不要一上来就做高并发。电商类网站的反爬往往是阶梯式触发的一开始可能没反应并发一大就直接封你账号或 IP。我现在的习惯是无论是模拟浏览器脚本还是纯接口采集都从 1 个并发开始逐步加到 2、3、5一旦发现异常立刻退回安全水位。4.5 常见问题速查表现象可能原因处理建议取到的字段为空数据由接口异步加载用 expect_response 等待接口返回再解析同一条数据重复入库列表页重复收录按商品唯一 ID 去重页面频繁弹验证码并发过高或指纹暴露降低并发模拟真实滚动复用持久化上下文浏览器内存持续上涨长时间运行未重启每 N 条任务完成后重启 context某商品详情页抓取失败页面结构特殊放入失败队列统一重试价格拿下来单位不对页面做了字体反爬解析字体文件映射或寻找内部接口CSS 选择器定位不到组件在 Shadow DOM 内改用 pierce 选择器4.6 合规提醒与数据使用边界谈到爬虫一定避不开合规问题。虽然我不打算在这里展开讲具体法律条款但有几条底线遵守了能少走很多弯路。第一确保你的采集行为不违反目标网站的 robots 协议和用户协议尤其是登录后才能看到的非公开数据。第二不要采集个人敏感信息比如用户手机号、收货地址、真实姓名这类内容。第三采集频率要克制不要影响目标网站正常服务。数据使用的边界要搞清楚采集公开数据做分析是一回事把数据重新包装后对外提供商业服务是另一回事。这个原则贯穿整个项目始终。我每轮全量采集前都会先做一个小范围测试确认目标网站的响应速度没有被明显拖慢。这不只是讲技术道德的问题也是保证采集任务能长期稳定跑下去的基础。5. 从单机脚本到工程化落地的扩展建议5.1 数据存储选型如果数据量级从几万条涨到几百万条pandas 那套方案就不够看了。CSV 文件读写会越来越慢内存占用也会变大。这个阶段我会迁移到数据库存储。MySQL 适合结构化程度高、需要多人查询的业务数据MongoDB 更适合商品快照、详情页 JSON 这类半结构化数据。我自己比较喜欢用 MySQL 存商品主表用 MongoDB 存每次采集的原始快照。原始快照的意义在于即使后续分析和清洗逻辑变了你还能从最原始的数据里重新提取字段不至于后悔当初存少了。5.2 定时调度与监控工程化落地之后定时任务、日志和监控就变得很重要。我每天固定凌晨跑增量采集用 cron 或计划任务触发脚本。采集完成之后把结果发送到企业微信或钉钉机器人失败率达到阈值就触发告警。日志要记录几个关键信息任务开始时间、结束时间、采集成功数、失败数、失败 URL 样例、执行过程中的异常堆栈。有日志才有排查依据。5.3 数据采集与 AI 的结合点最后聊一聊“AI 是爬虫技术的更深层次运用吗”这个话题。这两年大模型火起来之后很多团队会把大模型放进数据采集链路里。比较常见的做法是让大模型充当“信息抽取器”你不再需要为每种页面结构单独写解析规则直接把 HTML 文本或者截图扔给模型让它输出需要的 JSON 字段。这种方法在处理长尾网站、经常改版的页面时能省很多维护精力。但也要有心理准备大模型抽取的准确率做不到 100%而且推理成本比正则要高得多。我的建议是两条腿走路——结构稳定的重点页面用传统解析保证速度和精度结构经常变化或者没有规律的页面用大模型兜底。把它当作一个灵活组件而不是全盘替代。我再顺手分享一个自己用到比较好的流程用 Playwright 把页面渲染成截图和 HTML 摘要然后把摘要发给大模型让它识别页面里面哪些区块对应商品标题、价格、评论。这个流程特别适合页面结构频繁变化、维护成本失控的项目。写在最后的几点体会做了这么多采集项目最大感受是爬虫工程师的核心竞争力不在于会写多少解析规则而在于具备“结构拆解”和“问题预判”的能力。拿到一个复杂电商页面你能不能在五分钟内判断出它属于哪种加载方式、关键字段藏在 DOM 里还是接口里、风险点在哪里这决定了整个项目的推进速度。踩过的坑多了之后我现在做任何采集任务都会遵守三条底线一是策略克制永远保证对目标站点友好二是数据清洗仔细不要拿到脏数据就往下游送三是日志完善出现问题能当天定位而不是靠猜。这些看起来不酷但真正跑数据的人会明白稳定比炫酷重要得多。如果你正在做一个电商数据采集项目我的建议是先拿一天时间把目标页面结构摸清楚把接口列表列出来再开始写脚本。别急着抄代码这个准备工作做得越扎实后面踩的坑就越少。
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →