电商数据采集系统实战:从爬虫脚本到稳定数据管道

发布时间:2026/9/26 1:13:55
电商数据采集系统实战:从爬虫脚本到稳定数据管道 1. 从零搭建电商数据采集系统的整体思路电商数据采集这件事表面上看是写个爬虫抓数据但真正落到生产环境里你会发现抓取只是整条链路里最不值钱的一环。我做过好几个电商数据项目从最早的单机 requests 脚本到后来几十台机器跑的分布式采集再到现在的采集 清洗 入库 监控一体化管道踩过的坑足够写一本书。这篇文章就把我这些年攒下来的实战经验完整拆一遍重点讲怎么把一堆零散的爬虫脚本变成一个能长期稳定跑下去的数据管道。先说清楚这套系统到底解决什么问题。电商场景下的数据需求通常有这么几类商品详情标题、价格、SKU、库存、评论数据评分、内容、时间、销量与榜单、店铺信息、类目结构。这些数据的特点是量大、更新频繁、页面结构经常变、反爬策略持续升级。如果你只是偶尔抓一次写个脚本跑完就完事但如果你要做价格监控、竞品分析、选品决策那就必须有一套能每天自动跑、出错能自愈、数据能直接给分析师用的管道。适合谁来参考这套方案我的判断是有 Python 基础、写过简单爬虫、但没做过完整数据管道的同学收益最大。如果你完全零基础建议先把 requests 和 Scrapy 的基础过一遍再来看不然很多设计取舍你会看不懂为什么这么做。整套方案的技术栈我选的是Scrapy Playwright PostgreSQL/ClickHouse Airflow或轻量调度 Prometheus 监控下面会逐个解释为什么这么选。整体架构我习惯分成四层这个分层不是拍脑袋定的而是根据故障隔离原则来的——任何一层挂掉不应该影响其他层的正常运行层级职责核心技术故障影响范围采集层页面抓取、反爬对抗Scrapy、Playwright只影响新数据获取解析清洗层结构化提取、字段标准化Parsel、Pydantic影响数据质量存储层原始数据与业务数据分离存储PostgreSQL、ClickHouse影响查询与分析调度监控层任务编排、告警、重试Airflow、Prometheus影响运维效率这个分层最大的好处是可替换。比如你哪天觉得 Scrapy 不够用了想换 Playwright 集群只要采集层的输出接口不变上层完全不用动。我见过太多项目把抓取、解析、入库全塞在一个脚本里结果页面一改版整个流程全崩改起来牵一发动全身。2. 采集层设计Scrapy 与动态渲染的取舍2.1 为什么主力选 Scrapy 而不是纯 requests很多人入门爬虫是从 requests BeautifulSoup 开始的简单直接。但一旦你要抓几万、几十万个页面requests 的短板就暴露了没有内置的并发控制、没有自动重试、没有去重、没有中间件机制。你得自己造轮子造到最后发现造出来的东西就是半个 Scrapy。Scrapy 的核心优势在于它把爬虫的通用问题都抽象好了调度器负责 URL 队列和去重下载器负责并发和重试中间件负责请求/响应的统一处理Item Pipeline 负责数据流转。你只需要写解析逻辑其他都是配置。我实测下来单机 Scrapy 配合合理的并发配置CONCURRENT_REQUESTS 设为 16DOWNLOAD_DELAY 设为 0.5抓取速度能稳定在每秒 20-30 个页面比手写的多线程 requests 稳定得多。但 Scrapy 有个硬伤它默认不执行 JavaScript。而现在的电商页面尤其是淘宝、京东、抖音这类大量内容是通过 JS 动态渲染的你直接请求 HTML 拿到的往往是空壳。这就引出了下一个关键决策。2.2 动态渲染方案Playwright 集成进 Scrapy处理 JS 渲染页面常见方案有三种Selenium、Splash、Playwright。我最终选 Playwright理由很实在Selenium太老了驱动管理麻烦速度慢多标签页支持差Splash是 Scrapy 官方推荐的但它是独立服务部署复杂而且对现代前端框架支持一般Playwright是微软出的API 现代支持 Chromium/Firefox/WebKit自动等待机制好用还能拦截网络请求集成方式我推荐用scrapy-playwright这个库它把 Playwright 封装成了 Scrapy 的下载器中间件你只需要在 Spider 里给 Request 加个meta{playwright: True}就能触发浏览器渲染非常丝滑。# settings.py 关键配置 DOWNLOAD_HANDLERS { http: scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler, https: scrapy_playwright.handler.ScrapyPlaywrightDownloadHandler, } TWISTED_REACTOR twisted.internet.asyncioreactor.AsyncioSelectorReactor PLAYWRIGHT_BROWSER_TYPE chromium PLAYWRIGHT_LAUNCH_OPTIONS { headless: True, args: [--disable-blink-featuresAutomationControlled], }这里有个坑我必须提醒不要所有请求都走 Playwright。浏览器渲染的开销是普通请求的 10-20 倍内存占用也大。我的做法是先用普通请求试探如果解析出来关键字段为空再降级到 Playwright 重试。这样能把 80% 的请求走轻量通道整体吞吐量提升非常明显。2.3 动态 iframe 的处理技巧电商页面里 iframe 特别常见尤其是支付、登录、评论模块。Scrapy-Playwright 处理 iframe 有个细节默认情况下它只返回主 frame 的内容iframe 里的内容需要显式指定。我一般用playwright_page_methods配合frame_locator来定位yield scrapy.Request( url, meta{ playwright: True, playwright_page_methods: [ PageMethod(wait_for_selector, iframe#comment-frame), ], playwright_include_page: True, }, callbackself.parse_with_iframe, )然后在回调里通过page.frame_locator()拿到 iframe 内容。实测下来如果 iframe 是跨域的Playwright 依然能访问因为它是在浏览器上下文里操作这点比纯 HTTP 请求强太多。但要注意及时关闭 page否则浏览器实例会越积越多内存直接爆掉。2.4 反爬对抗的实战心得反爬这块我不展开讲具体绕过手段合规红线只讲工程层面的稳定性设计。核心思路是降低请求特征、控制请求频率、分散请求来源。User-Agent 池不要用固定的 UA准备 50-100 个真实浏览器 UA 轮换请求间隔随机化DOWNLOAD_DELAY配合RANDOMIZE_DOWNLOAD_DELAYTrue让间隔在 0.5-1.5 倍之间浮动Cookie 管理用 Scrapy 的 CookiesMiddleware但要注意有些站点会绑定 IP 和 Cookie这时候需要配合代理池失败重试策略对 403、429 这类状态码不要立即重试而是指数退避RETRY_TIMES3RETRY_HTTP_CODES[500, 502, 503, 504, 408, 429]提示反爬对抗是持续博弈不要指望一套配置吃一辈子。建议把反爬相关的参数全部做成可配置项方便随时调整而不用改代码。3. 数据解析与清洗从脏 HTML 到干净结构化数据3.1 解析器的选择与字段标准化Scrapy 默认用 Parsel底层是 lxml支持 CSS 和 XPath 两种选择器。我的经验是结构稳定的页面用 CSS结构混乱或需要复杂条件判断的用 XPath。比如提取商品价格CSS 写法是.price::text但如果价格分成了整数部分和小数部分两个标签就得用 XPath 拼接。解析出来只是第一步真正麻烦的是字段标准化。同一个价格字段不同平台可能叫price、sale_price、current_price单位可能是元也可能是分可能带货币符号也可能不带。我的做法是定义一个统一的 Pydantic 模型所有平台的解析结果都往这个模型里塞from pydantic import BaseModel, field_validator from decimal import Decimal class ProductItem(BaseModel): platform: str product_id: str title: str price: Decimal currency: str CNY stock: int | None None captured_at: datetime field_validator(price, modebefore) def clean_price(cls, v): if isinstance(v, str): v v.replace(¥, ).replace(,, ).strip() return Decimal(v)用 Pydantic 的好处是校验和清洗一步到位脏数据在进入管道前就被拦下来了。我见过太多项目把脏数据直接入库后面分析师用的时候才发现一堆空值和异常值返工成本极高。3.2 数据去重与增量采集电商数据采集最怕重复。同一个商品你今天抓一遍、明天抓一遍如果不去重数据库很快就膨胀到没法用。去重分两个层面URL 层面去重Scrapy 内置了dupefilter基于请求指纹去重。但默认是基于内存的重启就丢。生产环境要换成基于 Redis 的RFPDupeFilter这样多机共享去重集合。数据层面去重同一个商品可能通过不同 URL 访问到或者页面改版导致 URL 变化。这时候需要在入库时做去重我一般用(platform, product_id, captured_date)作为唯一键配合数据库的ON CONFLICT语法实现幂等写入。增量采集的策略我推荐基于时间戳 变更检测记录每个商品的最后采集时间只采集超过阈值比如 24 小时的商品同时对比关键字段价格、库存只有变化的才写入历史表没变化的只更新last_seen_at。这样能把存储量降低一个数量级。3.3 数据质量监控数据管道最怕的是静默失败——爬虫没报错但抓回来的数据全是空的或者错的。我一般会在 Pipeline 里加一层质量检查检查项阈值处理方式单批次记录数 预期的 50%告警关键字段空值率 10%告警并暂停入库价格异常值超出历史均值 ±3σ标记待人工确认解析失败率 5%触发页面结构变更告警这套检查帮我抓到过好几次问题有一次某平台改版价格字段的 class 名变了爬虫没报错但价格全抓成了空质量检查第一时间就发现了。4. 存储层设计PostgreSQL 与 ClickHouse 的分工4.1 为什么不用单一数据库很多人图省事所有数据都往 MySQL 或 PostgreSQL 里塞。小规模没问题但电商数据一旦上量问题就来了明细数据写入频繁、分析查询又重两者互相拖累。我的方案是冷热分离 读写分离PostgreSQL存商品主数据、店铺信息、配置表这类需要事务和频繁更新的数据ClickHouse存价格历史、评论明细、销量快照这类只追加、需要快速聚合分析的时序数据这个分工的核心逻辑是PostgreSQL 擅长 OLTP事务处理ClickHouse 擅长 OLAP分析查询。把合适的数据放到合适的库里各司其职。4.2 ClickHouse 的建表与写入优化ClickHouse 的建表有几个关键决策点。首先是引擎选择电商价格历史这种场景我用ReplacingMergeTree它能自动去重基于排序键配合version字段可以保留最新版本CREATE TABLE price_history ( platform LowCardinality(String), product_id String, price Decimal(10, 2), captured_at DateTime, version UInt64 ) ENGINE ReplacingMergeTree(version) PARTITION BY toYYYYMM(captured_at) ORDER BY (platform, product_id, captured_at);分区键用月份是因为电商数据查询通常按时间范围过滤按月分区能让查询只扫描相关分区。排序键的顺序也很讲究platform和product_id放前面是因为查询经常按这两个维度过滤captured_at放最后用于时间范围扫描。写入方面千万不要一条一条 INSERTClickHouse 对单条写入性能极差。正确做法是攒批每批 1000-10000 条用clickhouse-driver的execute批量插入或者用clickhouse-client的INSERT ... FORMAT CSV从文件导入。我实测过批量写入比单条写入快 100 倍以上。4.3 ClickHouse 重启报错的排查热词里提到了 clickhouse 重启报错 failed to flush system log already exists这个坑我踩过。原因是 ClickHouse 在关闭时没能正常 flush 系统日志表重启时发现日志文件已存在就报错了。解决方法通常是检查system库下的日志表query_log、metric_log等是否有残留的.tmp文件手动清理残留文件或者临时把对应的日志表配置关掉更根本的做法是调整flush相关参数给关闭流程留足时间注意ClickHouse 的系统日志表默认是开启的生产环境建议根据实际需要选择性开启不然日志表本身会占用大量磁盘和写入资源。4.4 Rocky Linux 9 上的部署要点在 Rocky Linux 9 上部署 ClickHouse有几个和 CentOS 时代不同的地方。首先是SELinux默认是 enforcing 状态ClickHouse 的数据目录如果不在标准路径下会被 SELinux 拦截。要么调整 SELinux 策略要么把数据目录放到/var/lib/clickhouse下。其次是firewalld的配置ClickHouse 默认监听 9000native和 8123HTTP端口需要显式放行。安装方式我推荐用官方 RPM 仓库比下载二进制包省心sudo dnf install -y clickhouse-server clickhouse-client sudo systemctl enable clickhouse-server sudo systemctl start clickhouse-server启动后第一件事是改config.xml里的max_memory_usage和max_threads默认值对生产环境来说太保守了。5. 调度与监控让管道自己跑起来5.1 调度方案的选择调度这块轻量场景用crontab 脚本就够了但一旦任务多了、依赖复杂了就必须上专业工具。我推荐Airflow虽然它有点重但 DAG 的依赖管理、重试机制、可视化界面都是刚需。一个典型的电商采集 DAG 长这样fetch_product_list fetch_product_detail parse_and_clean load_to_db quality_check每个任务失败可以单独重试不会影响已经成功的部分。Airflow 的catchup和backfill功能也很有用比如某天爬虫挂了可以补跑那天的任务。如果觉得 Airflow 太重Prefect或Dagster是更现代的选择API 更友好部署更简单。我最近的项目在用 Prefect体验不错。5.2 监控指标与告警监控我分三个层面采集层请求成功率、平均响应时间、重试次数、被封禁次数。这些指标 Scrapy 自带统计通过StatsCollector导出到 Prometheus 即可。数据层每批次入库记录数、字段空值率、数据延迟最新数据的时间戳与当前时间的差。这些需要在 Pipeline 里埋点。系统层CPU、内存、磁盘、网络。用 node_exporter 采集Grafana 展示。告警规则我一般设这几条请求成功率低于 90% 持续 5 分钟、数据延迟超过 2 小时、磁盘使用率超过 85%。告警渠道用企业微信或钉钉机器人简单直接。5.3 故障自愈设计管道跑久了故障是常态。我的设计原则是能自动恢复的绝不人工介入网络抖动Scrapy 自动重试配合指数退避页面结构变更解析失败率超阈值时自动切换到备用解析规则如果有同时告警数据库连接断开连接池自动重连写入失败进入本地队列恢复后补写浏览器实例泄漏定时任务扫描并清理僵尸进程这套机制下来大部分故障都能自愈运维只需要处理真正需要人工判断的问题。6. 常见问题与排查技巧实录6.1 采集层常见问题问题一Scrapy 跑着跑着内存暴涨这是最常见的问题90% 的情况是 Playwright 的 page 没关闭。解决方法是给playwright_include_pageTrue的请求在回调里务必await page.close()。另外可以在 settings 里设置PLAYWRIGHT_MAX_PAGES_PER_CONTEXT限制单上下文页面数。问题二请求全部返回 403先检查是不是 UA 被识别了换个 UA 池试试。如果还不行可能是 IP 被限了需要上代理。还有一种可能是请求头缺少关键字段比如 Referer、Accept-Language用浏览器开发者工具对比一下正常请求和你的请求差异。问题三动态内容抓不到先确认页面是不是真的需要 JS 渲染。用curl请求一下如果返回的 HTML 里就有数据那说明是静态的不需要 Playwright。如果确实是动态的检查wait_for_selector是否等到了正确的元素有时候需要等网络空闲wait_untilnetworkidle。6.2 存储层常见问题问题四ClickHouse 写入报 Too many parts这是批量写入太频繁导致的ClickHouse 每个分区建议不要超过 300 个 part。解决方法是增大批次大小、降低写入频率或者调整parts_to_throw_insert参数治标不治本。问题五PostgreSQL 连接池耗尽Scrapy 是异步的但 psycopg2 是同步的如果直接在 Pipeline 里用同步连接高并发下连接池很快耗尽。解决方案是用psycopg3的异步接口或者用asyncpg配合 Scrapy 的异步 Pipeline。6.3 排查速查表现象可能原因排查方向抓取速度突然变慢被限速 / 代理质量差看响应时间分布换代理测试数据大量为空页面改版 / 解析规则失效对比新旧 HTML 结构数据库写入失败连接断开 / 字段类型不匹配看数据库日志检查 schema任务卡住不动死锁 / 等待超时看进程栈检查锁和超时配置内存持续增长对象未释放 / 缓存无上限用 memory_profiler 定位6.4 几条血泪经验第一永远不要在生产环境直接调试。我早期犯过这个错改一行代码重启一次结果把正在跑的任务全打断了。正确做法是本地复现问题改好测试通过再上线。第二日志要打够但别打太多。关键节点请求开始、解析完成、入库成功必须打日志但不要在循环里打 debug 日志不然日志文件一天能涨几十 G。第三配置和代码分离。反爬参数、数据库连接、并发数这些全部放配置文件或环境变量改配置不用改代码、不用重新部署。第四做好数据备份。ClickHouse 的数据虽然可以从源头重新抓但成本很高。定期备份关键表尤其是那些历史数据。这套系统我从最早的脚本一路演进过来中间推翻重做了好几次。现在回头看最大的体会是采集系统的核心竞争力不在抓取本身而在于稳定性和数据质量。抓取技术网上教程一大堆但怎么让系统连续跑一年不出大问题、怎么保证数据干净可用这些才是真正拉开差距的地方。如果你正在做类似的项目建议一开始就把架构分层做好别图快把所有逻辑塞一起后面维护的时候你会感谢当初的自己。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询