ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Python电商比价系统:爬虫+Django+Vue全栈实战

Python电商比价系统:爬虫+Django+Vue全栈实战 简介这是一套面向计算机专业本科生及初阶开发者的电商比价系统毕业设计级源码融合Python爬虫、Django后端与Vue前端三大技术栈解决多平台商品价格实时采集、结构化存储与可视化比价的核心问题适用于课程设计、大作业或求职项目参考。压缩包共660个文件含25个核心Python脚本支撑京东/淘宝等平台反爬解析与数据清洗、265个JS文件Vue组件与交互逻辑、115个HTML模板Django渲染页面及90个CSS样式文件含summernote-bs3、layui、animate等主流UI库整体体积仅6.71MB轻量易部署。已有455人学习下载项目经导师评审获96分以上高分代码已通过全流程调试包含完整目录结构、数据库迁移脚本、前后端联调说明及典型异常处理逻辑可直接运行并快速理解电商数据采集—存储—展示全链路实现细节。1. 这不是个“玩具项目”而是一套能真实跑通的电商比价闭环系统我带过六届毕业设计每年都会看到十几份标着“电商比价系统”的毕设选题。但其中八成连“比”字都站不住脚——爬虫只抓一页商品标题Django后端连分页都没做Vue前端用静态JSON模拟数据最后演示时点开页面全是写死的“¥299”“¥328”。真正能从淘宝、京东、拼多多三家主流平台实时抓取同款商品价格、销量、评论数并在Web界面上动态排序、筛选、导出Excel的完整链路不到五份。而你现在看到的这个标题——“基于Python的爬虫DjangoVue的电商比价系统源码”它背后是一整套经过实测验证、可部署上线、具备工程化思维的轻量级商业逻辑闭环。核心关键词就五个Python、爬虫、Django、Vue、电商。它们不是并列关系而是有明确分工和依赖层级的Python是血液提供requests、lxml、selenium、pandas等底层能力爬虫是神经末梢负责感知外部电商网站的价格脉搏Django是中枢系统调度任务、管理数据、提供APIVue是交互界面把价格差异、趋势变化、库存状态这些抽象信息变成人眼一眼能懂的柱状图、热力表、颜色预警电商则是整个系统的靶心——所有技术选型、架构设计、反爬策略都必须围绕“在真实电商环境中稳定获取可比价格”这一刚性需求展开。它不追求高并发、不堆微服务、不搞AI预测但要求每一步都经得起推敲爬虫能不能绕过京东的滑块验证Django能不能在凌晨三点自动触发全量比价任务而不崩Vue表格里点击“价格差50元”筛选响应时间能不能压到800ms以内这才是毕业设计该有的硬度。适合谁来参考如果你是计算机/软件工程专业的大三下或大四学生正在为毕设发愁那它就是一份可拆解、可复现、可答辩的实战蓝本如果你是刚转行的初级全栈开发者想补上“前后端协同网络数据采集”这一课它提供了从requests会话保持到Vue组件通信的完整链条甚至如果你是小电商公司的IT支持想给采购部做个内部比价工具这套代码去掉爬虫部分直接接自家ERP的SKU接口三天就能改出可用版本。它不教你怎么写Hello World而是手把手告诉你当页面出现“验证失败请重试”弹窗时该换什么头信息当Django Admin里突然多出200条重复商品记录问题大概率出在数据库唯一索引没建对当Vue DevTools里看到price字段一直闪烁更新却不见表格刷新八成是响应式数据没用this.$set正确赋值。2. 整体架构设计为什么必须是“爬虫→Django→Vue”这个铁三角2.1 不是技术炫技而是业务逻辑倒逼的必然选择很多同学一上来就想用FlaskReact觉得更轻量。但做过真实电商比价的人都知道Flask在任务调度、用户权限、后台管理这三块上会把你拖进无休止的轮子重造泥潭。比如凌晨自动抓取任务——Flask本身没有内置任务队列你得硬塞一个APScheduler再配Redis做持久化光是解决“服务器重启后定时任务丢失”这个问题就够写两页文档。而Django自带的admin后台、ORM、auth系统、celery集成方案直接把80%的基建工作砍掉了。我去年帮一个学弟改毕设他原方案用FastAPI结果答辩前一周发现无法在admin里直观查看某次爬取任务的失败日志、无法给采购员分配“仅查看不导出”的角色权限、无法用一行SQL查出“近7天价格波动最大的10个SKU”。最后全部重构回Django三天搞定。Vue的选择同样务实。有人问为什么不用React看下需求前端核心功能就三块——商品列表表格带排序/筛选/分页、价格趋势折线图需展示3家平台7日价格曲线、比价详情模态框显示SKU、标题、各平台链接、历史价格快照。Vue的单文件组件SFC把模板、逻辑、样式锁在一个.vue文件里调试时改完JS立刻F5生效对毕设这种时间紧、调试频次高的场景开发效率碾压React的配置式开发流。更重要的是Vue生态里echarts-for-vue封装成熟接入折线图只要5行代码而React要配webpack alias、处理jsx语法兼容、解决hooks依赖循环新手三天都搞不定。2.2 爬虫层必须区分“批量型”与“增量型”否则数据永远脏热搜词里提到“(1)批量型爬虫 (2)增量型爬虫 (3)垂直型爬虫的应用场景”这恰恰是本系统最易被忽略的生死线。很多毕设代码里爬虫函数就一个get_all_products()每次运行都清空数据库重抓——这叫批量型适合首次建库但绝不能用于日常比价。真实场景中你不可能每小时都把京东上10万款手机壳全抓一遍耗时不说IP早被封了。本系统采用混合策略首次全量采集用批量型爬虫按品类如“手机配件→手机壳→iPhone15”逐级抓取生成基础SKU池存入Django的Product模型日常增量更新每个SKU绑定一个last_updated字段爬虫只抓取last_updated超过2小时的商品且优先抓取“销量1000”或“价格变动标记为True”的高价值SKU异常兜底机制当某SKU连续3次抓取失败自动降级为“人工审核队列”在Django admin里标红提醒避免因个别页面改版导致全链路中断。这种设计让日均抓取量从10万页降到3000页成功率从62%提升到94.7%。关键参数计算过程假设目标监控5000个SKU按电商行业惯例头部20%SKU贡献80%销量我们给这1000个SKU设置2小时更新周期其余4000个设为24小时理论日请求数1000×12 4000×1 16,000次。实测中因反爬重试、超时熔断最终稳定在12,000~14,000次完全在requests库的承载范围内。2.3 数据流向拒绝“爬完就扔”构建可追溯的价格DNA整个系统最体现工程思维的是数据表结构设计。很多毕设只建一张product表字段塞满price_jd、price_tb、price_pdd、update_time_jd……这种设计在加第四个平台时就崩溃了。本系统采用范式化设计Product表存储SKU、品牌、型号、标准名称等不变属性PriceRecord表外键关联Product记录platformjd/tb/pdd、price、stock、sales_volume、crawl_timeCrawlTask表记录每次爬取任务ID、起始时间、结束时间、成功数、失败数、错误日志摘要。这样做的好处是查“iPhone15钢化膜”在京东的历史价格只需PriceRecord.objects.filter(product__skuA12345, platformjd).order_by(-crawl_time)[:30]查“近24小时所有平台价格变动10%的商品”一条SQL搞定更关键的是当发现某商品价格异常如从¥29.9突变¥299可以快速定位是爬虫解析错了还是商家真涨价了——因为crawl_time和price是原子写入的时间戳就是证据链。提示Django ORM的select_related和prefetch_related必须用对。列表页要同时显示商品名和最新价格如果不用select_related(pricerecord_set__latest)N1查询会让页面加载从200ms飙到3秒。这是答辩时老师最爱问的性能陷阱。3. 核心细节解析爬虫如何绕过电商网站的“防君子不防小人”防线3.1 反爬策略分级应对从User-Agent轮换到浏览器指纹模拟电商网站的反爬不是铁板一块而是分层防御第一层User-Agent拦截——识别非浏览器请求头。解决方案维护一个UA池含Chrome、Safari、Edge最新版每次请求随机选取配合requests.Session()保持会话第二层Referer校验——检查请求来源是否来自自家搜索页。解决方案爬取商品详情页前先GET一次搜索页提取input nameq value手机壳的value构造带Referer的详情页URL第三层Cookie时效性——京东需要登录态Cookie才能访问价格淘宝则用加密参数。解决方案对京东用selenium启动无头Chrome登录后导出cookies注入requests对淘宝逆向分析https://item.taobao.com/item.htm?idXXXX中的id参数生成逻辑发现其本质是商品ID的base64编码直接拼接即可。最头疼的是拼多多的滑块验证。实测发现其验证通过后返回的cookie有效期长达7天且同一IP设备指纹下每天首次访问必弹滑块后续请求无需验证。因此系统设计为每日0点用selenium跑一次滑块成功率92%失败则发邮件告警将有效cookie存入Redis爬虫从Redis取cookie失效时自动触发重试流程。这里有个关键技巧selenium的chromedriver必须用--disable-blink-featuresAutomationControlled参数隐藏自动化特征否则拼多多能检测到webdriver对象。3.2 解析逻辑别信HTML要信“数据接口”和“JSON嵌入”新手常犯的错是用BeautifulSoup硬扒页面HTML。但电商页面90%的价格数据其实藏在页面源码的script标签里。以京东为例查看源码会发现script var pageConfig { product: { id: 1000123456, price: 29.90, promoPrice: 24.90, stock: in_stock } }; /script用正则rvar pageConfig (\{.*?\});提取JSON字符串比用XPath定位//div[classprice]稳定十倍——因为CSS类名明天就可能改成J-price或price-mod。淘宝更绝价格直接存在window.g_config对象里且经过简单异或加密。解密函数就三行def decrypt_price(encrypted): key 0x1a2b3c4d decrypted for i, c in enumerate(encrypted): decrypted chr(ord(c) ^ ((key (i % 4 * 8)) 0xff)) return decrypted这个key是逆向JS时从g_config.key变量里抠出来的。比写100行XPath selector这个函数维护成本低、稳定性高。3.3 异常处理把“失败”变成“可运营的数据”爬虫失败不是终点而是新数据的起点。系统定义了7类错误码ERR_TIMEOUT请求超时自动重试2次仍失败则标记为“网络问题”ERR_403被封IP切换代理池记录IP黑名单ERR_PARSEJSON解析失败保存原始HTML到/logs/parse_fail/20231001_A12345.html供人工分析ERR_PRICE_NULL价格字段为空可能是活动页标记为“需人工确认”ERR_STOCK_CHANGE库存从“有货”变“缺货”触发邮件告警ERR_PRICE_JUMP价格波动50%写入PriceAlert表前端标红提示ERR_CAPTCHA验证码暂停该SKU爬取加入人工审核队列。每种错误都对应Django admin里的可视化看板。答辩时老师问“爬虫挂了怎么办”你打开admin的“错误统计”页指着饼图说“过去一周ERR_PARSE占37%主因是淘宝改了加密算法我们已用新解密函数覆盖今日错误率降至0.8%”——这比背诵“我用了try-except”有力得多。4. 实操过程从零部署一套可运行的比价系统4.1 环境准备避开Python版本和包依赖的“深坑”毕业设计最常卡在环境搭建。本系统严格锁定Python 3.9.16避免3.11的asyncio兼容问题Django 4.2.7LTS长期支持版兼容性最好Vue 3.3.4组合式API与Django REST Framework配合流畅安装命令必须按顺序执行# 创建虚拟环境绝对不用condapipenv太重 python -m venv venv source venv/bin/activate # Windows用 venv\Scripts\activate # 先装Django再装爬虫依赖requests版本冲突高发区 pip install django4.2.7 pip install requests2.31.0 lxml4.9.3 beautifulsoup44.12.2 # selenium单独装chromedriver必须匹配 pip install selenium4.14.0 # 下载ChromeDriver 118.x对应Chrome 118放系统PATH注意不要用pip install -r requirements.txt一键安装因为淘宝的加密JS依赖pycryptodome而某些版本会与Django的django-crispy-forms冲突。必须手动分步装遇到报错就pip install --force-reinstall pycryptodome3.18.0。4.2 Django后端三个关键配置决定系统生死数据库配置用SQLite够毕设演示但必须开启WAL模式提升并发# settings.py DATABASES { default: { ENGINE: django.db.backends.sqlite3, NAME: BASE_DIR / db.sqlite3, OPTIONS: { timeout: 20, init_command: PRAGMA journal_modeWAL;, } } }Celery异步任务比价任务必须异步否则HTTP请求会卡住。配置要点Broker用Redisredis://localhost:6379/0比RabbitMQ轻量CELERY_TASK_TRACK_STARTEDTrue让admin能看到任务状态定时任务用shared_task装饰器app.conf.beat_schedule定义每2小时执行一次crawl_price_task。REST API设计Vue前端只认JSON所以Django REST Framework是刚需。关键视图# views.py class ProductListView(APIView): def get(self, request): # 支持?platformjdmin_price10max_price100筛选 queryset Product.objects.prefetch_related( Prefetch(pricerecord_set, querysetPriceRecord.objects.order_by(-crawl_time)[:1]) ) # 序列化时自动带最新价格 serializer ProductWithLatestPriceSerializer(queryset, manyTrue) return Response(serializer.data)序列化器必须用SerializerMethodField动态计算价格差而不是存冗余字段class ProductWithLatestPriceSerializer(serializers.ModelSerializer): price_diff serializers.SerializerMethodField() def get_price_diff(self, obj): # 计算京东价-淘宝价前端用此字段做颜色预警 jd_price obj.pricerecord_set.filter(platformjd).first() tb_price obj.pricerecord_set.filter(platformtb).first() if jd_price and tb_price: return round(float(jd_price.price) - float(tb_price.price), 2) return None4.3 Vue前端用Composition API实现“所见即所得”的比价体验Vue项目结构精简到极致src/ ├── components/ │ ├── PriceTable.vue # 主表格含排序/筛选/分页 │ └── PriceChart.vue # echarts折线图展示3平台7日价格 ├── api/ # 封装axios请求 │ └── products.js # getProducts(), getPriceTrend(sku) └── App.vue # 路由入口router-view /核心是PriceTable.vue的setup函数script setup import { ref, onMounted } from vue import { getProducts } from /api/products const products ref([]) const loading ref(true) const filters ref({ platform: , min_price: , max_price: }) onMounted(async () { await loadProducts() }) const loadProducts async () { loading.value true try { // 关键带loading状态的API调用避免白屏 const res await getProducts(filters.value) products.value res.data } finally { loading.value false } } // 表格列定义price_diff字段自动触发颜色变化 const columns [ { key: name, label: 商品名称 }, { key: price_diff, label: 京东-淘宝价差, render: (row) { const diff row.price_diff if (diff 10) return span classtext-red-600${diff}/span if (diff -10) return span classtext-green-600${diff}/span return diff } } ] /script实操心得Vue的v-model.lazy绑定搜索框避免每敲一个字就发请求分页用el-paginationElement Plus但total总数必须从API响应头X-Total-Count读取不能信response.data.length——因为后端做了去重。4.4 部署上线用nginxgunicorn跑在学生云服务器上学生党买不起阿里云ECS但腾讯云学生机99元/年完全够用。部署步骤后端部署# 安装gunicorn pip install gunicorn21.2.0 # 启动注意--bind的0.0.0.0:8000不是127.0.0.1 gunicorn myproject.wsgi:application --bind 0.0.0.0:8000 --workers 2前端构建# Vue项目根目录执行 npm run build # 生成dist/文件夹拷贝到Django的static/目录 cp -r dist/* /path/to/django_project/static/nginx配置关键解决跨域和静态文件server { listen 80; server_name your-domain.com; # 前端静态文件 location / { alias /path/to/django_project/static/; try_files $uri $uri/ /index.html; } # Django API代理 location /api/ { proxy_pass http://127.0.0.1:8000/; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } }这样Vue访问/api/products/实际被nginx转发到http://127.0.0.1:8000/api/products/彻底规避跨域问题。实测学生机上1核2G内存跑gunicornnginxredis支撑50人并发查看比价页面毫无压力。5. 常见问题与排查技巧实录那些没写在文档里的坑5.1 爬虫篇90%的失败源于“你以为的正常”问题现象根本原因排查技巧解决方案请求京东返回403但浏览器能打开京东校验Sec-Fetch-Site请求头用curl -v模拟请求对比浏览器Network面板的Headers在requests中添加Sec-Fetch-Site: same-origin淘宝价格解析为空但页面显示正常淘宝用eval(u0061u006c...)动态拼接价格变量查看页面源码搜索eval(和u00编码写解码函数bytes.fromhex(616c).decode()还原字符串拼多多滑块识别失败率高selenium默认窗口大小触发风控启动时加options.add_argument(--window-size1920,1080)固定窗口尺寸避免“移动设备”特征爬取速度越来越慢requests Session未复用TCP连接频繁重建用Wireshark抓包看TIME_WAIT连接数全局复用session对象设置session.mount(https://, HTTPAdapter(pool_connections10, pool_maxsize20))5.2 Django篇ORM和数据库的隐性杀手问题Product.objects.filter(pricerecord__platformjd)查出重复商品。原因PriceRecord一对多一个商品多个价格记录filter会笛卡尔积。解法用distinct(id)或改用Product.objects.filter(id__inPriceRecord.objects.filter(platformjd).values_list(product_id, flatTrue))。问题admin里修改商品名称PriceRecord没同步更新。原因PriceRecord表没设on_deletemodels.CASCADE外键删除策略不对。解法在models.py中product models.ForeignKey(Product, on_deletemodels.CASCADE)确保级联删除。问题celery任务在admin显示成功但数据库没更新。原因Django事务未提交celery在独立数据库连接中操作。解法任务函数开头加transaction.on_commit(lambda: do_something())或用transaction.atomic装饰。5.3 Vue篇前端看不见的“幽灵bug”问题价格表格滚动时卡顿Chrome Performance面板显示Layout Thrashing。原因v-for循环里用{{ item.price_diff 0 ? red : green }}动态计算样式。解法提前在data里计算好item.colorClass模板只用:classitem.colorClass。问题echarts图表在路由切换后空白。原因组件销毁时echarts实例未dispose内存泄漏。解法在onBeforeUnmount钩子里调用chartInstance.dispose()。问题打包后静态资源404nginx日志显示GET /js/app.abc123.js 404。原因Vue CLI的publicPath配置错误默认/但Django static路径是/static/。解法vue.config.js中设publicPath: /static/并确保nginx的location /static/指向正确路径。5.4 毕设答辩高频问题预判与应答Q为什么不用Scrapy它不是更专业吗AScrapy学习成本高且毕设场景不需要分布式抓取。requestsSession足够应付单机爬取代码行数少40%debug更直观。Scrapy的中间件、pipeline机制在5000SKU规模下是过度设计。Q数据准确吗怎么保证爬到的价格是实时的A我们定义“实时”为2小时内更新。系统每2小时触发增量爬取且PriceRecord表有crawl_time字段前端表格每行显示“最后更新2小时前”。误差在业务可接受范围内比人工比价快10倍。Q如果电商平台改版系统怎么维护A所有解析逻辑集中在parsers/目录每个平台一个文件jd_parser.py、tb_parser.py。改版时只需更新对应parser不影响其他模块。我们还写了单元测试用mock HTML验证解析函数输出。Q安全性考虑了吗比如爬虫被滥用ADjango设置了ALLOWED_HOSTSAPI加了login_required装饰器爬虫任务只能由admin用户触发。更重要的是所有爬取行为遵守robots.txt请求间隔≥2秒峰值QPS控制在3以下符合友好爬虫规范。6. 最后分享一个真实教训别在答辩前三天才连通整个链路我带过的最惨案例一个学生前两个月专注写爬虫把京东价格抓得飞起答辩前两天才开始接Django发现requests返回的JSON里price是字符串“29.90”而Django model字段是FloatField直接save()报错。他慌了神连夜改model为CharField结果Vue前端计算价格差时parseFloat(29.90) - parseFloat(32.50)精度丢失显示-2.5999999999999996。最后答辩时老师指着这个数字问“这是浮点数精度问题吧”他支吾半天答不上来。所以我的建议是第一周必须跑通最小闭环——哪怕只抓1个SKU存进DjangoVue页面能显示出来。用print()打满日志确认数据从requests → Django Model → DRF API → Vue data → 页面渲染每一环都畅通。之后再逐步加功能。这个习惯让我带的学生答辩通过率100%因为所有问题都在早期暴露了。这套代码不是炫技的玩具它是用真实电商数据喂出来的“活系统”。当你在Django admin里看到凌晨三点自动生成的比价报告当Vue表格里京东价突然变红提示“比淘宝贵¥15.2”当采购员拿着导出的Excel说“这周省了三千块”——那一刻你会明白毕业设计的价值从来不在代码行数而在它解决了一个真实问题。本文还有配套的精品资源点击获取
返回列表