京东关键词搜索数据采集:从网页解析到接口调用实战解析

发布时间:2026/10/3 10:09:57
京东关键词搜索数据采集:从网页解析到接口调用实战解析 做电商选品、竞品价格监控、市场趋势分析的朋友大概率都干过这么一件事打开京东搜索框输入关键词一页一页翻商品把名称、价格、评价数手动记到表格里。翻两页还行翻二十页手就麻了。这其实就是个典型的重复劳动场景——同样是搜索“机械键盘”这个关键词京东返回的商品列表、价格、店铺、评价数据完全可以一次性用代码拿全。这个项目的目标很简单调用京东关键词搜索相关的几个数据接口把“搜索某关键词后返回的商品数据”完整抓下来包含商品ID、商品名称、店铺名称、当前价格、划线价、评价数、好评率等常用字段最后落成一份结构化表格供后续选品分析、价格监控或市场调研使用。适合三类人电商运营想摸底品类竞争格局的开发新手想练手反爬和接口分析的以及准备电商方向面试想拿一个完整项目做复盘的。下面我把整个项目从接口选型到实操落地拆开揉碎了讲清楚包括我踩过的坑和觉得值得留意的细节。1. 项目整体思路与接口选型1.1 为什么关键词搜索数据是所有分析的地基很多人一开始上来就想抓京东全站数据这方向其实不对。关键词搜索页是京东最重要的流量分发入口搜索一个词返回的第一页、前几页商品基本就是京东算法认为“最值得展示”的那批商品。这意味着两件事第一这些商品本身就反映了平台对这个品类的核心供给选品调研看这里最有效率第二搜索排序本身就是一种市场信号谁能排前面谁的综合表现就好。把关键词搜索接口跑通之后后面几乎所有分析都有地基了。想知道“机械键盘”这个品类里哪些店铺更强势按搜索结果的店铺字段统计一眼就能看明白想监控竞品价格波动锁定搜索结果里的SKU集合定时跑一遍价格接口历史价格曲线就出来了。所以先做好关键词搜索这一步是性价比最高的切入方式。1.2 三种获取方式的对比与选型京东商品数据获取市面上常见的有三条路PC端网页页面、移动端App接口、第三方采集软件。三条路我都走过刚入门的朋友我会建议先走第一条。方式代表接口优点缺点适合场景PC端网页search.jd.com/Search s_new.php门槛低普通requests就能拿到HTML不需要强签名数据混在HTML里解析稍麻烦高频访问会被风控个人学习、中小规模数据分析移动端App接口api.m.jd.com/api返回纯JSON字段非常规整翻页简单需要处理h5st签名新手容易被卡住有一定反爬经验后的进阶方案第三方采集软件各家商业软件零代码点几下就有数据价格不透明、字段定制麻烦、数据质量不可控不想写代码、只做一次性调研我推荐先做PC端方案核心原因是“阻力最小”。先跑通整条流程手上有一份真实数据了你再去研究移动端API和h5st签名心态完全不一样——因为你已经知道目标长什么样了折腾起来有底。直接上移动端接口的往往卡在签名上两三天业务数据一行都没见到挫败感很强。1.3 项目落地需要准备的工具工具清单不复杂Python 3.9以上、requests库、lxml库、一个代码编辑器。想整理数据的话顺便装上pandas不想装也行用csv模块就够了。另外一个容易被忽略的工具是浏览器开发者工具抓包时定位请求参数全靠它这个后面实操部分会频繁用到。环境准备好之后先别急着写代码花十分钟把京东搜索页面的数据结构弄清楚这一步比写代码更重要。2. 接口分析搞清楚数据藏在哪儿2.1 搜索主页面商品列表藏在HTML的li标签里在浏览器里打开https://search.jd.com/Search?keyword机械键盘encutf-8按F12看Network刷新页面你会看到一个名为Search?keyword机械键盘的请求。这就是搜索主页面接口。它返回的是一整份HTML不是JSON。商品数据的位置很固定在#J_goodsList这个容器下面每个商品是一个li标签li上面带了一个>headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://www.jd.com/, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.8, }把User-Agent换成当前主流Chrome版本号Referer指向京东首页基本能过常规检测。Cookie方面首次调试时可以直接拿浏览器里登录态的Cookie放进请求里成功率更高。但要注意Cookie属于个人账户凭证别把带有个人信息的Cookie提交到公共平台或仓库里。频率控制是最容易被忽略的。我见过有人写了个for循环遍历100页不设间隔跑到第20页就被盾了。合理做法是每次请求之间随机sleep 2到5秒并且加一个熔断机制连续三次请求失败就暂停5分钟再重试而不是死循环重试。3.2 h5st是什么为什么新人一上来就会被它劝退PC端页面相对宽容但如果你想用移动端APIapi.m.jd.com获取更规整的JSON数据就绕不开h5st这个签名参数。简单说h5st是京东自己的一套请求签名机制客户端在处理请求时会基于当前时间戳、随机数、部分参数内容通过一段混淆过的JavaScript算法计算出一串签名值放在请求头或参数里。服务端拿到请求后会验签签名不合法就直接拒绝。新人劝退点在于第一生成签名的JS代码是压缩混淆过的很难直接读懂第二签名算法会不定期更新你今天逆向出来明天可能就废了第三就算签名对了短时间内高频请求还会触发频率风控。所以很多教程写移动端接口但读者实践时总在签名环节折戟。3.3 应对h5st的三种实践方案我在项目演进过程中试过三种思路各有取舍。第一种是无头浏览器RPC方案也是我最推荐的起步方案。思路是让Playwright或Selenium在本地打开一个真实的京东页面页面自己会加载并执行完整的签名JS逻辑然后我们通过浏览器调试协议直接调用页面上下文里已经可用的签名函数来给请求签名。这样做不用逆向算法维护成本低。代价是每个请求都要带一个真实浏览器实例速度慢、内存占用高适合中小规模采集。第二种是纯JS算法还原方案。把混淆JS下载下来分析算法流程在Node.js环境里复现生成逻辑。这个方案速度快、适合规模化但维护成本很高京东更新一次算法就要跟着改一次代码。没有持续投入的打算不建议一个人死磕这条路。第三种是直接绕开。既然PC端页面暂时不需要处理h5st那就先用PC端方案拿到80%的数据把业务先跑起来。等确实需要移动端那部分字段时再回头处理签名问题。很多做数据分析的朋友最后发现PC端数据已经足够用压根不用碰h5st。方案优点缺点推荐度浏览器RPC不用逆算法最稳定慢、占内存新手首选Node.js算法还原快、可规模化维护成本高进阶备选绕开走PC端零签名成本字段没那么规整务实之选这里有一个核心经验不要为了炫技去碰签名要先看业务到底需要什么数据。我见过太多人花了两周时间破解h5st最后发现他们想要的字段PC端全都有。4. 实操流程从零到拿到第一份数据4.1 环境准备与首次抓包定位开始写代码前先用浏览器手动搜索一次目标关键词比如“机械键盘”确认页面能正常打开。这一步很重要目的是确认你当前的网络环境本身没有问题。如果浏览器都出验证码或滑块那代码怎么写都没用先解决IP或Cookie的问题再说。环境安装一条命令就够pip install requests lxml pandas然后在浏览器开发者工具里找到Search请求把请求头和关键参数记下来这就够了。4.2 第一步商品列表采集与解析先实现一个抓取搜索页并解析商品列表的函数。核心逻辑是请求HTML然后用XPath定位到每个商品的li节点提取>import requests import time from lxml import etree session requests.Session() session.headers.update({ User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36, Referer: https://www.jd.com/, }) def fetch_search_page(keyword, page1): url https://search.jd.com/Search params { keyword: keyword, enc: utf-8, page: page, } resp session.get(url, paramsparams, timeout15) resp.encoding utf-8 return resp.text def parse_search_page(html): tree etree.HTML(html) items [] # 列表容器是 ul.gl-warp也有部分页面是 div.gl-warp for li in tree.xpath(//ul[contains(class,gl-warp)]/li): sku_id li.get(data-sku) or li.get(data-pid, ) if not sku_id: continue name .join(li.xpath(.//div[contains(class,p-name)]//a//text())).strip() if not name: name .join(li.xpath(.//div[contains(class,p-name)]//em//text())).strip() shop .join(li.xpath(.//div[contains(class,p-shop)]//a//text())).strip() shop shop or 自营 items.append({sku_id: sku_id, name: name, shop: shop}) return items html fetch_search_page(机械键盘, page1) items parse_search_page(html) for item in items[:5]: print(item)这里几个细节要注意名称XPath取的是p-name下a标签的文本但有些模板里a下面还有em所以加了兜底逻辑。店铺名取不到时默认填“自营”因为很多京东自营商品在列表页确实不展示店铺名。li.get(data-sku) or li.get(data-pid, )这个写法的意思是优先取>def fetch_prices(sku_ids): url https://p.3.cn/prices/mgets params { skuIds: ,.join(J_ str(sid) for sid in sku_ids), pduid: str(int(time.time() * 1000)), } resp requests.get(url, paramsparams, timeout10) return resp.json() def fetch_comments(sku_ids): url https://club.jd.com/comment/productCommentSummaries.action params {referenceIds: ,.join(str(sid) for sid in sku_ids)} resp requests.get(url, paramsparams, timeout10) return resp.json()价格接口返回的字段里p是销售价op是划线价m是Plus价。需要注意的是有些无货或缺货商品可能不返回价格所以解析时要做容错def merge_price_info(items, price_data): price_map {} for item in price_data: sku_id item[id].replace(J_, ) price_map[sku_id] item for it in items: info price_map.get(it[sku_id], {}) it[price] info.get(p, ) it[original_price] info.get(op, ) it[plus_price] info.get(m, ) return items评论摘要接口同理返回数组的字段名是SkuId、CommentCount、GoodRate可以用SkuId做key拼到字典里。4.4 第三步翻页采集与数据合并翻页逻辑和第一页不同第二页起改用s_new.php接口。我封装了一个兼容处理函数先判断返回内容是不是JSON——这个判断很关键因为s_new.php的返回格式在不同时期不一样有时候是纯HTML有时候是JSON字符串。def fetch_search_new(keyword, page2): url https://search.jd.com/s_new.php params { keyword: keyword, enc: utf-8, page: page, scrolling: y, s: 1, click: 0, log: , } resp session.get(url, paramsparams, timeout15) resp.encoding utf-8 text resp.text.strip() if text.startswith({): import json data json.loads(text) # 有的版本返回 {data: HTML字符串} if isinstance(data, dict) and data in data: return data[data] return text return text请求到第二页内容后解析逻辑可以复用parse_search_page。有一个经验要分享把第一页和第二页返回的商品对比一下你会发现京东的搜索列表本身就有重复商品这是正常的。最终合并数据时按sku_id去重即可不用纠结。完整采集的循环逻辑大致是这样all_items [] for page in range(1, total_pages 1): if page 1: html fetch_search_page(keyword, page1) else: html fetch_search_new(keyword, pagepage) items parse_search_page(html) all_items.extend(items) print(f第{page}页累计{len(all_items)}个商品) time.sleep(random.uniform(2, 5))4.5 第四步数据落库与定时任务数据合并去重后输出成CSV。注意编码用utf-8-sig不然用Excel打开会乱码。import pandas as pd df pd.DataFrame(all_items) df df.drop_duplicates(subset[sku_id]).reset_index(dropTrue) df.to_csv(jd_keyword_机械键盘.csv, indexFalse, encodingutf-8-sig) print(f保存完成共{len(df)}条商品数据)如果想做成定时任务最简单的方案是本地crontab或者用一个叫青龙面板的开源定时任务工具。很多人以为青龙面板只能跑京东签到的脚本实际上它就是通用的定时任务管理器把这套采集脚本丢进去设置好cron表达式就能实现每天定时采集。注意青龙面板所在环境一般比较精简如果不装pandas直接用csv模块写文件会更轻量我在服务器上跑采集时就是这么干的。5. 高频问题与排查实录5.1 请求被风控出现302重定向或验证码最常见的报错不是错误码而是请求结果被302重定向到了验证码页面。判断方法很简单打印resp.url如果发现URL不再是以search.jd.com开头基本就是被风控了。我踩过几次之后的处理经验是第一步停手不继续请求等5到10分钟第二步换Cookie重新从浏览器复制一份新的第三步检查请求频率把sleep间隔拉大。如果还是不行再考虑换IP或加代理。优先做前两步大多数个人开发者场景都够用了。5.2 价格接口返回空数组p.3.cn/prices/mgets有个很坑的点参数格式必须严格带J_前缀漏掉任何一个这批全部返回空。另外批量请求时如果其中有个商品ID无效可能整个返回都是空数组但之前单独测试又是好的。排查思路是先只传一个SKU测试单个接口通了再放量。有时候价格接口返回数据了但p字段是-1或0这通常是商品已下架或无货采集的时候保留原始值就行不用特别处理后面分析时过滤掉即可。5.3 翻页后数据大量重复或抓不全如果第二页返回的内容和第一页一模一样基本可以确定是s_new.php的参数没带全。重点检查scrolling和s这两个参数scrolling要等于ys在列表页一般是页码相关参数我默认传1基本可用。还有一种情况是page参数从1开始请求导致第一页数据被重复抓两次。记得第一页走Search第二页开始走s_new.php两者不要混用。5.4 移动端接口签名失效的判断方法如果你最终还是去折腾移动端API了这里给一个判断是不是h5st问题的标准请求返回JSON里出现code:501或msg:invalid sign这类字样基本就是签名失效或签名算法对不上。对策只有三个方向换最新签名算法、改用浏览器RPC方案、放弃移动端接口回到PC端。我个人建议后两个性价比更高。5.5 字段缺失店铺名为空或名称为空列表页解析时遇到字段缺失别慌先看单个li的HTML长什么样再调整XPath。我在不同时间抓过京东发现它的p-name和p-shop结构微调过好几次。这也是为什么我强调抓包分析和HTML结构观察是基本功。代码里做好兜底处理缺失字段填空字符串别让程序崩掉。现象可能原因处理方式302到验证码页频率过高/Cookie失效降频、换Cookie、冷静几分钟价格接口返回[]J_前缀缺失/单个ID无效单测后再批量严格格式第二页数据同第一页翻页参数不全补scrollingy、s等参数移动端API报501h5st签名失效换RPC方案或退回PC端店铺名空自营商品/模板变化默认填“自营”或手动看HTML6. 数据合规与后续扩展6.1 合规使用这是底线不是套话做数据采集这件事最后一定要回头审视合规问题。京东的robots协议对爬虫抓取并不鼓励个人学习研究自用是一回事如果要做成商业化产品或大规模采集必须先评估法律风险和数据使用边界。我在项目文档里会提醒自己几条原则控制请求频率不给目标站点制造压力只采集公开展示的数据不碰用户隐私字段数据仅用于个人学习或内部趋势分析不对外提供原始数据集。另外要特别提醒请求时如果带了自己的登录态Cookie要注意凭证安全不要把它硬编码进公开脚本或提交到代码仓库万一泄露被刷号就麻烦了。6.2 后续扩展方向从数据到洞察拿到商品列表数据只是个开始。可以继续做几件有价值的事把每天抓到的价格存成历史表画出商品价格曲线做降价提醒把评价数和好评率跟价格做交叉分析找出性价比商品把店铺字段聚合起来分析某个品类里店铺集中度。这些方向都不需要再碰复杂接口用现有数据就够做了。京东这套接口分析思路其实可以平移到其他电商平台。淘宝、拼多多虽然接口和签名机制不同但“列表页 价格接口 评价接口”的拼装逻辑大同小异。做过一遍京东再去面对其他平台心里就有底了。如果是准备面试这个项目也可以作为完整的项目复盘素材能讲清楚接口选型、反爬应对、数据清洗整条链路比单纯说“我会爬虫”有说服力得多。最后再说一个我个人的体会这个项目最值钱的部分不是那几十行采集代码而是做完之后你对“页面数据从哪来”的理解有了质的提升。你看到任何一个网页的时候脑子里会自动拆解它背后有哪些接口、哪些字段、哪些是渲染出来的、哪些是异步加载的。这种能力是会迁移的以后遇到再复杂的采集需求思路都是通的。还有一个实操小技巧写完采集脚本先在浏览器无痕模式下手动搜一次关键词确认自己能正常访问再让代码上这样能避开很多“代码没问题但被盾”的尴尬。