基于Python+Django的京东商品比价系统:从爬虫到Web应用的全栈实践
2026/9/16 12:27:35 网站建设 项目流程

简介:网络爬虫作为自动化数据采集的核心技术,其原理在于模拟浏览器行为,向目标网站发送HTTP请求并解析返回的HTML文档,从而高效提取结构化信息。这项技术在现代数据驱动决策中价值显著,尤其在电商领域,通过实时抓取商品价格、库存及评价数据,为消费者提供精准的比价服务,辅助其做出更经济的购物选择。Django框架以其“开箱即用”的特性,为快速构建此类数据驱动的Web应用提供了强大支持,其内置的ORM、Admin后台和MTV模式,极大地简化了从数据处理到前端展示的开发流程。本系统正是这一技术组合的典型应用,它利用Python爬虫抓取京东商品信息,通过Django构建比价平台,实现了商品搜索、价格对比与历史追踪等功能,为开发者提供了一个涵盖数据采集、处理与可视化展示的完整全栈项目范例。

1. 项目概述:一个能帮你省钱的毕业设计

最近几年,带“毕业设计”字眼的项目源码在网上特别火,尤其是那些结合了热门技术栈和实用场景的。今天要聊的这个“基于Python+Django的京东商品比价系统”,就是一个典型代表。乍一看标题,你可能觉得这又是一个学生为了应付毕设搞出来的“玩具”。但说实话,这个选题背后藏着不少真东西,它巧妙地串联了Python爬虫、Web开发、数据分析和一个几乎人人都有的痛点——怎么买东西更便宜。

简单来说,这个系统就是一个自动化的“价格侦察兵”。它的核心工作流程是:你输入一个想买的商品名称(比如“iPhone 15”),系统会模拟用户行为,去京东网站上搜索这个商品,把不同店铺、不同规格(颜色、内存版本)的商品信息,包括价格、店铺名称、促销活动、评价数量等,一股脑儿抓取下来。然后,它把这些数据清洗、整理,在一个简洁的网页上展示出来,让你一眼就能看出哪个链接卖得最便宜,哪个店铺有优惠券,哪个是自营店。对于学生而言,这是一个绝佳的练手项目,因为它覆盖了软件工程从数据获取、处理到展示的全流程;对于普通用户,这甚至是一个可以自己部署、为自己服务的实用工具。

为什么这个选题值得深挖?首先,技术栈选得准。Python是当下数据处理和自动化脚本的“头号语言”,生态丰富,学习资料遍地都是。Django作为Python界最负盛名的“全家桶”式Web框架,以功能全面、文档清晰、开发效率高著称,非常适合用来快速构建一个结构清晰、带后台管理的数据驱动型网站。用它们俩来做一个比价系统,技术路径非常成熟。其次,需求真实。在电商促销套路层出不穷的今天,“货比三家”是刚需。最后,作为毕业设计,它难度适中但“五脏俱全”,涉及前端展示、后端逻辑、数据库设计、网络爬虫、定时任务等多个模块,能充分体现学生的综合能力。接下来,我就以一个过来人的视角,拆解一下这个系统的里里外外,聊聊怎么把它从一份源码,变成一个真正能跑起来、甚至有优化空间的个人项目。

2. 核心需求与功能模块拆解

拿到一个项目源码包,第一步不是急着运行,而是先理解它到底要干什么,以及是怎么分块干的。这个比价系统,我们可以从用户和开发者两个角度来拆解它的核心需求。

2.1 用户视角:我需要一个什么样的比价工具?

站在使用者的角度,这个系统的需求非常直观:

  1. 搜索商品:我需要一个搜索框,输入我想买的东西,比如“华为MateBook 14”。
  2. 查看结果:搜索后,我希望看到一个清晰的列表,列出京东上所有相关的商品。每条信息至少应该包括:商品主图、标题、价格、店铺名称、是否是京东自营、商品详情页链接。
  3. 排序与筛选:面对几十上百个结果,我需要能按价格从低到高排序,这样最便宜的就排在最前面。最好还能筛选“仅看自营”或者“仅看有货”。
  4. 价格追踪:对于我特别关注的商品(比如准备等降价再买的显卡),我希望系统能定期(比如每天)自动刷新它的价格,并记录历史价格变化,生成一个价格走势图,让我知道现在是不是入手的好时机。

这些需求映射到产品功能上,就构成了系统的前端界面和核心交互。

2.2 开发者视角:系统由哪些技术模块构成?

为了实现上述功能,从开发实现层面,系统需要分解成以下几个关键模块:

  1. 数据采集模块(爬虫):这是系统的“眼睛”和“手”。它负责模拟浏览器访问京东网站,发送搜索请求,解析返回的网页HTML代码,从中提取出我们需要的商品信息(价格、标题等)。这个模块的技术核心是网络请求库(如requests)HTML解析库(如BeautifulSoup、lxml或parsel)。考虑到京东的反爬机制(如请求头校验、IP频率限制),一个健壮的爬虫还需要处理User-Agent轮换、IP代理、请求延时等问题。
  2. 数据处理与存储模块:爬虫抓回来的是原始、杂乱的文本数据。这个模块负责清洗数据(比如去除价格字符串中的“¥”符号,将其转为浮点数;统一标题格式),并将结构化后的数据保存起来。这里就会用到数据库。Django默认集成了SQLite,对于毕业设计或小规模使用完全足够。我们需要设计数据库模型(Model),比如Product表存商品基本信息,PriceHistory表存每个商品的历史价格记录。
  3. Web应用模块(Django核心):这是系统的大脑和面孔。它负责处理用户的HTTP请求。
    • 视图(Views):接收用户搜索请求,调用爬虫模块获取数据,或者从数据库查询数据,然后渲染模板返回给用户。
    • 模板(Templates):HTML页面,用于展示商品列表、价格走势图。可能会用到简单的JavaScript(如jQuery)来实现前端排序或图表渲染(用ECharts或Chart.js)。
    • 路由(URLs):定义哪个URL由哪个视图函数来处理。
    • 后台管理(Admin):Django自带的神器,可以让你通过一个Web界面轻松管理数据库里的商品和价格数据,非常适合项目演示和日常维护。
  4. 任务调度模块(可选但推荐):为了实现价格追踪,我们需要定期执行爬虫任务。这不能靠手动刷新网页。成熟的方案是引入定时任务。在Django中,可以借助django-crontab或更强大的Celery+Redis来实现。比如,设置一个每天凌晨2点运行的任务,自动爬取已关注商品的最新价格并存入PriceHistory表。

注意:在动手写爬虫之前,务必深入研究目标网站(京东)的robots.txt文件和相关服务条款。对于个人学习、小规模、低频次的抓取,通常风险较低,但一定要遵守规则,设置合理的请求间隔(如每请求一次休眠2-5秒),避免对对方服务器造成压力。这是做爬虫项目必须有的伦理和法律意识。

3. 技术栈深度解析与工具选型

一个项目的骨架是它的功能模块,而血肉则是具体的技术选型。为什么这个项目用Python+Django是黄金组合?我们来看看每个技术点的选型理由和实操要点。

3.1 为什么是Python?不仅仅是“简单”

Python成为这个项目首选,绝不仅仅因为它语法简洁。其核心优势在于庞大的生态系统,为每一个环节都提供了现成的、好用的“轮子”。

  • 爬虫环节requests库让发送HTTP请求变得像访问字典一样简单。BeautifulSouplxml提供了强大的HTML/XML解析能力,可以用类似CSS选择器的方式精准定位页面元素,提取价格和标题。对于更复杂的、动态加载(Ajax)的页面,可能需要SeleniumPlaywright来模拟真实浏览器操作,但京东的商品搜索列表页通常是静态或服务端渲染,用requests+BeautifulSoup组合大多能搞定。
  • 数据处理环节pandas虽然在这个小项目中可能用不上,但如果你需要对历史价格数据进行复杂分析(比如计算平均降价周期),它就是神器。Python内建的jsonre(正则表达式)模块对于处理API返回数据或清洗文本不可或缺。
  • 环境与依赖管理pip是Python的包管理器。使用requirements.txt文件来记录项目所有依赖包及其版本,是项目可复现的基石。一个规范的源码包,根目录下一定会有一个这个文件。

3.2 Django框架:快速搭建Web应用的“瑞士军刀”

Django遵循“MTV”(Model-Template-View)模式,与经典的MVC异曲同工。它的“开箱即用”特性,能让你省去大量造轮子的时间。

  • ORM(对象关系映射):这是Django的一大亮点。你不需要写复杂的SQL语句,用Python类就能定义数据表(Model),用类方法就能进行增删改查。例如,定义商品模型:
    # models.py from django.db import models class Product(models.Model): name = models.CharField(max_length=255, verbose_name='商品名称') price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='当前价格') shop = models.CharField(max_length=100, verbose_name='店铺名称') is_self_operated = models.BooleanField(default=False, verbose_name='是否自营') product_url = models.URLField(verbose_name='商品链接') created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) def __str__(self): return self.name
    这样,在视图里你就可以用Product.objects.filter(name__contains=“手机”).order_by(‘price’)这样的语句来查询和排序,Django会自动帮你生成并执行对应的SQL。
  • Admin后台:只需在admin.py中简单注册你的模型,一个功能完备的数据管理后台就生成了。这对于毕业设计演示和项目初期数据维护来说,效率极高。
  • 表单与验证:Django提供了强大的表单类,能轻松处理用户输入的搜索关键词,并进行安全验证和清洗。
  • 安全性:Django内置了CSRF防护、SQL注入防护、XSS防护等众多安全机制,对于新手开发者而言,这意味着很多安全坑已经被默认填上了。

3.3 前端展示:轻量级与实用性的平衡

作为毕业设计,前端通常不是重点,但良好的展示能极大提升项目质感。

  • 模板语言:Django有自己的模板语言(DTL),它允许你在HTML中嵌入动态变量和简单的逻辑。比如在商品列表页循环展示商品:
    <!-- product_list.html --> {% for product in products %} <div class="product-item"> <h3><a href="{{ product.product_url }}" target="_blank">{{ product.name }}</a></h3> <p>价格:<span class="price">¥{{ product.price }}</span></p> <p>店铺:{{ product.shop }} {% if product.is_self_operated %}<span class="badge">自营</span>{% endif %}</p> </div> {% endfor %}
  • CSS框架:为了快速得到一个美观的界面,强烈推荐使用Bootstrap。它提供了现成的网格系统、按钮、卡片、表格样式,你只需要给HTML元素加上对应的class,就能获得一个响应式、看起来挺专业的页面。把Bootstrap的CSS和JS文件通过CDN引入,几乎零成本。
  • 简单交互:对于排序、筛选这类操作,有两种实现方式。一是提交表单,后端处理后再刷新页面,简单但体验一般。二是用jQuery发起Ajax请求,后端返回JSON数据,前端JavaScript动态更新页面,体验更流畅。对于毕设,第一种方式完全足够,且更易于理解和实现。

3.4 数据持久化:SQLite还是MySQL?

Django默认使用SQLite,它是一个文件数据库,无需安装单独的服务,非常适合开发、测试和小型应用。对于毕业设计这个量级,SQLite是首选,因为它让项目部署变得极其简单——你只需要把整个项目文件夹拷贝到服务器上就行。 但是,如果你预计要爬取和存储海量商品的历史价格数据(比如数万条记录),或者需要进行更复杂的联表查询,那么可以考虑换用MySQLPostgreSQL。它们性能更强,更适合生产环境。在Django中切换数据库,基本上只需要修改settings.py中的DATABASES配置即可,ORM层的代码几乎不用动,这就是Django ORM的威力。

4. 核心实现步骤与代码剖析

理解了架构和选型,我们进入实战环节,看看一个最简化的比价系统是如何一步步构建起来的。我会以关键代码片段为例,解释其背后的逻辑。

4.1 第一步:搭建Django项目骨架

首先,确保你的环境已安装Python(3.8以上版本)和pip。然后通过pip安装Django。

# 安装Django pip install django # 创建项目,项目名假设为 jd_price_comparison django-admin startproject jd_price_comparison cd jd_price_comparison # 创建应用,一个应用代表一个功能模块,比如叫 price_crawler python manage.py startapp price_crawler

创建应用后,别忘了在项目settings.py文件的INSTALLED_APPS列表里添加‘price_crawler’

4.2 第二步:设计数据模型(Models)

price_crawler/models.py中定义我们的核心模型。除了之前提到的Product,我们还需要一个PriceHistory来记录价格变化。

from django.db import models class Product(models.Model): # 商品唯一标识,可以用京东的商品ID(SKU) sku_id = models.CharField(max_length=50, unique=True, verbose_name='商品ID') name = models.CharField(max_length=500, verbose_name='商品名称') # 京东商品标题可能很长 image_url = models.URLField(blank=True, verbose_name='图片链接') detail_url = models.URLField(verbose_name='详情页链接') shop_name = models.CharField(max_length=200, verbose_name='店铺名称') is_self_operated = models.BooleanField(default=False, verbose_name='京东自营') # 当前价格,用于快速展示 current_price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='当前价格') # 其他可能需要的字段:好评率、评论数、促销信息等 created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: ordering = [‘-updated_at’] # 默认按更新时间倒序排列 def __str__(self): return f"{self.name} ({self.sku_id})" class PriceHistory(models.Model): product = models.ForeignKey(Product, on_delete=models.CASCADE, related_name=‘price_history’) price = models.DecimalField(max_digits=10, decimal_places=2, verbose_name='价格') created_at = models.DateTimeField(auto_now_add=True) # 记录价格的时间点 class Meta: ordering = [‘created_at’] verbose_name_plural = ‘价格历史’ def __str__(self): return f"{self.product.name} - ¥{self.price} at {self.created_at}"

定义好模型后,运行python manage.py makemigrationspython manage.py migrate命令,Django就会在SQLite数据库中创建对应的数据表。

4.3 第三步:编写爬虫核心逻辑

price_crawler目录下新建一个文件crawler.py,这里封装爬取逻辑。

import requests import re from bs4 import BeautifulSoup import time import random from .models import Product, PriceHistory class JDCrawler: def __init__(self): self.headers = { ‘User-Agent’: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ...‘, ‘Referer’: ‘https://www.jd.com/‘, } self.search_url_template = “https://search.jd.com/Search?keyword={}&enc=utf-8” def get_search_page(self, keyword, page=1): """获取搜索页HTML内容""" url = self.search_url_template.format(keyword) # 添加分页参数,京东搜索页分页逻辑较复杂,有时需要结合‘&page=‘和‘&s=‘参数,这里做简化 if page > 1: url += f“&page={page}” try: # 添加随机延时,模拟真人操作,避免被封IP time.sleep(random.uniform(1, 3)) resp = requests.get(url, headers=self.headers, timeout=10) resp.raise_for_status() # 检查请求是否成功 resp.encoding = ‘utf-8’ return resp.text except requests.RequestException as e: print(f“请求失败: {e}”) return None def parse_search_page(self, html): """解析搜索页,提取商品列表""" if not html: return [] soup = BeautifulSoup(html, ‘lxml’) # 京东搜索页的商品列表通常放在id为‘J_goodsList’的ul标签里,li的class包含‘gl-item’ items = soup.select(‘#J_goodsList .gl-item’) product_list = [] for item in items: try: # 提取商品ID (SKU) sku = item.get(‘data-sku’) or item.get(‘sku’) if not sku: continue # 提取商品名称 name_elem = item.select_one(‘.p-name em’) name = name_elem.get_text(strip=True) if name_elem else ‘’ # 提取价格 - 注意:京东搜索页的价格可能是动态加载的,这里可能抓不到。 # 更可靠的方法是抓取后,再通过商品ID去请求价格接口。 price_elem = item.select_one(‘.p-price i’) price = float(price_elem.get_text(strip=True)) if price_elem else 0.0 # 提取店铺 shop_elem = item.select_one(‘.p-shop’) shop = shop_elem.get_text(strip=True) if shop_elem else ‘’ # 判断是否自营(通常店铺名包含‘京东自营’或有一个特殊标识) is_self = ‘京东自营’ in shop or item.select_one(‘.p-icons .J-im-icon’) is not None # 商品详情页链接 link_elem = item.select_one(‘.p-img a’) detail_url = ‘https:’ + link_elem[‘href’] if link_elem and link_elem.get(‘href’) else ‘’ # 图片链接 img_elem = item.select_one(‘.p-img img’) img_url = img_elem.get(‘data-lazy-img’) or img_elem.get(‘src’) if img_elem else ‘’ if img_url and not img_url.startswith(‘http’): img_url = ‘https:’ + img_url product_list.append({ ‘sku_id’: sku, ‘name’: name, ‘current_price’: price, ‘shop_name’: shop, ‘is_self_operated’: is_self, ‘detail_url’: detail_url, ‘image_url’: img_url, }) except Exception as e: print(f“解析商品条目时出错: {e}”) continue return product_list def crawl_and_save(self, keyword): """主爬取函数:爬取关键词,并保存到数据库""" print(f“开始爬取关键词: {keyword}”) html = self.get_search_page(keyword) products_data = self.parse_search_page(html) for p_data in products_data: # 使用update_or_create,如果商品已存在则更新价格,否则创建新记录 product, created = Product.objects.update_or_create( sku_id=p_data[‘sku_id’], defaults={ ‘name’: p_data[‘name’], ‘current_price’: p_data[‘current_price’], ‘shop_name’: p_data[‘shop_name’], ‘is_self_operated’: p_data[‘is_self_operated’], ‘detail_url’: p_data[‘detail_url’], ‘image_url’: p_data[‘image_url’], } ) # 无论新旧,都创建一条价格历史记录 PriceHistory.objects.create(product=product, price=p_data[‘current_price’]) status = “新增” if created else “更新” print(f“{status}商品: {product.name},价格: ¥{product.current_price}”) print(f“关键词 ‘{keyword}’ 爬取完成,共处理{len(products_data)}条商品。”)

实操心得:京东的页面结构可能会变化,select语句中使用的CSS选择器需要根据实际情况调整。最稳妥的方法是打开京东搜索页,使用浏览器的“开发者工具”(F12)查看商品列表的HTML结构。另外,搜索页显示的价格可能是静态的或需要JS加载,上述代码可能抓不到实时价格。更专业的做法是抓取商品ID(SKU)后,再去请求京东的一个内部价格API(通过浏览器网络请求分析获得),但这涉及更复杂的逆向工程,作为毕设,用页面上的价格做演示已经足够。

4.4 第四步:创建视图与模板

首先,在price_crawler/views.py中编写处理搜索和展示的视图。

from django.shortcuts import render from django.http import JsonResponse from .crawler import JDCrawler from .models import Product def search_view(request): """处理搜索请求""" if request.method == ‘GET’: keyword = request.GET.get(‘q’, ‘’).strip() if keyword: # 调用爬虫(这里为了演示,直接同步调用。实际应考虑异步任务,避免请求超时) crawler = JDCrawler() crawler.crawl_and_save(keyword) # 从数据库获取刚爬取的商品 products = Product.objects.filter(name__icontains=keyword).order_by(‘current_price’) else: products = Product.objects.none() return render(request, ‘price_crawler/search_results.html’, {‘products’: products, ‘keyword’: keyword}) return render(request, ‘price_crawler/search.html’) def product_list_view(request): """商品列表页,展示所有已爬取的商品""" products = Product.objects.all().order_by(‘-updated_at’) return render(request, ‘price_crawler/product_list.html’, {‘products’: products})

然后,配置URL路由。在price_crawler目录下创建urls.py

from django.urls import path from . import views urlpatterns = [ path(‘search/’, views.search_view, name=‘search’), path(‘list/’, views.product_list_view, name=‘product_list’), ]

在项目主urls.py中包含它:

from django.contrib import admin from django.urls import path, include urlpatterns = [ path(‘admin/’, admin.site.urls), path(‘’, include(‘price_crawler.urls’)), ]

最后,创建模板文件。在price_crawler下新建templates/price_crawler/目录,然后创建search_results.html

<!DOCTYPE html> <html lang=“zh-CN”> <head> <meta charset=“UTF-8”> <meta name=“viewport” content=“width=device-width, initial-scale=1.0”> <title>京东比价 - 搜索结果</title> <link href=“https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css” rel=“stylesheet”> </head> <body> <div class=“container mt-4”> <h2>搜索关键词: “{{ keyword }}”</h2> <a href=“{% url ‘search’ %}” class=“btn btn-secondary mb-3”>返回搜索</a> {% if products %} <div class=“table-responsive”> <table class=“table table-hover table-striped”> <thead> <tr> <th scope=“col”>图片</th> <th scope=“col”>商品名称</th> <th scope=“col”>价格</th> <th scope=“col”>店铺</th> <th scope=“col”>自营</th> <th scope=“col”>操作</th> </tr> </thead> <tbody> {% for product in products %} <tr> <td><img src=“{{ product.image_url }}” alt=“{{ product.name }}” style=“max-height: 60px;” onerror=“this.style.display=‘none’”></td> <td><a href=“{{ product.detail_url }}” target=“_blank”>{{ product.name|truncatechars:80 }}</a></td> <td class=“fw-bold text-danger”>¥{{ product.current_price }}</td> <td>{{ product.shop_name|truncatechars:20 }}</td> <td>{% if product.is_self_operated %}<span class=“badge bg-success”>是</span>{% else %}<span class=“badge bg-secondary”>否</span>{% endif %}</td> <td><a href=“#” class=“btn btn-sm btn-outline-primary” onclick=“trackPrice(‘{{ product.sku_id }}’)“>关注</a></td> </tr> {% endfor %} </tbody> </table> </div> {% else %} <div class=“alert alert-info”>未找到相关商品或尚未爬取数据。</div> {% endif %} </div> <script src=“https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js”></script> <script> function trackPrice(skuId) { alert(‘关注功能(SKU: ’ + skuId + ‘)需后端配合定时任务实现。此处为前端演示。’); // 实际应发送Ajax请求到后端,将商品加入监控列表 } </script> </body> </html>

4.5 第五步:启用Admin后台并创建超级用户

Django Admin是快速管理数据的利器。在price_crawler/admin.py中注册模型:

from django.contrib import admin from .models import Product, PriceHistory class PriceHistoryInline(admin.TabularInline): # 内联显示,方便在商品页面直接看价格历史 model = PriceHistory extra = 0 readonly_fields = (‘created_at’,) @admin.register(Product) class ProductAdmin(admin.ModelAdmin): list_display = (‘name’, ‘current_price’, ‘shop_name’, ‘is_self_operated’, ‘updated_at’) list_filter = (‘is_self_operated’, ‘shop_name’) search_fields = (‘name’, ‘sku_id’) inlines = [PriceHistoryInline] @admin.register(PriceHistory) class PriceHistoryAdmin(admin.ModelAdmin): list_display = (‘product’, ‘price’, ‘created_at’) list_filter = (‘created_at’,) date_hierarchy = ‘created_at’

然后创建超级用户,运行python manage.py createsuperuser,按提示输入用户名、邮箱和密码。运行开发服务器python manage.py runserver,访问http://127.0.0.1:8000/admin/即可登录后台管理所有数据。

5. 部署、优化与问题排查

一个能在本地跑起来的系统只是第一步。要让别人也能访问,或者让它长期稳定运行,还需要考虑部署和优化。

5.1 简易部署方案(以Linux服务器为例)

对于毕业设计演示或个人使用,部署到一台云服务器是最佳选择。

  1. 准备服务器:购买一台最低配置的云服务器(如1核1G),安装Ubuntu系统。
  2. 上传代码:通过Git或SFTP工具将你的Django项目代码上传到服务器。
  3. 安装环境:在服务器上安装Python、pip、虚拟环境(virtualenv)。
    sudo apt update sudo apt install python3-pip python3-venv cd /path/to/your/project python3 -m venv venv source venv/bin/activate pip install -r requirements.txt # 安装项目依赖
  4. 配置生产设置:修改settings.py,关闭调试模式DEBUG = False,设置允许访问的域名ALLOWED_HOSTS = [‘你的服务器IP或域名’]。静态文件收集:python manage.py collectstatic
  5. 使用WSGI服务器:开发服务器(runserver)不适合生产。使用Gunicorn作为WSGI服务器。
    pip install gunicorn gunicorn --workers 3 your_project_name.wsgi:application
  6. 搭配Nginx:用Nginx作为反向代理,处理静态文件并转发动态请求给Gunicorn。配置Nginx站点文件,指向你的项目静态文件目录和Gunicorn socket。
  7. 进程管理:使用Supervisor来管理Gunicorn进程,确保应用崩溃后能自动重启。

5.2 性能与反爬优化策略

  1. 爬虫优化

    • 请求头:伪装成主流浏览器,包含完整的User-AgentRefererAccept-Language等。
    • 代理IP池:如果频繁抓取触发IP限制,需要考虑使用代理IP。有免费和付费的代理IP服务,但免费的质量不稳定。
    • 降低频率:在爬虫代码的请求间加入随机延时(time.sleep(random.uniform(2, 5))),这是最基本的道德和规避反爬的措施。
    • 错误重试:使用tenacityretrying库为请求添加重试机制,应对网络波动。
    • 异步爬取:对于大量商品,可以使用aiohttp+asyncio进行异步IO爬取,极大提升效率,但代码复杂度会增加。
  2. 数据库优化

    • 建立索引:对经常用于查询和排序的字段建立数据库索引,如Product表的namecurrent_priceupdated_at字段。在Django模型中可以通过db_index=True参数设置。
    • 分页查询:商品列表一定要做分页,使用Django内置的Paginator类,避免一次性加载海量数据。
  3. 前端优化

    • 异步加载:将搜索和价格监控改为Ajax请求,提升用户体验。
    • 价格图表:使用轻量级的图表库如Chart.js,在商品详情页展示其价格历史曲线。

5.3 常见问题与排查实录

在开发和运行这个系统的过程中,你几乎一定会遇到下面这些问题:

  1. 爬虫抓不到数据或数据错乱

    • 现象:爬虫运行后,product_list为空,或者提取的字段全是错的。
    • 排查
      • 检查网络请求:首先打印resp.status_coderesp.text的前几百字符,看请求是否成功,返回的HTML是否正常。
      • 验证选择器:京东的页面结构可能已更新。用浏览器开发者工具重新检查商品列表、价格、标题等元素对应的CSS选择器路径。
      • 处理动态内容:如果价格是JS动态加载的,在resp.text里就找不到。需要分析网络请求,找到真正的数据接口(通常是返回JSON的XHR请求),然后直接请求那个接口。
    • 解决:更新parse_search_page方法中的CSS选择器。对于动态数据,改用requests直接调用找到的API接口,并注意携带必要的请求参数和头信息(如cookies)。
  2. Django报错:TemplateDoesNotExist

    • 现象:访问页面时,Django报错找不到模板。
    • 排查:检查settings.pyTEMPLATESDIRS设置,以及你的模板文件是否放在了正确的路径下(app_name/templates/app_name/)。
    • 解决:确保项目结构正确,并在INSTALLED_APPS中注册了你的应用。
  3. 数据库操作错误:IntegrityError(UNIQUE constraint failed)

    • 现象:在保存商品时,提示唯一约束失败。
    • 排查:这通常是因为你试图插入sku_id重复的商品。update_or_create方法应该能处理这种情况,检查它的逻辑是否正确,或者是否有其他地方直接调用了Product.objects.create()
    • 解决:确保所有保存商品的地方都使用update_or_create,或者先查询是否存在,再决定是更新还是创建。
  4. 部署后静态文件(CSS, JS, 图片)404

    • 现象:网站能访问,但样式全无,浏览器控制台显示静态文件404。
    • 排查:Django在生产环境不处理静态文件。检查settings.py中的STATIC_URLSTATIC_ROOT,以及Nginx配置中是否正确指向了STATIC_ROOT目录。
    • 解决:正确运行collectstatic命令,并在Nginx配置中添加location /static/块,指向收集后的静态文件目录。
  5. 定时任务不执行

    • 现象:配置了django-crontab,但任务没有按计划运行。
    • 排查:首先确认是否将django-crontab添加到了INSTALLED_APPS,并运行了python manage.py crontab add将任务添加到系统的crontab。在Linux上,可以用crontab -l查看当前用户的任务列表。
    • 解决:确保Django项目在cron任务执行时的环境变量(尤其是Python路径)是正确的。一个常见做法是在cron命令中,先切换到项目目录并激活虚拟环境,再执行Django管理命令。例如:
      0 2 * * * cd /path/to/your/project && /path/to/venv/bin/python manage.py runcrons

这个基于Python+Django的京东商品比价系统,从学习角度看,它是一条贯穿多个核心知识点的绝佳实践路径;从实用角度看,它孵化出了一个能解决实际问题的工具原型。在实现过程中,你会深刻体会到从数据获取到最终呈现的完整闭环,也会遇到并解决各种典型的开发问题。无论是为了完成一份出色的毕业设计,还是作为个人技能提升的练手项目,它都极具价值。最后,记得在遵守规则的前提下进行技术探索,让技术服务于生活,而不是带来麻烦。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询