资讯动态

Selenium爬取京东商品信息:反爬应对与采集实践

发布时间:2026/10/26 8:39:01 来源:尧图企业网站定制
简介基于Selenium抓取京东商品信息的Python爬虫脚本包面向有Python基础、希望攻克动态渲染页面数据获取的爬虫学习者与数据分析从业者。压缩包共2个文件包含一个py脚本和一个txt数据文件包体仅2KB内容精炼。脚本演示了Selenium WebDriver的完整使用流程从创建Chrome浏览器驱动、get()访问商品页面到通过CSS选择器提取商品名称与价格再借WebDriverWait显式等待动态加载的价签元素同时涵盖分页按钮识别与点击、time.sleep()延时节流、随机User-Agent及代理IP等反反爬策略txt文件用于存储采集结果。这份小规模代码将Selenium处理动态页面、模拟用户交互、规避封禁的关键环节串联成可运行范例便于快速上手。目前已有245人学习下载可作为自主扩展多页抓取、数据清洗及电商竞品分析的基础模板。1. selenium 爬取京东商品信息面对的不只是代码还有反爬做电商数据采集的工程师基本都绕不过这个需求——把京东商品的价格、标题、评论数结构化落库。标题里带上了selenium 爬取京东商品信息.zip说明这是一份打包好的方案里面不外乎是 Python 脚本、ChromeDriver 驱动、配置文件和结果数据。但真上手后你会发现selenium 跑京东和跑静态网页完全是两个难度京东的 DOM 结构复杂、接口加密、页面埋了各种检测脚本稍微写得不稳脚本跑到一半就卡死或跳滑块。这不是 selenium 本身的问题而是你没有把浏览器行为伪装到“像真人”的程度。我能说的是selenium 能做的事情是模拟一个完整的浏览器环境对京东这种偏重前端渲染的电商页面来说反而是比 Requests 直接抓接口更稳的路线代价是慢和吃资源。适合的场景是商品数量不大几百到几千条、需要登录态、需要绕过简单的 JS 渲染校验。本文会从驱动配置、页面定位、分页遍历、风控应对四个层面把一份可以直接跑的采集脚本拆到你愿意动手照着做为止。2. 把 selenium 跑通京东商品采集的最小环境依赖、驱动和启动参数2.1 Chrome 与 ChromeDriver 的版本对齐是第一道坎常见做法是先用 pip 装好selenium本体然后去下载匹配本机 Chrome 的 ChromeDriver。这里有个血泪经验Chrome 自动更新之后ChromeDriver 版本不匹配脚本保准报SessionNotCreatedException。你用 selenium 4.6 以上可以用 Selenium Manager 自动下载驱动但国内网络环境下经常拉不动还是手动解压放到指定目录最省事。我一般会把驱动放在项目目录的drivers/下面然后在代码里显式指定路径避免污染系统 PATHfrom selenium import webdriver from selenium.webdriver.chrome.service import Service # 指定本地 ChromeDriver 路径版本号必须与 chrome 完全一致 service Service(executable_pathdrivers/chromedriver) options webdriver.ChromeOptions() driver webdriver.Chrome(serviceservice, optionsoptions)这段代码的逻辑很直接Service负责管理驱动进程的生命周期ChromeOptions控制浏览器启动参数。如果你没有指定路径selenium 4 会自动去找chromedriver这个名字的可执行文件找不到就会抛WebDriverException。参数说明里有两个我必用的项--headlessnew是开启无头模式服务器上跑的时候能省掉显示资源--disable-gpu在某些 Linux 环境不写会白屏。但注意无头模式容易被 JS 检测到如果发现京东在无头模式下返回空页面就改成有头模式跑一次看看逻辑对不对。2.2 启动参数里藏着反爬的第一道防线京东的前端脚本会检查navigator.webdriver这个标志位。默认情况下selenium 启动的 Chrome这个值是true而真实浏览器里是undefined。很多采集脚本死在这里而不自知页面能打开但商品列表是空的或者关键字段一直加载不出来。options.add_argument(--disable-blink-featuresAutomationControlled) options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_experimental_option(useAutomationExtension, False)这三行是绕过 WebDriver 检测的最基本配置。第一行是关闭 Blink 内核的自动化控制特性第二行是去掉 Chrome 顶部的“正在受自动化软件控制”提示条第三行是禁用自动化扩展。我见过很多人只加第一行就以为万事大吉结果对方脚本升级检测策略后立刻翻车。要做得更彻底还需要在页面加载后手动执行 JS把navigator.webdriver重写成undefined这个放到后面避坑章节再展开。2.3 打开京东搜索页并等待商品列表出现的标准姿势启动浏览器后第一个实际操作是get()打开链接。京东商品搜索页的 URL 规则很固定核心是keyword参数编码后拼进去即可。这里我建议直接按关键词搜索而不是从首页点进去省掉很多点击步骤和不可控的弹窗干扰。from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC driver.get(https://search.jd.com/Search?keyword quote(机械键盘)) wait WebDriverWait(driver, 10) wait.until(EC.presence_of_element_located((By.CSS_SELECTOR, #J_goodsList li.gl-item)))逻辑说明WebDriverWait不是干等而是每 0.5 秒轮询一次页面状态直到满足条件或超时。presence_of_element_located只要 DOM 里出现节点就算通过不要求可见。这一手可以避开sleep(5)这种笨办法——加载快的时候省时间加载慢或者网卡的时候又不会因为等待不足而报错。参数说明里要注意的是timeout10如果机器配置差或者网络不稳定建议放大到 15~20 秒如果页面结构改版导致选择器失效这里会直接抛TimeoutException错误信息里会带上当前页面 URL 和页面源码片段排查时很有用。3. 用 CSS 选择器精准切入京东商品 DOM字段提取与页面结构分析3.1 京东搜索页的商品列表结构拆解京东的搜索页商品列表容器是#J_goodsList每个商品项是li.gl-item。在这个列表里你能拿到的核心字段包括商品标题、价格、店铺名、评论数、商品链接。这些信息分布在几个不同的 DOM 层级里直接按整块li去提取再拆分是最稳的思路。先看下面这段取数脚本items driver.find_elements(By.CSS_SELECTOR, #J_goodsList li.gl-item) for item in items: title item.find_element(By.CSS_SELECTOR, .p-name em).text link item.find_element(By.CSS_SELECTOR, .p-name a).get_attribute(href) shop item.find_element(By.CSS_SELECTOR, .p-shop a).text comment item.find_element(By.CSS_SELECTOR, .p-commit strong).text # 价格在 .p-price 里但可能因为动态加载为空 price_text item.find_element(By.CSS_SELECTOR, .p-price i).text这段代码的逻辑是先锁定所有商品li节点再分别取子节点里的文本和属性。.p-name em是商品标题的标准位置.p-name a的href就是商品详情页链接.p-shop a是店铺名称.p-commit strong是评论数。这里有个细节价格i标签越来越依赖动态渲染搜索页上经常是空的需要到详情页或者换个节点去拿后面有一节专门说这个。参数体会find_elements返回的是列表哪怕只有一个元素也有一个find_element如果没找到会抛NoSuchElementException所以在循环里要小心建议用try/except兜底或者封装一个safe_text()函数。3.2 为什么 CSS 选择器比 XPath 更好维护很多人习惯用 XPath因为浏览器开发者工具里右键 copy 出来就是 XPath。但京东前端的 DOM 结构变动频繁XPath 里的span[1]、div[2]这种纯下标路径稍微插入一个节点就废了。CSS 选择器更关注 class 和 id容错性明显好一些。# 用 XPath 复制出来的原始路径又长又容易碎 # //*[idJ_goodsList]/ul/li[1]/div/div[2]/div[3]/a # 换成 CSS # #J_goodsList li.gl-item .p-shop a像li.gl-item这种写法只要商品项的 class 不变列表里多一个少一个 div 都不影响定位。这是我在京东采集上踩过最深的一个坑某次页面改版商品链接从第 3 个 div 挪到了第 4 个 divXPath 全部失效换成 CSS 后一次通过。3.3 延迟加载与滚动触发拿不到商品字段时先滚一屏京东商品列表是分两段加载的首屏直接渲染往下滚动时用 JS 追加第二屏和第三屏。如果你只是get()之后立刻抓最多拿到 30 个商品后面的节点还没进 DOM。常见做法是模拟滚动强制触发加载driver.execute_script(window.scrollTo(0, document.body.scrollHeight);) time.sleep(2) driver.execute_script(window.scrollTo(0, 0);)逻辑说明滚到底再回到顶部能让页面把懒加载的商品列表补全然后再执行一次商品提取。这里为什么要用time.sleep(2)而不是WebDriverWait因为懒加载没有明确的完成状态很难等一个稳定的 DOM 节点给一个保守的等待时间反而是最可靠的。参数上scrollHeight是当前页面总高度如果商品很多一次滚动可能不够我习惯循环滚两到三次。另外注意无头模式下窗口默认为 800x600滚动判定会和有头模式有差异建议设置--window-size1920,1080。4. 翻页遍历与行为模拟从第一页到最后一页的完整循环4.1 京东翻页用的不是点击而是 URL 参数京东搜索页的翻页是通过page参数控制的URL 长这样https://search.jd.com/Search?keyword机械键盘page1翻页改数字就行。不是说不能点击页面的“下一页”按钮而是直接改 URL 更稳定——不用等待 JS 绑定的事件不用管按钮的 disabled 状态失败率低得多。一个完整的翻页循环写成这样def fetch_search_page(driver, keyword, page): url fhttps://search.jd.com/Search?keyword{quote(keyword)}page{page} driver.get(url) try: WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CSS_SELECTOR, #J_goodsList li.gl-item)) ) except TimeoutException: return [] # 页面异常直接返回空列表 # 滚动触发懒加载 for _ in range(2): driver.execute_script(window.scrollTo(0, document.body.scrollHeight);) time.sleep(1.5) return extract_items(driver)这里用page循环控制总页数常见做法是for page in range(1, max_page 1)。需要注意京东搜索页的翻页参数在第三页之后会有个规律变化前两页是page1和page2第三页实际要写page3第四页page5……这样做是为了防爬故意错开页码。我是在连续抓取时发现数据重复才意识到这个问题的。4.2 行为模拟请求之间加延时和随机化你在爬虫里最忌讳的是“机器节奏”——每页固定间隔、无抖动、无鼠标轨迹。京东的服务端会统计同一 IP 的访问频次固定间隔反而更容易触发风控。我一般这样写延时import random import time # 每次翻页后休眠 3~6 秒随机值 time.sleep(random.uniform(3, 6))逻辑说明random.uniform(3, 6)返回 3 到 6 之间的浮点数比time.sleep(5)的定值更接近人手操作节奏。如果你还开了有头模式可以在等待期间调用driver.execute_script滚动页面或移动鼠标来增加行为多样性。这些看起来是玄学但它们确实降低了被识别为爬虫的概率。参数设计要结合总量来决定如果采集 10 页每页延时 5 秒总耗时不到 1 分钟如果采集 100 页吃不住就得上代理池和多线程。但多线程配 selenium 是另一个量级的问题——每个线程一个独立 driver 实例内存会飙高我建议初版先单线程跑通再考虑并发。4.3 页面异常与空列表的兜底策略翻页过程中会遇到不稳定的情况网络抖动导致页面加载了一半或者触发滑块导致列表只剩 1~2 个商品。这时候如果你直接find_elements拿到的不完整数据会污染最终的 CSV。我习惯在extract_items()里做一次长度校验def extract_items(driver): items driver.find_elements(By.CSS_SELECTOR, #J_goodsList li.gl-item) # 商品数远低于期望值时视为异常页 if len(items) 10: return None results [] for item in items: # 解析逻辑略 results.append(data) return resultsNone和空列表[]在后续逻辑里要区分处理None表示这一页失效我一般会刷新重试一次[]表示这一页确实没有商品直接跳过。这里体现了一个重要的容错设计让每一页的采集结果都带上明确的返回状态而不是让异常静默吞掉否则最终数据少了几百条你都不知道丢在哪一页。5. 京东采集避坑指南登录弹窗、滑块验证、WebDriver 特征检测的 3 个典型现场5.1 登录弹窗挡在列表与数据之间页面看似正常却无法操作现象脚本跑着跑着商品列表消失了页面右上角出现“扫码登录”find_elements返回空。原因京东对高频访问的用户触发登录墙。触发点通常是快速翻页、请求密度过高或 IP 段被标记。解决两个方向。第一降低翻页速度和单轮总请求量把每次翻页后的延时从 3~6 秒拉到 8~10 秒第二在脚本启动时通过add_cookie()注入提前登录好的 Cookie。Cookie 怎么拿你有头模式下手动登录一次京东然后执行driver.get_cookies()序列化保存到本地cookies.json下次启动时加载进去就行。import json # 提前保存 # with open(cookies.json, w) as f: json.dump(driver.get_cookies(), f) # 启动后加载 with open(cookies.json, r) as f: cookies json.load(f) for c in cookies: driver.add_cookie(c)注意add_cookie()必须在driver.get()打开域名之后才能调用否则会报InvalidCookieDomainException。而且 Cookie 里的登录态有效期一般只有几天过期后重新导一次就成了不用每次都扫码。5.2 滑块验证点击和拖拽硬过一定会暴露现象页面中间浮出一个滑块组件要求“拖动滑块完成拼图”。selenium 的ActionChains拖拽一旦加速比不匹配滑块就无限重置。原因滑块的轨迹检测已经用上了贝塞尔曲线匹配普通直线拖拽兔耳朵特别明显。解决我的经验是不要硬过而是绕开触发条件。滑块出现大多与 IP 在短时间内的请求频率有关换一个干净的住宅代理比研究轨迹算法成本低得多。如果只是偶尔触发就手动滑一次再继续跑脚本里捕获到滑块节点存在时用input()暂停等人工介入。另外现在很多电商风控是不直接弹滑块了转而把价格数据放到加密接口里页面显示暂无报价。遇到这种字段缺失再回头去看Requests直连接口的路子能拿到但需要逆向sign参数不是本文范围。5.3 WebDriver 特征被识别数据加载正常就是取不到价格节点现象搜索页标题评论都抓到了唯独.p-price i永远为空字符串。手动打开浏览器看是正常的数值明明白白在页面上。原因京东的价格是异步接口渲染的服务端在返回 HTML 时先判断了navigator.webdriver。一旦判定是 WebDriver接口不下发真实价格。解决除了前面提到的excludeSwitches还要在页面初始脚本里提前做一次性覆写。options.add_argument(--disable-blink-featuresAutomationControlled) driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, {get: () undefined}) })execute_cdp_cmd是 Chrome DevTools 协议的执行入口Page.addScriptToEvaluateOnNewDocument会在每次页面导航前执行指定 JS。这个方案的优先级极高在页面任何脚本运行之前就把webdriver属性改掉比事后execute_script再改更可靠。这是我在反复翻车后验证出来的最有效手法建议直接写进你的启动配置里。价格拿不到的另一个姿势是用手机版页面——京东触屏版https://so.m.jd.com/ware/search.action?keyword机械键盘的接口返回简化 HTML价格直接写在节点里但需要注意移动端的 DOM 结构跟 PC 端完全不一样定位要重写。一般我保留两套解析逻辑PC 版挂了切移动版互为备用。5.4 内存溢出与僵尸 Chrome 进程长时间采集中跑着跑着机器就卡死了现象脚本跑到第 20 页时突然变慢页面加载不出来最终ChromeDriver报Timed out或者干脆无响应。原因每get()一次页面旧页面的资源并不会完全释放无头模式下的渲染进程积累占用大量内存。解决定期重启 driver 是常见做法。每采集 10 页调用driver.quit()关闭当前浏览器实例然后重建一个 driver 继续跑。注意重建后要重新加载 Cookie代码上封装一个create_driver()函数来复用。def create_driver(): options webdriver.ChromeOptions() # ... 你的启动参数 d webdriver.Chrome(serviceservice, optionsoptions) # 加载 cookies return d for batch in range(0, max_page, 10): driver create_driver() for page in range(batch 1, min(batch 11, max_page 1)): # 采集逻辑 pass driver.quit()分段重启既是资源管理也是容错设计——就算前 10 页里把登录态跑挂了后 10 页还能用新会话接上不至于全军覆没。6. 进阶把脚本跑成隔夜任务的两个实用技巧——Cookie 复用与结果落盘当你把采集逻辑从“跑通一次”升级到“稳定跑一夜”有两个容易被忽视的点直接决定成败。第一是登录态的保活第二是数据的增量写入而不是一次性覆盖。这里分享我常用的做法。Cookie 复用不能只做一次加载。Cookie 里的pt_key和pt_token在你长时间运行时会过期你会发现凌晨三点脚本突然批量返回空列表。我的习惯是每次新建 driver 后先用driver.get(https://www.jd.com)打开首页再driver.add_cookie()然后手动访问一个登录后才能看的页面验证是否仍处于登录态。如果验证失败立即用企业微信机器人或者钉钉机器人给自己发条告警避免人睡了脚本还在空转。数据落盘上我不建议“跑完全部页数再一次性写 CSV”中间任何一步崩了就是全丢。更稳的是边采集边写配合追加写入模式import csv import os file_path jd_keyboard.csv with open(file_path, a, newline, encodingutf-8-sig) as f: writer csv.DictWriter(f, fieldnames[title, link, shop, comment, price]) if os.path.getsize(file_path) 0: writer.writeheader() # 空文件才写表头避免重复 for item in page_data: writer.writerow(item)这里的utf-8-sig是为了让 CSV 在 Excel 里打开不乱码fieldnames必须和字典的键完全一致否则writerow会静默写成空值。追加模式下分页脚本可以放心断点续跑——把上次跑到的页数记录在progress.txt里重启后直接从下页开始。还有一个小技巧是关于 price 字段的清洗。京东价格文本里会带符号或起字落库前用re.sub(r[^\d.], , price)处理成纯数字。这个细节能避免你在后续数据分析阶段被字符串类型绊住。最后想说selenium 这条路能跑但它本质上是“用浏览器换数据”的笨功夫学到的这些定位思路和启动参数策略换到别的电商平台也通用。用不用得上是一回事但它们确实让我在做采集类需求时少熬了好几个通宵希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑