速卖通商品数据爬取全攻略:Python选品调研自动化
简介面向跨境电商数据分析与爬虫学习场景这份源码包提供了一套速卖通数据采集的完整实现。工具通过自动化浏览器登录获取登录凭证支持多线程爬取、自定义关键词与页面数并在图形界面中展示结果及导出表格文件能有效解决手动收集商品数据效率低、难以批量处理的问题。压缩包共六个文件三个核心脚本分别承担控制逻辑、登录与爬取执行、图形界面交互另含依赖清单及忽略规则等配置文件整体仅11KB结构轻量便于阅读和二次改造。目前已有104人学习适合想入门爬虫与界面结合开发的中初级开发者也适合有速卖通选品分析需求的运营人员参考。源码模块划分清晰使用者可直接运行或扩展从中学习登录凭证维护、多线程任务调度及数据导出的工程化写法。 这套工具是我在去年做跨境选品调研时被逼出来的产物。当时需要大量分析速卖通上同行产品的销量趋势、价格带分布和评价关键词每天手动复制粘贴到Excel里效率低到怀疑人生而且数据一多就乱。后来索性用Python写了一套自动抓取速卖通商品数据的脚本从搜索结果页到商品详情再到评价信息基本覆盖了选品调研的核心数据需求。今天把这套工具的完整实现思路和关键源码拆出来给同样在折腾跨境数据的朋友一条明路。先说清楚这套东西能干什么输入关键词自动抓取速卖通搜索结果里的商品标题、价格、月销量、店铺名、链接再点进详情页把评价数、评分、卖家信息拉下来最终落成一份结构化的CSV表格。整个流程全自动跑一次几百条数据比手动效率高很多。代码我整理过新手拿来改改关键词就能用有一定基础的还能照着这个思路扩展成定时监控或竞品追踪。1. 项目整体设计与技术选型1.1 核心需求解析动手写代码之前先把需求拆明白。当时我给自己定的目标是输入一个搜索关键词程序自动完成“搜索结果采集 → 商品链接提取 → 详情页信息补全 → 数据落盘”这一整条链路中间不需要人为干预。拆解下来核心需求就是下面这张表里列的需求模块说明优先级搜索结果采集输入关键词抓取搜索结果列表页中的商品卡片信息必须详情页补全进入每个商品的详情页补抓评价数、评分、卖家类型必须数据落盘统一输出为CSV文件字段要规整必须断点续跑部分商品抓取失败时记录日志再次运行只补抓失败项建议频率控制限制请求频率避免对目标站点造成压力必须这些需求看着简单真正落地时才发现坑一个接一个。速卖通的页面是典型的前后端分离结构网页HTML里只有初始骨架真正的商品数据都是通过接口异步返回的。这就意味着传统的“requests拿HTML BeautifulSoup解析DOM”策略直接失效必须先定位接口。1.2 技术选型技术栈我选的是Python理由很简单生态全、上手快爬虫这块的轮子基本都齐了。核心依赖就四样requests发送HTTP请求拿接口数据json解析接口返回的JSON结构标准库不需要装csv数据落盘标准库time/random控制请求间隔模拟人工操作节奏没有用Scrapy因为这套工具规模不大用不着上分布式爬虫框架requests一把梭反而更轻量。后续要扩展成多关键词批量跑无非就是加层循环框架化反而碍手碍脚。这里补充一点关于Pycharm和VS Code的选择如果你平时有本地Python开发环境直接开干如果没有装个Anaconda然后配Pycharm社区版十分钟就能跑起来。代码不要用Jupyter Notebook跑长任务跑起来容易断我吃过亏。2. 速卖通数据爬取的关键技术点拆解2.1 接口定位与分析拿到速卖通搜索结果页第一步不是急着写代码而是先把页面和接口结构摸清楚。我用的方法是打开浏览器无痕模式进入速卖通搜索页按F12打开开发者工具切到Network面板勾选Fetch/XHR过滤然后手动搜索一个商品关键词观察页面刷出了哪些异步请求。实际观察下来搜索结果页的数据核心是search?keywordsxxx这个接口。它返回的JSON结构里有个data字段里面是mods-itemList-content这样一个嵌套结构每一组对象代表一个商品卡片包含title、itemId、price、tradeDesc销量描述、sellerName、productDetailUrl这些关键信息。这个接口有意思的一点是它的参数里没有传统意义上的“页号”而是通过page和pageSize控制。也就是说你要翻页直接改这两个参数就行不用像有些平台那样还得找nextPageToken之类的游标参数省了不少事。再来是商品详情页。搜索结果列表里的信息到底还是太薄像评价数、评分这种选品核心指标得进详情页才有。详情页也是走接口接口路径大致是/item/itemdetail这样的格式后面跟一串商品ID。返回结构里data下有个priceModule和titleModule分别对应价格和标题信息评价数据则在reviewModule里。2.2 反爬机制与应对策略说到速卖通的反爬我真心觉得它的策略就是“大量速率限制少量动态参数”。没有像某些国内电商平台那样玩非常复杂的滑块验证它会优先控制你的请求频率和总量。刚开始测试时我图省事把请求间隔设成了0.5秒结果跑了不到100个商品就出现了频繁的响应体异常直接被平台限流了。应对策略上我的经验是速度和伪装两手抓请求间隔控制相邻请求至少间隔2到3秒这个值是个人经验如果触发限流就在原基础上再翻倍。Header伪装设置完整的User-Agent、Referer、Accept-Language越像真实浏览器越好。Cookie策略首次访问搜索结果页时先请求一次首页拿基础Cookie后续所有请求带上这个Cookie池。随机化在2到3秒的基础上加入随机浮动比如2.3秒到2.8秒之间随机取一个值避免节奏太规律被机器识别。这些策略合在一起配合合理的抓取规模基本能保证工具稳定运行几百条数据不翻车。3. 核心代码实现与完整源码解析3.1 项目结构代码我做了模块化看起来清爽也好维护aliexpress_spider/ ├── config.py # 配置文件存放Header、基础URL、请求间隔 ├── search_parser.py # 搜索结果解析模块 ├── detail_parser.py # 商品详情解析模块 ├── storage.py # 数据存储模块 └── main.py # 主流程入口这里把配置单独拆出来主要是一个习惯问题Header、URL、间隔时间这些高频调整的东西集中放改起来方便也避免在主逻辑里翻来翻去找魔法数字。3.2 配置模块实现# config.py import random BASE_HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36, Accept: application/json, text/plain, */*, Accept-Language: en-US,en;q0.9, Referer: https://www.aliexpress.com/, Origin: https://www.aliexpress.com, } SEARCH_URL https://www.aliexpress.com/search def get_headers(): headers BASE_HEADERS.copy() return headers def get_delay(): return round(random.uniform(2.3, 2.8), 1)BASE_HEADERS里这个User-Agent我是从自己浏览器复制的最新Chrome版本。有朋友可能会问UA是不是越新越好其实不是关键是别用那种明显的爬虫默认UA比如Python-requests/2.x这种一眼就被识别了。3.3 搜索结果解析模块# search_parser.py import requests import json import config def search_products(keyword, page1): params { keywords: keyword, page: page, pageSize: 50, } resp requests.get( config.SEARCH_URL, paramsparams, headersconfig.get_headers(), timeout10 ) if resp.status_code ! 200: print(f[搜索页] HTTP {resp.status_code}) return [] raw resp.json() products [] try: content raw[data][mods][itemList][content] except KeyError: print([搜索页] 数据结构异常可能被限流或接口升级) return [] for item in content: product { title: item.get(title, ), item_id: item.get(itemId, ), price: item.get(price, ), trade_desc: item.get(tradeDesc, ), seller_name: item.get(sellerName, ), detail_url: item.get(productDetailUrl, ), } products.append(product) return products这里有个小细节解析时我没有用item[title]而是用item.get(title, )。原因很简单搜索结果里偶尔会有广告位或其他类型的卡片字段缺失的情况时有发生。用get方法取值缺了就给个空字符串程序不会崩。3.4 商品详情解析模块# detail_parser.py import requests import json import config DETAIL_URL https://www.aliexpress.com/item/itemdetail def fetch_detail(item_id): params { itemId: item_id, } resp requests.get( DETAIL_URL, paramsparams, headersconfig.get_headers(), timeout10 ) if resp.status_code ! 200: print(f[详情页] 商品{item_id} HTTP {resp.status_code}) return {} raw resp.json() data raw.get(data, {}) price_module data.get(priceModule, {}) review_module data.get(reviewModule, {}) title_module data.get(titleModule, {}) return { item_id: item_id, detail_title: title_module.get(subject, ), final_price: price_module.get(finalPrice, ), review_count: review_module.get(totalNum, 0), rating: review_module.get(rating, 0), }详情接口返回的数据结构里评价数据在reviewModule下面但有些商品没有评价模块或者评价模块是空的get方法在这里又发挥了兜底作用。totalNum是评价总数rating是评分这两个字段是选品时判断一个商品是否值得跟款的核心指标。3.5 存储模块# storage.py import csv def save_to_csv(rows, filenamealiexpress_data.csv): if not rows: print([存储] 无数据可保存) return fieldnames [ title, item_id, price, trade_desc, seller_name, detail_url, final_price, review_count, rating, detail_title ] with open(filename, w, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnamesfieldnames) writer.writeheader() writer.writerows(rows) print(f[存储] 已保存 {len(rows)} 条数据到 {filename})编码用了utf-8-sig而不是utf-8这是个细节坑。后者生成的CSV用Excel直接打开中文会乱码而utf-8-sig加了BOM头Excel能正确识别编码这算是老爬虫的常识了。3.6 主流程入口# main.py import time import json import search_parser import detail_parser import storage import config def main(): keyword input(请输入搜索关键词: ).strip() if not keyword: print([主流程] 关键词不能为空) return total_results [] # 第一步抓取搜索结果这里先跑前3页可根据需要调整 for page in range(1, 4): print(f[主流程] 正在抓取第 {page} 页搜索结果...) products search_parser.search_products(keyword, page) if not products: print([主流程] 本页无数据提前结束) break total_results.extend(products) delay config.get_delay() print(f[主流程] 第 {page} 页抓取到 {len(products)} 个商品等待 {delay} 秒) time.sleep(delay) print(f[主流程] 共抓取 {len(total_results)} 个搜索结果开始补全详情...) # 第二步遍历每个商品补全详情 for idx, product in enumerate(total_results): item_id product[item_id] detail detail_parser.fetch_detail(item_id) product.update(detail) delay config.get_delay() print(f[主流程] 进度 {idx 1}/{len(total_results)}补全商品 {item_id}等待 {delay} 秒) time.sleep(delay) # 第三步保存数据 storage.save_to_csv(total_results) print([主流程] 全部完成) if __name__ __main__: main()这套代码跑起来输入一个关键词大概几分钟后就能拿到一份完整的CSV数据表包含商品标题、价格、销量描述、卖家名称、评价数、评分等信息。我实际用的时候一次跑3页搜索结果大概能拿到60到100个商品每个商品补全详情按2.5秒左右的间隔算整个流程大约4到6分钟属于可以接受的等待范围。4. 常见问题与排查技巧实录4.1 问题速查表现象可能原因排查思路搜索结果始终为空列表数据结构变更或触发限流打印原始raw的keys()对比接口返回是否变化接口返回HTTP 403被识别为爬虫检查User-Agent和Cookie加大请求间隔详情页补全大量为空商品ID无效或商品已下架检查itemId是否与搜索结果一致部分商品确实无评价模块CSV中文乱码编码用了utf-8改为utf-8-sig程序运行中途卡死请求超时未设置给所有requests调用加上timeout10这里挑两个遇到最多的展开说说。4.2 搜索结果空列表的深度排查这是新手最容易卡住的问题接口返回200但解析出来没有数据。问题大概率不是爬虫被封而是页面布局变了接口里的字段结构调整了。我的排查习惯是先缓存一次原始响应到本地文件再仔细检查JSON结构。比如有一次我发现itemList不见了变成了itemListNew这时候只要把解析逻辑改成新的字段名就行不算伤筋动骨。另一个可能是老接口失效平台迁移到了新接口。这个比较隐蔽建议在浏览器里重新搜一次看Network里实际走的哪个接口路径和参数用新接口替换配置里的URL即可。4.3 翻页时的页大小选择pageSize我建议设成50这是我在实际测试中验证过相对稳定的值。改成100试过接口响应速度明显变慢还容易触发限流改成20虽然更稳但翻页次数变多总耗时反而更长。50是一个平衡点单页数据量足够请求频率又不会太激进。关于页数上限我默认就是3页。真要大批量抓数据建议加个参数控制并分段跑每次抓完歇几分钟再跑下个关键词别一根筋连续跑通宵大概率第二天醒来发现所有请求都失效了。4.4 平台登录与Cookie处理这套工具最初是按未登录状态设计的因为搜索和详情接口在未登录时就能正常返回数据。但如果你需要抓的数据涉及更高的频率或更深的内容就可能要处理登录态了。注意登录功能会引入验证码、短信验证、账号风控等一系列问题个人项目里最好不要动这个念头用登录态加频繁请求账号被限制的风险很大。有个折中的办法是手动登录后在浏览器里复制完整Cookie字符串放到config.py里作为请求头带上。这种方式对低频数据抓取够用不用写自动登录代码也不触发验证码机制。5. 扩展思路与合规提醒这套工具目前定位是“选品调研辅助工具”但核心链路搭好之后扩展空间其实挺大的。比如加上定时任务每天早上自动跑一遍核心类目关键词生成一份当天的价格与销量变化报告就能从“查数据”升级成“盯数据”。再比如把CSV输出改成直接写入SQLite或MySQL数据量大了之后查询和过滤会方便得多。关于合规这里必须多说两句。爬虫工具本身是中性的但使用边界一定要注意只抓公开数据不绕过登录验证控制请求频率不干扰平台正常服务数据仅用于个人调研分析不批量复制他人原创内容用于商业发布。我自己的原则是抓下来的数据人工二次加工后才使用原始数据不转发、不售卖、不公开。6. 关于这套工具的使用心得工具写完之后我实际用了一阵子最直观的感受是做跨境选品不再靠感觉了。以前看一个类目感觉销量不错但说不出具体数字也没法横向比价。现在跑一次数据下来哪个价格带竞争最激烈哪个商品评价数断层领先一目了然节奏感完全不同。最后再分享一个很实用的小技巧如果搜索结果里有大量商品没有评价数据这类商品往往是一些刚上架的新品或者没有太多销量的展示位。如果你在做选品这类数据不用删除它们本身就是一种信号。把这些“零评价商品”单独筛出来看它们的价格和标题写作方式往往能发现平台上正在发生的新趋势。这个功能用现成的CSV在Excel里加个筛选就能实现不需要额外写代码。代码是死的思路是活的这套工具的价值不在那几百行Python而在于帮你把“看看别人卖什么”变成一项有系统、可复用的流程。希望这篇文章能帮你少走几步弯路折腾出属于自己的数据工作流。本文还有配套的精品资源点击获取