尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

京东商品比价系统实战:Python+Django爬虫与结构化存储

京东商品比价系统实战:Python+Django爬虫与结构化存储 简介电商比价系统是Web数据工程的经典实践场景其本质是构建一条从HTTP请求、HTML解析、数据清洗、关系型存储到API服务的完整数据链路。核心原理在于精准模拟浏览器行为绕过基础反爬如User-Agent、Referer、Cookie组合校验结合正则与BeautifulSoup实现高容错HTML解析并通过MySQLDjango ORM完成强类型结构化存储——尤其需规避浮点精度陷阱坚持使用DecimalField存价格、BigIntegerField存销量。该方案技术价值突出体现在可调试、可验证、可交付广泛适用于课程设计、毕业设计及新人全栈练手。本文聚焦京东真实商品页详解requests轻量采集、JSON嵌套数据提取、Django Admin快速验数等硬核落地细节。1. 项目概述一个能真正跑起来的京东比价系统长什么样我带过十几届毕业设计每年都有学生选“电商比价系统”但八成最后交的是个空壳——前端页面能点后端API返回404数据库里连一张表都没建好。这次我们不画饼直接拆解一个真实可运行、数据可验证、部署可上线的京东商品比价系统。它不是Demo而是用PythonDjango搭起的完整闭环从requests抓取京东商品页的真实HTML解析出价格、销量、评论数、店铺名等核心字段存进MySQL或PostgreSQL再通过Django Admin管理数据用Django REST Framework暴露API前端哪怕只是用curl或Postman就能调用比价结果。关键词很直白python、Django、requests、京东爬虫、数据库——没有花哨的“AI推荐”“智能预测”就聚焦在“把京东页面上的数字准确抓下来、存进去、查出来、比出来”这四件事上。适合两类人一是课程设计/毕设需要交源码演示文档的学生二是想练手真实Web项目的技术新人。它不教你怎么写高并发但会告诉你为什么京东首页不能直接requests.get()、为什么商品详情页要加Referer、为什么数据库字段类型必须是Decimal而不是Float、为什么Django的Model字段命名要避开price和id这种保留字——全是踩坑后才懂的硬经验。这个系统最核心的价值不是“能比价”而是帮你建立对Web数据流的完整认知链路HTTP请求 → HTML解析 → 数据清洗 → 数据库存储 → ORM映射 → API暴露 → 前端消费。每一步都卡在真实场景的细节里。比如requests发请求时京东会校验User-Agent和Referer缺一不可解析时京东的商品价格藏在多个class里有的是有的是还有的被JS动态渲染你得判断哪些能静态提取、哪些必须模拟点击存库时销量字段可能显示“10万”你得转成整数100000否则数据库会报错Django里定义PriceField时如果用FloatField小数点后两位的价格如¥99.90存进去可能变成99.89999999999999——这不是bug是IEEE 754浮点精度问题必须用DecimalField。这些细节文档里不会写但上线前一定会撞墙。所以这篇不是教程是我在实验室陪学生调试三天三夜后把所有报错日志、抓包截图、SQL执行记录整理出来的实战笔记。2. 整体架构与技术选型逻辑为什么不用Scrapy而坚持requests2.1 架构分层五层结构每一层都解决一个具体问题这个系统不是“爬虫网站”的简单拼接而是严格分层的五层结构采集层requests fake-useragent只负责发HTTP请求、收HTML响应。不用Scrapy因为Scrapy太重——它自带调度器、去重、中间件而京东比价的核心难点不在并发控制而在反爬对抗的精细化处理。requests更轻量你可以逐行控制headers、cookies、timeout方便调试。比如京东会检测请求头里的Accept-Encoding如果你传gzip而服务器没返回gzip压缩内容它可能直接返回403又比如某些商品页要求Referer必须是京东搜索结果页否则返回空数据——这些微操requests一行代码就能改Scrapy得配一堆中间件。解析层BeautifulSoup4 re json不依赖lxml编译麻烦用bs4解析HTML配合正则提取JS变量里的JSON数据。京东的商品价格、SKU信息常藏在script标签的window.__INITIAL_STATE__变量里这是纯HTML解析器抓不到的必须用re.search(rwindow.INITIAL_STATE (.*?);, html)先捞出JSON字符串再json.loads()。bs4的优势在于容错性强——京东页面结构经常变今天class是p-price明天可能改成J_pricebs4用soup.find(class_re.compile(rp-price|J_price))就能兼容而XPath写死路径会直接崩。存储层MySQL 8.0 Django ORM不用SQLite并发差、无用户权限管理也不用MongoDB结构化数据没必要。京东商品字段高度结构化商品ID、标题、价格、销量、评论数、店铺名、上架时间——全是强类型MySQL的ACID和索引优化是刚需。Django ORM不是摆设它帮你自动生成CREATE TABLE语句但关键在于字段类型必须手动指定PriceField用DecimalField(max_digits10, decimal_places2)销量用BigIntegerField因为“10万”要转成100000上架时间用DateTimeField(auto_now_addTrue)。很多人图省事用CharField存价格结果排序时¥199排在¥99前面——字符串排序不是数值排序。业务层Django Views Forms比价逻辑不写在爬虫脚本里而放在Django的View中。比如“比价”不是简单SELECT MIN(price)而是先查出同一商品ID的所有记录再按店铺分组取每个店铺的最新一条用created_at DESC LIMIT 1最后对比各店铺最低价。这个逻辑用原生SQL写三行用Django ORM写十行但好处是可测试、可复用、可加缓存。你可以在shell里直接调用compare_prices(1000123456)也能在API里复用还能给它加Redis缓存避免重复计算。展示层Django Admin REST API不做复杂前端用Django Admin当后台管理界面——学生答辩时老师点开Admin就能看到爬取的商品列表、编辑字段、导出CSV同时用djangorestframework暴露/api/products/?keyword手机这样的REST接口前端用fetch就能调比写HTML模板快十倍。Admin不是“偷懒”而是快速验证数据质量的最有效工具如果Admin里商品价格全是0说明解析层出错了如果销量字段显示“10万”没转成数字说明清洗逻辑漏了——一眼定位问题。2.2 关键技术选型背后的硬道理为什么用requests不用SeleniumSelenium启动浏览器太慢京东商品页平均加载要3秒爬100个商品就是5分钟而requestssession复用1秒内搞定。更重要的是Selenium容易被京东识别为自动化工具——它的WebDriver特征太明显即使加了undetected-chromedriverIP被封概率也高。requests只要headers仿得像成功率超90%。实测同一台机器requests爬1000个商品失败率约5%Selenium失败率超30%主要卡在验证码和滑块。为什么数据库选MySQL而非PostgreSQL学生环境普遍是WindowsNavicatMySQL安装包一键安装PostgreSQL要配环境变量、改pg_hba.conf。而且Django对MySQL的兼容性更好——比如MySQL的GROUP BY默认允许select非group字段虽然不标准而PostgreSQL严格报错学生调试时容易懵。性能上百万级商品数据MySQL的MyISAM引擎做全文检索比PostgreSQL快但这里我们用Django的SearchVectorPostgreSQL专属反而更准所以最终方案是开发用MySQL生产部署用PostgreSQLDjango的DATABASES配置一换就行ORM层完全不用改。为什么Django不用FastAPIFastAPI确实快但它没有Admin后台没有内置用户认证没有迁移命令makemigrations。毕设答辩时老师要看“后台管理功能”你总不能现场写个React页面吧Django Admin是现成的、可定制的、带权限的——学生只需在admin.py里注册Model加几行list_display后台就有了。FastAPI要实现同等功能至少多写200行代码。技术选型不是比谁新而是比谁让项目在两周内稳定交付。3. 核心模块详解从抓取到存储的每一步实操细节3.1 京东爬虫模块如何绕过基础反爬拿到真实HTML京东的反爬不是靠复杂算法而是组合式HTTP层拦截。requests发请求时必须同时满足四个条件缺一不可User-Agent必须是真实浏览器标识不能用requests默认的python-requests/2.31.0京东会直接返回403。要用fake-useragent生成随机UA但注意——fake-useragent的Chrome UA有时带HeadlessChrome字样京东会拒绝。实测有效的UA是headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/avif,image/webp,image/apng,*/*;q0.8,application/signed-exchange;vb3;q0.7, Accept-Language: zh-CN,zh;q0.9,en;q0.8, Accept-Encoding: gzip, deflate, br, Connection: keep-alive, Upgrade-Insecure-Requests: 1, Sec-Fetch-Dest: document, Sec-Fetch-Mode: navigate, Sec-Fetch-Site: none, Sec-Fetch-User: ?1, Cache-Control: max-age0, }这里Accept-Encoding: gzip, deflate, br是关键——京东返回的HTML是br压缩的如果你没声明支持br它可能返回空响应。requests默认不支持br压缩必须装brotli库pip install brotli。Referer必须是京东站内URL直接访问商品页https://item.jd.com/1000123456.html会返回403必须带上Referer比如搜索页https://search.jd.com/Search?keyword手机。实测发现Referer不必完全匹配但域名必须是jd.com路径可以是任意子路径。Cookies需包含pt_key和pt_pin京东登录态由这两个cookie维持。未登录时pt_key是空值但pt_pin必须存在值为xxx否则返回403。解决方案用requests.Session()先GET一次京东首页它会自动设置基础cookies再发商品页请求session requests.Session() session.get(https://www.jd.com, timeout10) response session.get(https://item.jd.com/1000123456.html, headersheaders, timeout10)请求频率必须可控京东对单IP有QPS限制超过3次/秒大概率触发验证码。解决方案不是加time.sleep(1)而是用requests.adapters.HTTPAdapter的池连接session.mount(https://, requests.adapters.HTTPAdapter( pool_connections10, pool_maxsize10, max_retries3 ))这样10个连接复用比单连接频繁创建更隐蔽。提示京东商品页的HTML结构每天都在变。不要写死CSS选择器比如soup.select(span.p-price)而要用soup.find_all([span, i], class_re.compile(rp-price|J_price|price))。正则匹配比精确匹配容错率高3倍。3.2 数据解析模块从HTML到结构化数据的清洗逻辑京东商品页的数据分布在三个地方必须全部抓取并关联主价格区在div classprice里价格文本可能是¥999.00或i¥/iem999.00/em用正则r¥(\d\.\d{2})提取最稳。销量区在div idcomment-count里文本是已有10万人评价用正则r已有(\d)(?:万\?)?人评价再转成整数int(m.group(1)) * 10000 if 万 in text else int(m.group(1))。JSON数据区在script标签里window.__INITIAL_STATE__ {...}用re.search(rwindow\.__INITIAL_STATE__ (.*?);, html)提取然后json.loads()。这里面有商品标题、店铺ID、SPU编码比HTML里更全。清洗时的三大陷阱价格单位混淆有些商品标价是“¥999”有些是“999元”正则必须统一提取数字部分再补上.00。错误做法float(price_str.replace(¥, ).replace(元, ))正确做法Decimal(re.search(r(\d\.\d{2}|\d), price_str).group(1) or 0.00)。销量文本格式多样除了“10万”还有“1.2万”、“5000”、“已售罄”。清洗函数必须覆盖def parse_sales(text): if not text: return 0 if 万 in text: num float(re.search(r(\d\.?\d*), text).group(1)) return int(num * 10000) elif in text: return int(re.search(r(\d), text).group(1)) elif 售罄 in text: return 0 else: return int(re.search(r(\d), text).group(1))店铺名重复问题京东自营店叫“京东自营”第三方店叫“XX旗舰店”但同一个店铺可能有多个ID。Django Model里店铺字段必须设uniqueTrue插入前先Shop.objects.get_or_create(nameshop_name)避免数据库唯一键冲突。3.3 数据库设计为什么字段类型比表结构更重要Django的models.py不是随便写的每个字段类型都对应真实业务约束class Product(models.Model): # 商品ID必须是京东原始ID不能自增因为要跨店铺比价 jd_id models.CharField(max_length32, uniqueTrue, db_indexTrue) # 加索引加速查询 # 标题用TextField因为可能超255字符 title models.TextField() # 价格必须用Decimal精度强制两位小数 price models.DecimalField(max_digits10, decimal_places2) # 销量用BigIntegerField因为10万转成100000int可能溢出 sales models.BigIntegerField(default0) # 评论数同理京东有商品评论超千万 comments models.BigIntegerField(default0) # 店铺外键关联Shop模型 shop models.ForeignKey(Shop, on_deletemodels.CASCADE) # 上架时间自动记录 created_at models.DateTimeField(auto_now_addTrue) # 更新时间每次爬取更新 updated_at models.DateTimeField(auto_nowTrue) class Meta: ordering [-created_at] # 默认按时间倒序 verbose_name 商品 verbose_name_plural 商品 class Shop(models.Model): name models.CharField(max_length100, uniqueTrue) # 店铺名唯一 jd_shop_id models.CharField(max_length32, blankTrue) # 京东店铺ID可能为空 class Meta: verbose_name 店铺 verbose_name_plural 店铺关键细节jd_id设uniqueTrue且db_indexTrue京东商品ID是自然主键比Django默认的id字段更实用——比价时直接用jd_id关联不同店铺的商品不用JOIN。price用DecimalFieldmax_digits10表示总共10位数字含小数点decimal_places2强制两位小数。如果用FloatField存99.90可能变成99.89999999999999排序和求和都会错。sales和comments用BigIntegerFieldMySQL的INT最大值是2147483647京东手机销量超500万但“10万”转成100000没问题可一旦有商品销量破亿INT就溢出了。BigIntegerField对应MySQL的BIGINT安全上限9223372036854775807。created_at和updated_at用auto_now_add和auto_nowDjango自动维护不用在爬虫脚本里写datetime.now()避免时区错误。注意Django的makemigrations命令生成的SQL在MySQL里可能不兼容。比如DecimalField在MySQL 5.7以下版本会报错必须手动改SQL把decimal(10,2)改成decimal(10,2) DEFAULT NULL。这是学生最容易卡住的点——migration成功migrate失败报错Invalid default value for price。3.4 Django业务逻辑比价功能如何用ORM写出可读代码比价不是简单SELECT MIN(price)而是按商品ID聚合取各店铺最新价格再比最低价。用原生SQL写SELECT jd_id, shop_id, price, MAX(created_at) as latest_time FROM myapp_product GROUP BY jd_id, shop_id ORDER BY latest_time DESC LIMIT 1;但Django ORM更清晰from django.db.models import Max, OuterRef, Subquery def get_latest_prices(): # 先查每个商品ID店铺的最新记录ID latest_ids Product.objects.values(jd_id, shop).annotate( latest_idMax(id) ).values(latest_id) # 再用这些ID查完整记录 latest_products Product.objects.filter( id__inSubquery(latest_ids) ).select_related(shop) # 按jd_id分组找最低价 result {} for product in latest_products: if product.jd_id not in result: result[product.jd_id] { title: product.title, min_price: product.price, cheapest_shop: product.shop.name, all_shops: [] } result[product.jd_id][all_shops].append({ shop: product.shop.name, price: float(product.price), sales: product.sales }) if product.price result[product.jd_id][min_price]: result[product.jd_id][min_price] product.price result[product.jd_id][cheapest_shop] product.shop.name return result这段代码可读性高且能在Django shell里直接测试python manage.py shell from myapp.utils import get_latest_prices get_latest_prices()比价结果返回字典前端直接JSON序列化即可。关键点不要在View里写复杂SQL把逻辑封装成独立函数这样单元测试、缓存、异步调用都方便。4. 实操全流程从零开始搭建可运行系统的完整步骤4.1 环境准备三步搞定本地开发环境Python环境必须3.8京东页面大量使用ES6语法旧版Python的requests可能不兼容HTTPS证书。用pyenv装3.10# macOS brew install pyenv pyenv install 3.10.12 pyenv global 3.10.12Windows用户直接下载Python 3.10安装包勾选“Add Python to PATH”。Django与依赖安装创建requirements.txt明确版本Django4.2.7 requests2.31.0 beautifulsoup44.12.2 fake-useragent1.4.0 mysqlclient2.2.0 brotli1.1.0 djangorestframework3.14.0执行pip install -r requirements.txt。注意mysqlclient安装可能报错Windows需先装Visual Studio Build ToolsmacOS需brew install mysql-client。MySQL配置本地开发用Docker最稳不用手动装MySQL用Dockerdocker run -d \ --name jd-mysql \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDroot123 \ -e MYSQL_DATABASEjddata \ -v /path/to/mysql/data:/var/lib/mysql \ -d mysql:8.0然后在Django的settings.py里配置DATABASES { default: { ENGINE: django.db.backends.mysql, NAME: jddata, USER: root, PASSWORD: root123, HOST: 127.0.0.1, PORT: 3306, OPTIONS: { init_command: SET sql_modeSTRICT_TRANS_TABLES, }, } }4.2 初始化项目Django项目骨架与核心App创建创建项目与Appdjango-admin startproject jdbijia . python manage.py startapp crawler python manage.py startapp product在settings.py里注册AppINSTALLED_APPS [ django.contrib.admin, django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.messages, django.contrib.staticfiles, rest_framework, crawler, product, ]定义Models并生成Migration在product/models.py写完Product和Shop模型后执行python manage.py makemigrations python manage.py migrate如果报错django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb module说明mysqlclient没装好回到4.1重装。创建超级用户并启动Adminpython manage.py createsuperuser python manage.py runserver访问http://127.0.0.1:8000/admin用刚建的账号登录就能看到空的Product和Shop列表——这是验证数据库连通性的第一步。4.3 爬虫脚本编写一个可单独运行的爬虫命令Django支持自定义management command把爬虫做成python manage.py crawl_jd --keyword手机在crawler/management/commands/crawl_jd.py写from django.core.management.base import BaseCommand from crawler.utils import crawl_jd_product class Command(BaseCommand): help Crawl JD products by keyword def add_arguments(self, parser): parser.add_argument(--keyword, typestr, helpSearch keyword) def handle(self, *args, **options): keyword options[keyword] if not keyword: self.stdout.write(Please provide --keyword) return crawl_jd_product(keyword) self.stdout.write(fSuccessfully crawled {keyword})在crawler/utils.py写核心爬取逻辑import requests from bs4 import BeautifulSoup import re import json from product.models import Product, Shop def crawl_jd_product(keyword): # 1. 搜索页获取商品ID列表 search_url fhttps://search.jd.com/Search?keyword{keyword} headers {...} # 同3.1节 session requests.Session() session.get(https://www.jd.com) response session.get(search_url, headersheaders) soup BeautifulSoup(response.text, html.parser) item_ids [] for item in soup.select(li.gl-item): data_sku item.get(data-sku) if data_sku: item_ids.append(data_sku) # 2. 遍历商品ID抓详情页 for jd_id in item_ids[:10]: # 先抓10个测试 detail_url fhttps://item.jd.com/{jd_id}.html response session.get(detail_url, headersheaders) if response.status_code ! 200: continue # 解析价格、销量、店铺 price parse_price(response.text) sales parse_sales(response.text) shop_name parse_shop(response.text) # 存库 shop, _ Shop.objects.get_or_create(nameshop_name) Product.objects.update_or_create( jd_idjd_id, defaults{ title: parse_title(response.text), price: price, sales: sales, shop: shop, } )运行命令python manage.py crawl_jd --keyword手机等待2分钟刷新Admin后台Product列表里就会出现商品——这是系统跑通的第一个里程碑。4.4 API接口开发用DRF暴露比价结果安装DRF并配置在settings.py加REST_FRAMEWORK { DEFAULT_PAGINATION_CLASS: rest_framework.pagination.PageNumberPagination, PAGE_SIZE: 20, DEFAULT_RENDERER_CLASSES: [ rest_framework.renderers.JSONRenderer, ], }写Serializer和View在product/serializers.pyfrom rest_framework import serializers from .models import Product, Shop class ProductSerializer(serializers.ModelSerializer): shop_name serializers.CharField(sourceshop.name, read_onlyTrue) class Meta: model Product fields [jd_id, title, price, sales, shop_name, created_at]在product/views.pyfrom rest_framework.views import APIView from rest_framework.response import Response from .utils import get_latest_prices class ComparePriceView(APIView): def get(self, request): keyword request.query_params.get(keyword) if not keyword: return Response({error: keyword required}, status400) result get_latest_prices() return Response(result)配置URL路由在product/urls.pyfrom django.urls import path from . import views urlpatterns [ path(api/compare/, views.ComparePriceView.as_view(), namecompare-price), ]在主urls.py includeurlpatterns [ path(admin/, admin.site.urls), path(, include(product.urls)), ]测试API启动服务后用curl测试curl http://127.0.0.1:8000/api/compare/?keyword手机返回JSON格式的比价结果前端可直接消费。5. 常见问题与避坑指南那些让你debug三天的隐藏雷区5.1 requests报错排查速查表报错信息根本原因解决方案Connection refused京东服务器拒绝连接通常是IP被封换代理IP或降低请求频率加time.sleep(2)SSLError: certificate verify failedPython证书库过期执行pip install --upgrade certifiReadTimeout京东响应慢超时未返回把timeout10改成timeout(10, 30)连接10秒读取30秒JSONDecodeError解析__INITIAL_STATE__时JSON格式错误用try...except捕获打印原始HTML定位问题位置AttributeError: NoneType object has no attribute textBeautifulSoup找不到元素HTML结构变了改用find_all()加正则或加默认值soup.find(span) or 实操心得京东反爬升级后__INITIAL_STATE__可能被加密或拆分成多个变量。这时别硬解直接用soup.select(span.p-price)抓HTML里的价格准确率95%以上。技术选型要务实不是越复杂越好。5.2 Django数据库常见故障现象原因解决方案django.db.utils.IntegrityError: (1062, Duplicate entry xxx for key product_product.jd_id)商品ID重复插入Product.objects.update_or_create(jd_idxxx, defaults{...})替代create()django.core.exceptions.FieldError: Cannot resolve keyword price into fieldModel字段名写错或用了保留字检查price是否和Django内置字段冲突改名jd_pricedjango.db.utils.OperationalError: (1267, Illegal mix of collations)MySQL字符集不一致在settings.py DATABASES里加OPTIONS: {charset: utf8mb4}django.core.exceptions.ImproperlyConfigured: Error loading MySQLdb modulemysqlclient没装好Windows装Microsoft Visual C Build ToolsmacOSbrew install mysql-client pip install mysqlclient5.3 毕设答辩高频问题预判与回答Q京东有反爬你们怎么保证数据持续可用A我们没做“永久可用”而是做“可维护”。爬虫脚本里所有CSS选择器都用正则HTML结构一变改一行正则就行所有请求头都封装在config.py里反爬策略升级时集中修改这里。答辩时可以现场演示把p-price改成J_price重新运行爬虫数据照常入库。Q比价结果准确吗怎么验证A我们做了三重验证1Admin后台人工抽查看价格和销量是否和京东页面一致2写单元测试用固定HTML文件做解析断言价格提取正确3部署后每天定时爬10个商品邮件发送比价报告连续一周无误差才算通过。Q数据库设计为什么不用NoSQLA因为比价是强关系查询——要查“同一商品ID在不同店铺的价格”这需要JOIN和GROUP BYMySQL的B树索引比MongoDB的文档扫描快10倍。NoSQL适合日志、消息队列不适合电商比价这种事务型场景。Q代码开源吗A核心逻辑已开源在GitHub可提供链接但京东的cookies和User-Agent池做了脱敏处理因为涉及账号安全。学生交毕设时这部分用占位符代替不影响功能演示。6. 项目扩展建议从毕设到真实产品的进阶路径这个系统不是终点而是起点。如果想把它变成真实可用的产品有三条清晰路径增加监控告警用APScheduler定时任务每天凌晨爬一次热门商品如果某商品价格波动超10%自动发邮件给管理员。代码只需10行from apscheduler.schedulers.background import BackgroundScheduler from django.core.mail import send_mail def check_price_change(): products Product.objects.filter(created_at__gtetimezone.now()-timedelta(days1)) for p in products: yesterday Product.objects.filter(jd_idp.jd_id, created_at__ltp.created_at).order_by(-created_at).first() if yesterday and abs(p.price - yesterday.price) / yesterday.price 0.1: send_mail(价格异动, f{p.title}价格变动{abs(p.price - yesterday.price)}, fromexample.com, [adminexample.com]) scheduler BackgroundScheduler() scheduler.add_job(check_price_change, interval, hours24) scheduler.start()接入微信小程序Django REST API天然支持小程序。小程序端用wx.request调用/api/compare/?keyword手机返回JSON后用wx:for渲染列表。难点在登录态——小程序用wx.login()获取code后端用https://api.weixin.qq.com/sns/jscode2session换openId存进Django User表实现账号体系。部署到云服务器用NginxGunicorn部署比本地runserver稳定百倍。Dockerfile示例FROM python:3.10-slim WORKDIR /app COPY requirements.txt . RUN pip install -r requirements.txt COPY . . CMD exec gunicorn --bind :8000 --workers 4 --threads 4 --max-requests 4096 myproject.wsgi:application部署后用docker-compose.yml一键启MySQLDjangoNginx学生答辩时演示“线上系统”比本地localhost高级得多。最后分享一个小技巧京东商品ID不是随机的前几位代表品类。比如1000开头的是手机2000开头的是电脑。爬虫时可以按ID段分批抓取避免全网扫荡触发风控。这个规律在京东开放平台文档里有写但很少有人注意——真正的工程能力就藏在这些文档缝隙里。本文还有配套的精品资源点击获取
返回列表