京东商品数据采集可视化平台实战:Python+Django+Selenium+Echarts
发布时间:2026/9/30 3:09:15来源:尧图网络
每年三四月份后台咨询毕业设计选题的总会扎堆出现。电商数据方向基本是每年都有人问的热门而京东商品数据采集分析可视化平台这个题目又是其中最有代表性的一类PythonDjangoSelenium爬虫Echarts四个关键词放在一起爬虫、后端、数据库、可视化全都要沾对课程设计来说难度适中对毕业设计来说又能撑起完整的功能闭环。这篇就按我做过的同类项目的经验把这个平台的每个模块怎么设计、为什么这么设计、实际踩过哪些坑完整拆开讲一遍。如果你打算做这类题目或者已经在写代码但遇到一堆报错建议先收藏再慢慢看。1. 选题拆解这个毕设题目到底在考察哪几项能力1.1 从题目反推评分点拿到毕设题目的第一步不是急着打开IDE而是先把题目里的每个技术名词拆开反推评委想看什么。Python对应的是编程基本功包括类的组织、函数划分、异常处理、代码风格Django对应的是后端框架掌握程度包括模型设计、ORM操作、视图函数、URL路由、模板渲染Selenium对应的是动态页面抓取能力包括元素定位、浏览器自动化、登录态维护、常见反爬应对Echarts对应的则是前端数据可视化能力包括图表选型、JSON数据格式转换、页面布局和交互配置。说白了这套题就是在考察一条完整的数据链路采集、清洗、存储、分析、展示。你只要把这条链路走通每个环节都有东西可写、有图表可看论文和答辩就都不愁没内容。反过来很多同学只盯着爬虫部分把数据抓下来之后随便导个Excel就结束了Django和Echarts沦为摆设那这道题的价值就丢掉了一半——评委一眼就能看出来你是在凑工作量还是在认真做一个完整的平台。1.2 为什么是PythonDjangoSeleniumEcharts这个组合有人会问爬虫用Scrapy不是更专业吗为什么毕业设计偏偏爱用Selenium我先说答案因为毕业设计评的是过程而不只是结果。Scrapy基于异步网络请求性能确实高但代码抽象层次也高中间件、管道、选择器全是自己的体系新手调起错来容易懵。Selenium走的是浏览器自动化路线打开真实浏览器、定位元素、提取文本、点击翻页每一步都能看到实际效果代码逻辑非常直白。再从京东这个具体目标看。京东的商品列表页有大量数据是靠JavaScript动态渲染的你直接用requests去请求HTML源码拿到的可能是一堆空的占位标签商品标题和价格根本不在里面。Selenium驱动的是真实浏览器页面脚本会正常执行渲染完的数据自然能拿到。对不熟悉前端渲染机制的同学来说这是最不容易翻车的选型。再加上Django负责建模、ORM、后台管理、API接口Echarts负责把数据变成柱状图、饼图、折线图整条链路顺滑、演示效果好老师一眼就能看到你的工作量分布。1.3 一个可以直接落地的功能清单我建议把平台拆成下面几个功能模块既可以当开发计划也可以直接当论文的章节骨架商品数据采集输入搜索关键词自动抓取京东搜索结果里的商品标题、价格、店铺、评论数、商品链接数据持久化把抓取结果写入SQLite或MySQL用商品链接做唯一标识实现去重数据管理后台用Django Admin直接查看和维护采集结果支持删除脏数据统计接口后端按分类、价格区间、评论数量做聚合统计输出JSON可视化大屏Echarts展示分类占比饼图、价格分布柱状图、评论量排行条形图、价格走势折线图定时更新可选用APScheduler定期触发采集让平台有时间序列的活数据优先级从前往后时间不够就砍掉最后一项但核心链路仍然是完整的。我自己带人做项目时从来都是先保证这条链路全通再谈锦上添花的模块。2. 采集层落地Selenium抓取京东商品数据的完整思路2.1 先观察页面再写定位代码写爬虫最忌讳上来就写脚本。正确做法是先手动打开页面按F12把DOM结构看清楚再动手。京东的流程是访问主站搜索框输入关键词回车进入搜索结果页商品列表是class为gl-item的li标签每个li包含一张商品卡。以机械键盘搜索结果为例核心字段的CSS选择器如下商品标题li.gl-item .p-name em价格.p-price i店铺名称.p-shop评论数.p-commit strong的title属性或文本常见200万商品链接.p-img a的href属性注意这些选择器对应的页面结构京东偶尔会调整正式写代码之前一定要在DevTools里用querySelector验证一遍。另外任何元素查找都不能裸写find_element后立刻.text页面滚动加载、图片加载慢都会导致元素还没出现就报NoSuchElementException。稳妥做法是用WebDriverWait显式等待等元素可交互之后再操作。2.2 登录态与Cookie处理京东搜索页不登录也能看但连续翻页多了容易弹登录框和验证码。最省事的方案是手动登录一次把Cookie持久化保存之后每次采集自动加载。具体操作分两步第一次登录时用Selenium打开京东首页在代码里暂停手动扫码登录登录成功后抓取Cookie存成JSON文件。第二次运行先访问一次京东域名建立会话再把Cookie逐条写回浏览器最后刷新页面即可。import json from selenium import webdriver driver webdriver.Chrome() # 首次登录手动扫码后保存 cookie driver.get(https://www.jd.com) input(登录完成后按回车继续...) with open(jd_cookies.json, w, encodingutf-8) as f: json.dump(driver.get_cookies(), f) # 再次运行加载 cookie driver.get(https://www.jd.com) with open(jd_cookies.json, r, encodingutf-8) as f: cookies json.load(f) for cookie in cookies: driver.add_cookie(cookie) driver.refresh()这里有个很容易踩的坑add_cookie之前必须先让浏览器访问一次该域名否则会报invalid cookie domain。我自己第一次写的时候就没注意纠结了好半天才发现是调用顺序的问题。2.3 ChromeOptions反检测参数配置Selenium启动的浏览器有一个明显特征window.navigator.webdriver属性为true。京东这类大站对自动化特征识别很敏感访问频率一旦上去滑块验证码就来了。我常用的处理方式是在启动参数里做几层处理最核心的一段如下from selenium.webdriver.chrome.options import Options options Options() options.add_argument(--disable-blink-featuresAutomationControlled) options.add_experimental_option(excludeSwitches, [enable-automation]) options.add_experimental_option(useAutomationExtension, False) options.add_argument(--window-size1920,1080) options.add_argument(user-agentMozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36) driver webdriver.Chrome(optionsoptions) driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument, { source: Object.defineProperty(navigator, webdriver, {get: () undefined}); window.navigator.chrome {runtime: {}}; })第一行的AutomationControlled参数把正在受自动化控制的提示关掉下面几行去掉浏览器右上角的自动化测试标签最后用CDP在每次新页面加载前把webdriver属性改回undefined。这套组合是网上广泛验证过的通用做法能解决绝大多数站点的基础特征检测。但比参数更重要的是访问节奏。Selenium翻页间隔一定要随机抖动建议每页间隔2到6秒翻几页后再随机停一下模拟真人查找商品的操作。毕业设计演示场景下几十条数据的采集量完全没必要追求速度慢一点、稳一点反而不容易触发验证码。2.4 商品信息提取与清洗规则抓到的原始文本几乎不能直接入库存标题里混着换行和空格价格文本可能带着促销价满减等干扰信息评论数显示成200万。我习惯把所有清洗逻辑抽到一个独立函数模块里保持主流程干净import re def clean_title(title): return re.sub(r\s, , title) def clean_price(price_text): match re.search(r\d(\.\d)?, price_text.replace(,, )) return float(match.group()) if match else 0.0 def clean_comment_count(text): text text.strip() if 万 in text: return int(float(text.replace(万, )) * 10000) match re.search(r\d, text.replace(,, )) return int(match.group()) if match else 0清洗完的数据先写CSV再批量导入Django的ORM模型。先落CSV的好处是即使后面Django改模型字段原始数据还在不用重新爬一遍。我自己做项目时CSV文件一直保留到答辩结束中间改过两轮模型全靠这份备份兜底。Selenium脚本的主函数大致长这样接收关键词和页数参数循环翻页每页提取商品卡片清洗后追加到结果列表最后统一返回。记得用try/finally确保driver.quit()一定执行不然浏览器进程会一直占着内存下次启动时就报process already running。3. Django后端设计数据模型、采集命令与图表接口3.1 三张核心表的设计先用django-admin startproject和startapp把工程建好我的项目结构习惯是这样jd_platform/ ├── manage.py ├── jd_platform/ # 项目配置目录 └── analyzer/ # 采集与统计主应用 ├── models.py ├── views.py ├── management/ │ └── commands/ │ └── fetch_jd.py # 采集命令 ├── spider/ │ ├── jd_spider.py # Selenium采集逻辑 │ └── cleaners.py # 清洗函数 └── templates/ └── dashboard.html # 可视化大屏数据模型我建议至少建三张表表之间用外键串联from django.db import models class Category(models.Model): name models.CharField(max_length50, uniqueTrue) class Product(models.Model): title models.CharField(max_length255) url models.URLField(uniqueTrue) price models.FloatField(default0.0) shop models.CharField(max_length100, blankTrue) comment_count models.IntegerField(default0) category models.ForeignKey(Category, on_deletemodels.SET_NULL, nullTrue) created_at models.DateTimeField(auto_now_addTrue) class PriceRecord(models.Model): product models.ForeignKey(Product, on_deletemodels.CASCADE, related_nameprice_records) price models.FloatField() record_time models.DateTimeField(auto_now_addTrue)Product表存商品当前快照PriceRecord表存不同批次的价格折线图就靠它来画。url字段加unique约束天然防重复重复抓取时用get_or_create或先查后插都可以。Category单独拆一张表是为了分类统计方便也符合关系型数据库的规范设计。表建好后记得依次执行python manage.py makemigrations和python manage.py migrate。3.2 采集逻辑与Django的集成方式很多同学纠结Selenium脚本和Django项目怎么合并。最简单的想法是把采集逻辑直接写进视图函数点击页面按钮触发但这样有个明显问题Django开发服务器默认同步处理请求采集几十页数据需要几分钟浏览器页面会一直转圈体验非常差。我的做法是把采集逻辑写成Django的自定义管理命令。这样命令行python manage.py fetch_jd 机械键盘 --pages 3就能独立触发采集不占用Web请求线程也方便答辩时手动演示。命令文件放在analyzer/management/commands/fetch_jd.py里核心结构如下from django.core.management.base import BaseCommand from analyzer.spider.jd_spider import fetch_jd_products class Command(BaseCommand): help 采集京东商品数据 def add_arguments(self, parser): parser.add_argument(keyword, typestr) parser.add_argument(--pages, typeint, default2) def handle(self, *args, **options): count fetch_jd_products(options[keyword], options[pages]) self.stdout.write(self.style.SUCCESS(f采集完成共 {count} 条商品))如果你想做定时采集就在这个命令外面加APScheduler每天固定时间跑一次配合PriceRecord表自然形成价格时间序列数据。这一步是很好的加分项答辩时可以专门提一句。3.3 给Echarts喂数据的接口约定可视化部分需要的是结构化JSON而不是HTML。在views.py里写几个聚合接口统一返回JsonResponse前端用fetch或axios拉取就行。最常用的分类统计接口长这样from django.http import JsonResponse from django.db.models import Count, Avg, Sum from analyzer.models import Product def category_stats(request): data list(Product.objects.values(category__name).annotate( countCount(id), avg_priceAvg(price), total_commentsSum(comment_count) )) return JsonResponse({data: data})类似的接口还可以写价格区间分布、评论数TOP10商品、指定商品的价格时间序列。接口返回字段统一用英文命名前端取值不容易出错这是我在多个项目里养成的习惯。写完后直接用浏览器访问接口地址确认JSON结构符合预期再去做前端图表不然等页面写完才发现接口字段对不上排查成本会翻倍。4. Echarts可视化大屏图表选型、数据对接与常用配置4.1 先给每个图表定一个业务问题Echarts图表类型很多但可视化不是堆得越多越好。我的习惯是先列业务问题再选图表分类占比哪个品类的商品数量最多用饼图价格区间分布商品价格集中在哪个区间用柱状图评论量排行哪件商品讨论热度最高用横向条形图价格走势某几件商品价格随时间怎么变化用折线图价格与评论量关系价格高的商品评论一定更多吗用散点图一张大屏安排四到六个图表就足够撑起演示。页面最顶部可以做几个统计卡片显示总商品数、平均价格、最高评论数这些数据从同一个接口拿前端算一下就行。图表之间最好有联动比如点击饼图某个分类右侧柱状图只展示该分类下的价格分布这种交互在答辩时非常加分Echarts的dispatchAction事件可以实现代码量不大。4.2 Django向Echarts传数据的两种方式第一种是模板直渲染Django视图把聚合结果转成JSON字符串放进context模板里通过safe过滤器直接注入JavaScript变量。import json from django.shortcuts import render from analyzer.models import Product def dashboard(request): chart_data json.dumps( list(Product.objects.values(category__name).annotate(countCount(id))), ensure_asciiFalse ) return render(request, dashboard.html, {chart_data: chart_data})模板侧就是一行代码script const rawData {{ chart_data|safe }}; /script第二种是AJAX异步拉取页面加载后用fetch分别请求分类统计、价格区间等接口拿到数据再调chart.setOption更新图表。这种方式和3.3小节的接口天然搭配首屏渲染更快也方便做联动筛选。我的建议是两者结合首屏直渲染关键数据保证一打开就有图交互联动部分走AJAX。纯靠AJAX有个小风险如果接口报错页面就是一片空白答辩时不好解释。4.3 大屏布局与常用配置Echarts图表容器高度必须显式设置这是新手最常踩的坑。容器div只有宽度没有高度图表根本画不出来。布局上我用最简单的CSS Grid把大屏分成上下两行每行两个图表div classgrid div idpie-category styleheight: 320px;/div div idbar-price styleheight: 320px;/div div idbar-comments styleheight: 320px;/div div idline-trend styleheight: 320px;/div /div常用配置里柱状图可以加渐变色让视觉效果更好饼图通过label的formatter显示百分比折线图别忘了在xAxis里设置boundaryGap: false否则折线图两头会多出一截空白chart.setOption({ tooltip: { trigger: axis }, xAxis: { type: category, boundaryGap: false, data: dates }, yAxis: { type: value }, series: [{ type: line, data: prices, smooth: true }] });Echarts建议直接用官方CDN的4.x版本文档多、资料全不需要npm构建流程对毕设项目来说最省心。加载方式就是script标签引用再配合一个简单的初始化函数就能跑起来。5. 环境配置与排错从零跑通整套项目的全过程5.1 环境清单与版本匹配版本匹配是这套技术栈里最容易被忽视的部分。我把推荐版本整理成一张表照着装就行组件推荐版本说明Python3.10 / 3.113.12 以上部分依赖可能还没适配Django4.2 LTS稳定资料多教程好找Selenium4.x新API不推荐再用老式driver路径写法Chrome 浏览器最新稳定版与 ChromeDriver 版本必须严格对应Echarts4.9.0 CDN兼容性好功能够用安装命令就三条python -m venv venv venv\Scripts\activate # Windows 环境 pip install django selenium webdriver_manager重点工作在ChromeDriver上。之前手动下载驱动容易版本不匹配现在直接推荐用webdriver_manager它能根据你本机Chrome版本自动下载对应驱动from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager driver webdriver.Chrome(serviceService(ChromeDriverManager().install()))如果你本机不方便装Chrome也可以用headless无头模式跑但毕业设计演示时我强烈建议保留有头模式让浏览器窗口在投影上自动打开、自动翻页、自动提取数据这个视觉冲击力比任何截图都强评委对爬虫到底做了什么的理解会直接上一个台阶。5.2 按顺序跑通项目的路径第一次跑项目我建议按下面这个顺序来不要一上来就写完整代码先写最小Selenium脚本能打开京东搜索页并打印前十条商品标题再补清洗逻辑输出到CSV文件人工检查数据质量建Django工程和数据模型把CSV数据导入数据库写聚合接口用浏览器访问接口确认JSON结构正确写前端页面接入Echarts确认每个图表都能正常渲染把Selenium整合成管理命令联调完整采集链路最后加分项定时任务、数据筛选、导出功能每一层跑通再往下一层走报错范围会小很多。我最常看到的情况是同学一口气写完所有代码然后面对一整屏Traceback无从下手不得不从第一个报错开始倒退排查效率极低。分层开发虽然看起来慢实际上是最快的方式。5.3 新手最容易卡的几个报错下面这几个坑是我带人做项目时几乎每组都会遇到的提前看一遍能省很多时间报错/现象原因处理方式WebDriverException: chromedriver executable needs to be in PATH驱动没装或路径没配置改用 webdriver_manager 自动处理SessionNotCreatedExceptionChrome 和 ChromeDriver 版本不匹配更新 Chrome 或更换驱动版本NoSuchElementException元素还没加载完就定位用 WebDriverWait 显式等待ElementClickInterceptedException按钮被登录弹层或悬浮层盖住先关闭弹层再执行点击中文乱码控制台默认编码问题设 PYTHONIOENCODINGutf-8采集到一半弹验证码频率太高或浏览器特征被识别加随机延迟、检查Cookie、核对反检测参数排错方法论只有一条先看异常类型再定位到具体行号不要整段代码瞎猜。Selenium的报错信息其实很友好只要你养成分段调试的习惯绝大多数坑十分钟内能解决。遇到不确定的元素问题直接driver.save_screenshot(debug.png)截图看现场比自己脑补页面状态靠谱得多。6. 答辩演示与代码讲解让评委跟着你的思路走6.1 演示前一天要做的三件事第一数据库里预置一批完整数据保证断网也能演示。很多同学只准备了一条实时采集的演示路径结果现场网络波动或者验证码弹窗整个答辩就卡壳了。正确做法是提前用脚本采集200条以上商品数据入库演示时先打开大屏展示已有的可视化效果再现场触发一次小规模采集展示从抓取到入库的完整流程。第二把演示用的采集参数固定好。比如python manage.py fetch_jd 机械键盘 --pages 1一次只爬一两页现场三十秒内能完成节奏刚好。千万别在答辩时爬十页数据所有人干等两分钟气氛会非常尴尬。第三准备一条代码讲解路线图。老师通常会让你讲爬虫实现这时候光说这就是爬虫代码等于白说。要能按打开浏览器—搜索页面—定位商品块—提取字段—清洗数据—ORM入库—聚合统计—Echarts渲染这条线把代码串着讲一遍。你讲得有条理评委自然觉得你对自己的项目有完整理解。6.2 现场采集翻车时的应急预案现场最怕的不是代码bug而是环境问题。我见过好几次因为演示前忘了启动Django服务打开浏览器一片白的情况。建议在家把完整流程演练至少三遍把启动命令写成一个bat或sh脚本一键执行。如果现场真的采集失败别慌按下面顺序处理立即切到数据库里已有的数据告诉评委这是之前采集入库的历史批次检查浏览器窗口是否弹了验证码如果是退出后重新加载Cookie再试如果页面整个打不开直接切回代码讲解说明相关模块在干净环境下的运行逻辑大部分评委在乎的是你是否理解自己的项目而不是现场零失误。你能把报错原因讲清楚比对着代码手足无措强得多。我见过一个学弟现场遇到驱动版本报错他直接说这是ChromeDriver版本和浏览器不匹配导致的用webdriver_manager可以自动解决然后手改两行代码重跑成功评委反而给了很高评价。6.3 容易被追问的五个问题及回答思路为什么选Selenium而不是Scrapy回答思路京东列表页依赖JavaScript动态渲染requests拿到的静态HTML不完整Selenium驱动真实浏览器能处理JS渲染、登录态和验证码虽然性能不如Scrapy但在这个场景下稳定性和可调试性更高。反爬怎么处理的回答思路随机延时、Cookie持久化、隐藏webdriver特征、控制采集频率。重点强调低频、学习用途这个边界态度要坦然。数据准确吗回答思路数据以页面展示为准评论数里的万做了单位换算价格清洗掉了促销文案干扰趋势分析层面的误差可控。如果评委较真可以补充说这是演示级数据不做商业用途。能不能扩展成分布式爬虫回答思路可以采集层替换成Scrapy或集成Celery异步任务数据层加Redis做队列和缓存这套架构是现成的扩展路径说明自己了解行业主流方案即可。钱和精力花在哪了回答思路把时间分配讲清楚比如40%在采集与清洗30%在Django接口20%在可视化10%在排错让评委感受到工作量实打实。最后说一个我自己的习惯项目做完不要急着交掉就完事。把采集脚本、清洗函数、接口文档各自整理成独立模块留下清晰的注释这对你后续做其他数据类项目来说就是现成的工具箱。答辩前多跑几遍把每个报错截图存下来整理成一页排错笔记演示时一旦出问题翻笔记比现场搜索高效得多。祝看到这里的同学答辩顺利项目一把过。
网站建设高端定制企业官网