Python+selenium京东评论爬虫实战:无需登录抓取与避坑指南

发布时间:2026/10/20 10:02:33
Python+selenium京东评论爬虫实战:无需登录抓取与避坑指南 简介这是一套面向数据分析初学者与市场调研人员的京东评论采集工具基于Python与selenium实现无需登录京东账户即可自动化抓取商品评论数据可用于商品口碑分析、竞品调研与用户偏好研究等场景。资源包共6个文件以3个py脚本为核心分别承担爬虫主逻辑、数据库操作与日志记录另含1个ini配置文件用于参数调整以及md说明文档和license授权文件压缩包整体约8KB结构精简、便于快速上手。目前已有163人学习关注。代码中涵盖页面元素定位、评论内容与评分等字段提取、数据存储、异常处理等关键环节并预留了分布式扩展与合规爬取的思路读者可据此理解selenium模拟浏览器绕过登录校验的完整实现路径掌握从配置到落地的爬虫开发流程。1. 从一次“评论抓不全”说起这套爬虫到底能解决什么做电商数据分析的同行大概率都遇到过这种场景想拉一批商品评论做情感分析或竞品对比手动复制粘贴显然不现实写 requests 直接怼接口又发现评论是异步加载的翻页还得带一堆加密参数。更麻烦的是很多平台现在强制登录才能看完整评论账号一多就触发风控。这套基于 Python selenium 的京东评论爬虫系统核心卖点就是无需登录——它不碰账号体系靠浏览器自动化把渲染后的评论 DOM 直接抠下来绕开了接口签名和登录态这两道坎。它适合谁一是做 NLP 语料采集的学生和初级数据分析师二是需要快速验证选品评论趋势的运营开发三是想学 selenium 实战但不想一上来就啃反爬硬骨头的 Python 学习者。不适合谁需要百万级高并发采集的生产环境——selenium 的资源开销摆在那这点后面会细说。整份资源是一个可运行的 Python 工程包拿到手改几个参数就能跑下面按“能跑起来 → 跑得稳 → 跑得久”的顺序拆一遍。2. 环境搭起来selenium 浏览器驱动的版本对齐2.1 为什么选 selenium 而不是 requests京东评论的加载方式决定了技术选型。商品详情页的评论区域是滚动到可视区后才触发 XHR 请求的而且请求头里带了h5st这类动态签名参数逆向成本高且随版本变动。selenium 的思路是“让浏览器自己算”你只管等页面渲染完从 DOM 里取文本。代价是慢单条评论的采集耗时大约是接口方案的 5 到 10 倍但胜在稳定、代码短、不需要维护签名逻辑。常见做法是配合undetected-chromedriver或至少给 ChromeOptions 加几个规避自动化检测的参数。这套资源默认用的是原生 selenium我一般会补上--disable-blink-featuresAutomationControlled否则部分页面会返回空评论列表。2.2 依赖安装与驱动配置先建虚拟环境别把系统 Python 搞乱python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate pip install selenium4.15.0 webdriver-manager4.0.1 pandas openpyxl这里锁selenium4.15.0是有原因的4.6 之后 selenium 内置了 Selenium Manager能自动下载匹配的驱动省掉手动配 chromedriver 的麻烦。但如果你公司内网限制外网下载就得回退到手动指定驱动路径。from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.chrome.service import Service def build_driver(driver_pathNone): opts Options() opts.add_argument(--disable-blink-featuresAutomationControlled) opts.add_argument(--start-maximized) opts.add_argument(--disable-gpu) # 无头模式采集更快但部分反爬会识别调试阶段先关掉 # opts.add_argument(--headlessnew) opts.add_experimental_option(excludeSwitches, [enable-automation]) opts.add_experimental_option(useAutomationExtension, False) if driver_path: service Service(executable_pathdriver_path) driver webdriver.Chrome(serviceservice, optionsopts) else: driver webdriver.Chrome(optionsopts) # 走 Selenium Manager 自动匹配 driver.execute_cdp_cmd( Page.addScriptToEvaluateOnNewDocument, {source: Object.defineProperty(navigator, webdriver, {get: () undefined})} ) return driver逻辑说明excludeSwitches去掉“Chrome 正受到自动测试软件控制”的提示条addScriptToEvaluateOnNewDocument在每个新文档加载前把navigator.webdriver改掉这是最基础的指纹抹除。参数driver_path留空时走自动匹配内网环境就传本地驱动绝对路径。注意无头模式在调试阶段别开出错了你看不到页面状态排查全靠猜。3. 评论抓取的核心逻辑滚动加载与 DOM 解析3.1 页面滚动触发评论渲染京东商品页的评论不是一次性加载的需要先滚动到评论模块再在模块内继续滚动加载更多。直接driver.get()之后find_elements大概率拿到空列表这是新手第一个翻车点。import time from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC def scroll_to_comments(driver, timeout15): # 先滚到页面底部触发评论模块懒加载 driver.execute_script(window.scrollTo(0, document.body.scrollHeight);) time.sleep(2) # 等待评论容器出现 wait WebDriverWait(driver, timeout) comment_root wait.until( EC.presence_of_element_located((By.CSS_SELECTOR, .comment-root, #comment, .J-comment)) ) driver.execute_script(arguments[0].scrollIntoView({block: center});, comment_root) time.sleep(1) return comment_rootscrollIntoView的block: center让评论模块停在视口中间比滚到顶部或底部更稳因为部分懒加载逻辑判断的是元素是否进入可视区。time.sleep在这里不是偷懒是给 XHR 留响应窗口后面会讲怎么用显式等待替代。3.2 逐条提取评论字段评论 DOM 结构会随京东前端版本变所以选择器要写得宽容一点用多个候选def parse_comments(driver): items driver.find_elements(By.CSS_SELECTOR, .comment-item, .J-comment-item, [class*commentItem]) rows [] for it in items: try: content it.find_element(By.CSS_SELECTOR, .comment-con, .J-comment-content, [class*content]).text.strip() except Exception: content try: user it.find_element(By.CSS_SELECTOR, .user-info, .J-user-info, [class*user]).text.strip() except Exception: user try: star it.find_element(By.CSS_SELECTOR, .comment-star, [class*star]).get_attribute(class) except Exception: star rows.append({user: user, content: content, star_class: star}) return rows每个字段都用 try/except 包住因为不同商品页的评论模板可能缺字段比如无追评、无图。star_class存的是 class 字符串星级通常藏在star5、star4这类类名里后续用正则提取数字即可。别用find_element不加保护一条评论缺字段整个循环就断了。3.3 翻页与去重京东评论翻页是点击“下一页”按钮按钮可能被遮挡得先滚动到它可见def go_next_page(driver): try: btn driver.find_element(By.CSS_SELECTOR, .ui-pager-next, .J-pager-next, a[class*next]) driver.execute_script(arguments[0].scrollIntoView({block: center});, btn) time.sleep(0.5) btn.click() time.sleep(2) return True except Exception: return False去重建议用评论内容的 md5 做 key因为同一用户可能重复评论但内容一般唯一。翻到最后一页时按钮会变成 disabled 或消失go_next_page返回 False 就停。4. 避坑与排查五个真实踩过的坑4.1 评论列表始终为空现象页面明明有评论find_elements返回空列表。原因评论模块还没滚动到可视区或者页面用的是 iframe 嵌套。解决先scrollIntoView再等待用driver.switch_to.frame()切进 iframe 再查找采集完记得switch_to.default_content()切回来。4.2 翻到第二页就卡住现象第一页正常点下一页后超时。原因翻页触发了新窗口或页面局部刷新旧元素引用失效StaleElementReferenceException。解决每次翻页后重新定位元素别缓存WebElement对象或者用WebDriverWait等待新评论出现再继续。4.3 跑几十条就被限流现象采集到一定数量后返回空页或验证码。原因selenium 的请求特征太明显且滚动节奏机械。解决加随机time.sleep(random.uniform(1.5, 3.5))别用固定间隔控制单次采集量分批次跑必要时换 IP 或降低频率。这套资源本身不带代理池生产用要自己补。4.4 无头模式下采集失败现象有头模式正常--headless后评论为空。原因部分反爬脚本检测无头特征或视口尺寸不对导致懒加载不触发。解决无头模式加--window-size1920,1080并配合--headlessnew新版 Chrome 的无头更接近真实浏览器。4.5 中文写入 Excel 乱码现象pandas 导出的 CSV 用 Excel 打开是乱码。原因默认 utf-8 无 BOMExcel 识别不了。解决df.to_csv(out.csv, indexFalse, encodingutf-8-sig)或者直接to_excel输出 xlsx。5. 进阶把采集结果落库并做基础清洗5.1 从列表到 DataFrame 的字段规整采集回来的star_class是原始 class 字符串得转成 1 到 5 的整数星级方便后续统计import re import pandas as pd def normalize(rows): df pd.DataFrame(rows) df[star] df[star_class].apply( lambda x: int(re.search(rstar(\d), x).group(1)) if re.search(rstar(\d), x) else None ) df[content] df[content].str.replace(r\s, , regexTrue).str.strip() df df[df[content].str.len() 0] # 丢掉空评论 df df.drop_duplicates(subset[content]) # 按内容去重 return df[[user, content, star]]正则star(\d)抓星级数字\s把换行和多余空格压成单空格空评论和重复内容直接过滤。这一步做完数据就能直接喂给情感分析模型或做词云了。5.2 落库与增量采集小批量用 SQLite 就够建表时给content加唯一索引天然去重import sqlite3 def save_to_sqlite(df, db_pathcomments.db): conn sqlite3.connect(db_path) conn.execute( CREATE TABLE IF NOT EXISTS comments ( id INTEGER PRIMARY KEY AUTOINCREMENT, user TEXT, content TEXT UNIQUE, star INTEGER, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) ) for _, r in df.iterrows(): conn.execute( INSERT OR IGNORE INTO comments (user, content, star) VALUES (?, ?, ?), (r[user], r[content], r[star]) ) conn.commit() conn.close()INSERT OR IGNORE配合UNIQUE约束重复评论自动跳过增量采集时不用先查再插省一次往返。created_at默认当前时间方便按批次回溯。5.3 验证采集完整性的小技巧跑完一轮后别急着信结果。我一般会做两件事一是对比页面显示的“评论总数”和实际入库条数差太多说明翻页漏了二是随机抽 5 条评论手动在页面上搜一下确认内容一致。这套资源没有内置校验逻辑得自己加。def sanity_check(driver, df): try: total_text driver.find_element(By.CSS_SELECTOR, .comment-count, [class*count]).text total int(re.search(r\d, total_text).group()) print(f页面显示 {total} 条实际采集 {len(df)} 条覆盖率 {len(df)/total:.1%}) except Exception: print(未找到评论总数跳过校验)覆盖率低于 80% 就得回头查翻页逻辑或等待时间。这个习惯帮我省过好几次“以为采完了其实只采了前三页”的尴尬。从那以后我每次跑这类 selenium 采集都强制先跑一遍小样本比如只采 2 页验证字段和翻页确认没问题再放开批量。希望帮到你。本文还有配套的精品资源点击获取