
计算机毕设这块只要题目里带了“全网自动比价系统”这几个字基本默认你绕不开爬虫。我当时选这个题的时候以为就是写几个Python爬虫脚本把几个电商平台的价格抓回来再写个页面比一比做完交差。结果真正跑起来才发现一个能用的自动比价系统难点根本不在“爬”而在后面那一堆看起来不显眼的活商品匹配、价格规范化、反爬对抗、增量更新、防封号、还有数据展示层的性能。这篇文章把我做这套系统的完整思路、技术选型、核心代码、踩坑记录都整理出来。不管你是要拿来做毕业设计还是想在业余时间搭一个给自己用的比价工具都能直接照着改。涉及的技术点包括Python爬虫、requests与Scrapy、XPath解析、分布式爬虫、Java后端Controller层的反爬设计、前端安全加固以及比价算法中商品匹配与价格标准化的细节。我尽量用项目里真实遇到的情况来讲而不是泛泛谈理论。1. 选型场景为什么做全网自动比价系统1.1 毕设和实际业务中的真实痛点全网自动比价系统看着是一个“比较通用”的题目但不同人做出来的东西差距非常大。有人做出来就是个静态数据展示页价格用Excel手工导入有人做出来是真能每天自动抓取几十个平台、上千个商品、持续稳定运行几个月的完整系统。差别就在于你有没有把“获取数据、清洗数据、匹配商品、计算价格、更新缓存、展示结果”这一整条链路想清楚。毕设场景里最常见的问题是老师只看你的工作量和技术含量不关心你是不是真的“全网”。所以你在设计时一定要有明确的业务闭环。系统至少要能验证三个问题第一数据是不是自动获取的第二商品匹配逻辑是不是可靠第三系统在长时间运行后能不能保持稳定。如果只是为了答辩演示那抓三五个站点就够了但如果你以后想自己用就必须提前考虑平台改版、反爬升级、商品上下架这些变化因素。我在设计时确定的目标是先抓自营类电商平台、游戏交易平台和跨境电商平台的三类数据源商品固定为50个左右的测试样本每天定时抓取两次。这样做的好处是数据量可控出问题能快速定位坏处是展示效果不够“全网”。所以后来我把数据采集层设计成插件式结构每个平台对应一个独立的爬虫模块抓取任务通过配置文件驱动新增平台时不需要改主流程代码。这样既保证了毕设范围内能稳定演示又能在后期方便扩展。1.2 技术选型Python爬虫加Java后端的搭配逻辑这套系统的技术栈是典型的“混合架构”采集层用Python业务层用Java。很多同学会纠结到底该全用Python还是全用Java我的建议是各干各的擅长事。Python在爬虫领域的生态优势太明显了。requests处理HTTP请求lxml负责XPath和HTML解析Scrapy可以做分布式采集Selenium解决动态渲染页面这些库在Java生态里都有替代品但用起来远没有Python顺手。尤其是写解析规则的时候Python的交互式开发体验能帮你省很多时间。你写了一个XPath表达式直接在命令行里试一下就知道了不用反复编译打包。而Java这边Spring Boot写后端接口、管理定时任务、对接数据库整体工程结构会更清晰。Java的类型安全和成熟框架在处理并发、缓存、接口校验这些场景时也更稳。比价系统的核心操作是高频读取商品数据和价格数据用Spring Cache加Redis做缓存用MyBatis-Plus操作数据库开发效率很高。当然也有一个更好的分工Python采集后把数据写入消息队列或数据库Java只负责读取和展示两边通过JSON格式做数据交换。这样可以避免两种语言之间函数调用的复杂度。我实际用的是“Python写MySQL再同步Redis”的方式爬虫解析完成后直接操作数据库把商品基础信息、价格快照写进去Java后端从数据库读数据并缓存到Redis。数据一致性通过版本号和更新时间字段来控制。1.3 全网比价系统的功能边界与预期效果做项目之前先把边界定清楚不然很容易陷入“什么都想抓什么都抓不好”的局面。我当时给系统划分了五个核心功能模块数据采集、数据清洗、商品匹配、价格对比、前端展示。数据采集负责定时抓取目标平台的商品列表页和详情页数据清洗处理标题、价格、图片、销量、店铺名等字段商品匹配解决“不同平台卖的同一个商品怎么识别”的问题价格对比在匹配结果基础上排序并计算最低价、最高价、价格差前端展示提供商品搜索、价格历史曲线、平台跳转入口。至于“全网”这个概念我理解的不是字面意义上的全互联网而是覆盖主流目标平台的聚合比价。相比功能范围运行稳定性更重要。一个每天挂掉的“全网比价系统”反而比一个稳定跑三个月的单平台比价工具更没有说服力。所以我在项目说明里也写清楚了支持平台的列表并留了扩展方式。2. 系统整体架构与数据流转2.1 数据流设计从采集到展示要经过几个环节我画数据流图的时候把整条链路分成了六个阶段。采集层用Python定时任务触发请求目标平台获取HTML或JSON解析层提取商品ID、标题、价格、店铺、销量等信息并统一成标准结构清洗层处理缺失字段和异常值同时做编码转换存储层把清洗后的数据写入MySQL同步层把热点数据加载到Redis展示层通过Controller接口把价格对比结果返回给前端页面。这里有一个特别容易被忽略的点商品数据不是一成不变的。同一个商品今天99元明天可能调价到109元还有可能下架、缺货、商家改名。所以数据库里必须同时有“商品表”和“价格快照表”。商品表保存商品主键和基础信息价格快照表保存每一次采集到的价格、库存、采集时间。这样前端才能画价格走势曲线答辩时展示出来特别加分。我在做表结构时还加了一个“采集任务日志表”记录每次任务的开始时间、结束时间、成功数、失败数、错误原因。这个表看起来不起眼但后期排查问题全靠它。比如某天某个平台的数据全是0你一看日志发现是请求头过期导致被反爬拦截几分钟就能定位问题不用对着数据发呆。2.2 关键表结构设计商品主表和价格快照表是这套系统的地基。商品表不需要太复杂核心字段是平台编码、平台商品ID、商品标题、商品URL、店铺名称、图片URL、分类ID。平台编码很关键它用来区分同一个商品在不同平台的记录比如platform_code steam、platform_code zbg藏宝阁。价格快照表的字段则要细一些自增ID、平台商品ID、价格、原价、优惠价、库存状态、采集时间、来源任务ID。为什么要单独存一份快照表而不是直接在商品表上更新价格因为比价系统最核心的“价格历史”功能需要这张表。如果你只保存最新价格就没法分析价格走势也没法知道某个平台昨天是不是有过一次性降价。商品匹配结果表用来存“同款商品的关系”这是比价的基础。比如你在京东找到了“iPhone 15 128G 蓝色”在淘宝也找到一条记录系统需要能自动判断它们是同一个商品然后通过group_id字段把它们关联起来。这个表的结构很简单但写匹配算法的过程很麻烦后面我会详细讲。这三个表建好后整个系统的查询路径就很清晰了前端请求商品搜索接口 → Java Controller查Redis缓存 → 缓存未命中则查MySQL商品表 → 根据group_id找到同组所有平台的记录 → 查询最新价格快照 → 组装返回结果。2.3 爬虫目录结构与运行机制我把Python爬虫部分设计成独立工程目录结构大致是这样的spider_project/ ├── requirements.txt ├── config/ │ ├── settings.py │ └── platforms.json ├── core/ │ ├── http_client.py │ ├── parser.py │ └── pipeline.py ├── spiders/ │ ├── base.py │ ├── steam_spider.py │ ├── zbg_spider.py │ └── demo_spider.py ├── utils/ │ ├── user_agents.py │ ├── proxy_pool.py │ └── logger.py └── run.py运行机制上我没有直接用Scrapy的CrawlerProcess而是用APScheduler写了定时调度。每天上午10点和晚上8点各触发一次全量任务中间每小时跑一次轻量增量任务只采集热门前50商品的详情页。这样做的原因是价格波动往往集中在几个爆款商品上全量采集成本太高增量采集性价比更好。config/platforms.json 里维护每个平台的请求域名、列表页URL模板、详情页URL模板、解析规则类型和请求头模板。新增平台时不用改代码只要往配置文件里加一条记录然后实现对应的解析类就行。这个设计虽然前期多花了一点时间但后期维护成本大大降低。3. 爬虫核心实现细节3.1 从零搭建Python爬虫requests的正确姿势很多python爬虫入门教程都会教你直接用requests.get抓页面然后打印状态码。真实项目中绝对不能这么写。比价系统要长期稳定运行第一步就要把请求层封装好。我在core/http_client.py里封装了一个带重试机制、随机延迟、代理切换的请求类。关键点有三个一是请求头必须随机轮换不能每次都带着同一个User-Agent二是必须有重试机制遇超时、连接错误、状态码429或503时自动切换代理重试最多重试3次三是两次请求之间要加随机延迟避免请求频率看起来像机器。举个例子请求头我维护了一个常用User-Agent池里面包含Windows Chrome、macOS Safari、Android Chrome、iOS Safari等多个客户端标识。每次请求前随机选一个再配合一个随机生成的浏览器指纹参数# 简单示例随机UA和延迟 import random import time USER_AGENTS [ Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ... Chrome/120.0 Safari/537.36, Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 ... Version/17.2 Safari/605.1.15, ] def get_headers(): return { User-Agent: random.choice(USER_AGENTS), Accept: text/html,application/xhtmlxml,application/xml;q0.9,image/webp,*/*;q0.8, Accept-Language: zh-CN,zh;q0.9,en;q0.6, } def safe_request(url, sessionNone, retry3): session session or requests.Session() for i in range(retry): try: time.sleep(random.uniform(1.0, 3.0)) resp session.get(url, headersget_headers(), timeout10) if resp.status_code 200: return resp if resp.status_code in (403, 429, 503): continue except requests.RequestException as e: continue return None这里要注意time.sleep的延迟范围要根据目标平台调整。有些平台对请求频率极其敏感平均请求间隔小于3秒就会被封IP有些平台相对宽松1秒一次没问题。建议先从小频率开始逐步加大直到找到临界值。3.2 XPath解析与text()函数的使用技巧拿到HTML之后解析规则的准确性直接决定数据质量。我主要用lxml库来解析XPath表达式是核心。很多新手在写XPath时会踩一个坑想要提取某个节点下的所有文本却不知道text()函数的具体行为。text()只能获取当前节点的直接文本节点不能获取子节点里的文本。比如div classprice-wrap 优惠价 span classprice-num2999/span 元 /div如果你写//div[contains(class,price-wrap)]/text()结果只会是“优惠价”和“元”中间的2999根本取不到。正确做法是先定位到price-wrap下的price-num节点再取它的text()最后用两段文本去拼一个完整价格。另一个常见问题是用contains匹配class时由于class属性可能包含多个类名直接写classprice往往匹配不到。我习惯写成//div[contains(class,price)]。但也要注意contains是模糊匹配如果页面里同时存在“price”和“price_old”两个节点很容易匹配错。所以尽量选择特征更明显的class名比如价格节点通常带有数字标志或者用更精确的父节点定位。解析完成后最好做一层结构化验证标题长度不能为0价格必须能转成float商品URL必须包含商品ID。不满足条件的记录直接跳过并写入采集错误日志不要硬解析。这能避免大量脏数据进入数据库。3.3 动态加载页面Selenium与接口直连方案电商平台和游戏交易平台很多页面是动态渲染的直接requests请求拿不到商品价格。我第一次抓藏宝阁的时候就栽了跟头直接请求URL返回的HTML里只有框架数据全是JavaScript异步加载的。动态页面有两种主流处理方案。第一种是Selenium模拟浏览器操作优点是无脑只要能打开浏览器就能抓缺点是慢、占用资源、容易被识别。第二种是直接找网站的数据接口这是我要推荐的做法。大部分动态页面的数据都来自一个固定的JSON接口你只需要用浏览器开发者工具抓一下网络请求找到真正返回数据的那个XHR请求然后直接用requests请求这个接口就行效率比Selenium高一截。具体操作是打开目标商品页面按F12进入开发者工具切到Network面板刷新页面筛选XHR或Fetch请求找到返回商品信息的那个接口。然后复制它的请求URL、请求头和参数。通常这个接口会带签名参数或者加密参数那就得分析参数的生成逻辑或者用Selenium补一遍。我在这套系统里对Steam的抓取直接用了它的公开接口对藏宝阁这类反爬严格的平台则采用了“接口加Cookie”的方案先用Selenium登录一次获取到有效的Cookie再保存到本地后续用requests携带Cookie去请求数据接口。这个方案比全程Selenium效率高也比纯requests可行。3.4 分布式爬虫的轻量落地方式如果你的比价系统要扩展到几百上千个商品单机爬虫可能不够用这时候可以考虑分布式爬虫。分布式爬虫的核心不是“多台机器”而是“任务分发和结果汇总”。我这里用的是轻量方案一台调度机负责分配任务多台爬虫机负责执行采集任务清单放在Redis队列里结果汇总写到MySQL。Scrapy本身支持分布式扩展配合Scrapy-Redis可以实现基于Redis的调度队列。大致思路是Request对象序列化后写入Redis多台机器的Scrapy实例从同一个Redis队列取任务抓取结果再统一入列。这样你只需要控制一台机器的调度逻辑其他机器执行相同的爬虫代码就行。但说实话毕设阶段如果不是数据量大到单机扛不住我不建议上分布式。分布式会引入网络、部署、任务分配、去重等一系列额外问题调试成本很高。我更推荐的方式是“单机多线程加队列”用ThreadPoolExecutor控制并发数比如8个线程同时抓取不同平台或不同商品页响应速度已经比单线程快很多倍。配合线程级别的随机延迟反爬压力也不会太集中。3.5 特定平台Steam和藏宝阁的爬取经验Steam这个平台相对友好它的商店页面会渲染服务端生成的HTML部分区域也能直接调用内部接口。抓Steam价格时要注意两个问题一个是货币单位和地区差异同样一个游戏在不同地区的价格显示不一样你要根据目标区间强制指定cc和l参数另一个是价格历史数据Steam有专门的historical low接口可以从第三方渠道获取但直接抓页面也能分析出历史低价区间。藏宝阁这类游戏交易平台比普通电商难抓得多。它的页面几乎全靠动态接口渲染而且关键数据接口有签名校验。我试过直接构造请求返回的是“参数错误”之类的提示后来发现它把签名算法藏在某个JS文件里而且每次请求还要带一个动态token。最终解决方案是先登录获取Cookie再模拟浏览器行为去请求页面把页面中内嵌的JSON数据解析出来而不是直接请求它的数据接口。这类经验最大的价值在于它不是教科书能教你的只能靠一遍遍调试踩出来。所以如果你也做类似平台建议第一步先花半小时打开开发者工具观察数据流而不是急着写代码。观察清楚再动手效率反而更高。4. 反爬对抗与合规边界4.1 Java Controller层如何防爬虫爬虫系统的另一面是Java后端如何设计防护逻辑。作为毕设这一块其实是加分项因为很多同学的所谓“系统”只有单向的采集功能没有考虑自己的后端接口也会被别人爬。Java后端防护的核心层是Spring MVC的Controller。我做了三层防护第一层是请求频率限制用一个拦截器对每个IP统计单位时间内的请求次数超过阈值就直接返回403第二层是请求头校验正常的浏览器请求一定会带User-Agent、Accept、Accept-Language等Header纯脚本请求往往只带一小部分第三层是行为校验比如登录态、验证码、请求路径的访问顺序。实现拦截器的方式很简单比如继承HandlerInterceptorAdapter在preHandle里做判断低于阈值的放行超过阈值的返回JSON错误提示。考虑到比价系统自己也要被定时任务调用我加了一个白名单接口内部请求带一个动态Token不经过频率限制这样既不影响程序自动刷新也能防住外部爬虫。这里要提醒一下单纯检查User-Agent很容易被绕过因为爬虫完全可以伪造单纯限制IP又容易误伤共享IP网段的正常用户。最实用的做法是组合校验先看请求频率再看请求头再看页面关键参数。三者都异常才判定为爬虫。4.2 前端安全加固防止查看源码与自动化操作前端反爬不等于彻底防止爬虫更多是提高爬取门槛。我在前端页面里做了几个简单的加固操作。第一个是禁用右键菜单防止访客直接右键查看页面源码。第二个是监听键盘F12和CtrlShiftI等快捷键弹窗提示当前页面不支持开发者工具。第三个是检测到DevTools打开时把页面关键内容替换成空白或警告文本。这三个手段都只是提高门槛并不能真正挡住专业爬虫但在演示和论文里能体现出你对安全防护的重视。还有一种做法是接口数据混淆。前端展示的价格数据不直接用明文JSON返回而是先做一层编码前端用JS解码后再渲染。这样即使别人拿到了接口返回内容也不能直接当作结构化数据使用。代价是前端代码复杂度增加调试时容易出错。我在这次项目里只在“价格走势”接口上做了简单混淆其他接口保持明文方便答辩时展示。4.3 爬虫侧的应对策略站在爬虫侧面对平台的反爬我总结了一套实用的应对步骤先试纯requests不行就加Cookie再不行就换Selenium还不行就分析加密接口参数。一般平台的反爬强度都逃不出这几个梯队。你还要学会看HTTP状态码。403代表权限不足可能是IP被封或User-Agent被拒绝429代表请求太频繁需要等待503代表服务暂时不可用多数是触发了限流。遇到这些状态码时不要盲目重试要根据错误类型调整策略。IP封禁是最常见的问题。轻量方案是准备一个代理IP池抓取时随机切换代理。更精细的方案是“慢爬”把并发数降到2以内每次请求间隔拉长到5秒以上虽然慢但很难被识别。实际项目中我把这两种方案配合使用日常用慢爬保底遇到批量抓取时切换代理池。4.4 合规建议别把自己写成反面案例写爬虫项目绕不开合规问题区别只在于你是否重视。我的做法是严格遵守三个底线第一只采集公开可见的页面数据不触碰需要登录才能访问且涉及个人隐私的数据第二遵守目标网站的robots.txt约定凡是明确禁止爬取的路径一律不碰第三控制请求频率不给目标服务器造成压力不搞恶意攻击式抓取。在实际项目中有一个容易被忽视的点商品价格、库存、销量等数据从法律属性上说是目标平台的经营数据如果用于商业牟利可能需要授权。但作为毕设和学术研究只要不超量采集、不对外提供商业化服务基本没有风险。你在论文里一定要写清楚“本系统仅用于教学演示采集数据仅作技术验证”这句话既是合规姿态也是保护自己。做技术的人手上有了爬虫能力很容易飘觉得什么都能抓。但真正成熟的工程技术首先要知道边界在哪里。这套系统的意义是验证“自动比价”这一技术链路能跑通而不是指导你去运营一个爬虫生意。5. 比价核心商品匹配与价格计算5.1 SKU归一化与标题清洗比价系统能不能准确比较取决于商品匹配的准确性。如果连不同平台的“同款商品”都认不出来那后面所有的比价结果都不可信。商品匹配的第一步是标题清洗。不同平台的标题格式差别很大例如京东的是“Apple iPhone 15 (A3092) 128GB 蓝色 支持移动联通电信5G 双卡双待手机”而淘宝的是“苹果15 128G 蓝色 iPhone15全新正品国行”。看上去完全不一样但本质上是同一个商品。我做的标题清洗流程是先统一大小写再替换繁体为简体再去除品牌后面的多余描述信息提取核心规格关键词。“128GB”这类存储规格和“蓝色”这类颜色属性我建立了一套规则库去提取。提取出“品牌型号存储颜色”四个维度后再生成一个匹配指纹。对于iPhone这类商品型号本身就能作为唯一标识比如“A3092”或者“iPhone 15”。对于没有明确型号的商品则用“品牌关键词规格”拼接后做相似度计算。我当时用了一个简单的加权方案品牌权重30%型号权重40%规格属性权重30%综合得分超过85分才判定为同一商品。这个阈值可以通过测试集反复调整。5.2 价格标准化单位、优惠、运费都得算进去价格标准化听起来简单实际坑很多。同一个商品平台A标价2999但包邮平台B标价2950但运费20元平台C标价3099但送一个100元优惠券。如果直接比较标价结果是A最低但真实到手价其实是B更低。我在价格计算模块里维护了一套标准化规则最终到手价等于商品标价减去优惠券金额再加上运费。优惠券的识别很难自动化我目前的方案是人工在配置页维护热门商品的优惠规则最终展示时同时显示“标价”和“估算到手价”两个字段。价格快照表里的价格我统一存储为“分”为单位避免浮点数比较导致的精度问题。例如原价2999元存储为299900。展示层再换算为元。这个细节虽然小但在做“最低价比较”和“历史价格差值”时能避免很多莫名其妙的bug。货币单位也要注意。如果系统抓取了Steam美区和国区的价格一个是美元一个是人民币必须先按汇率换算成同一币种。我在汇率模块里维护了一个简单定时任务每天从公开接口拉一次汇率并缓存到Redis避免每次计算都去请求外部服务。5.3 比价展示的核心查询逻辑前端展示比价结果的逻辑是用户输入商品名系统返回所有匹配商品然后按group_id聚合。每个商品组下面展示各个平台的报价、店铺、链接、价格更新时间并按最终到手价从低到高排序。为了性能考虑热门商品的比价结果会提前查好并缓存到Redis缓存时间设为30分钟。冷门商品则实时查询数据库。这样高频访问场景下的响应时间基本能控制在200毫秒以内。展示“历史最低价”时我用了SQL窗口函数在价格快照表中按platform_product_id分组取最低价格和对应时间。这个查询在数据量不大时性能没问题数据量大时就需要建立联合索引或者把历史价格统计结果离线计算好再写入Redis。毕设阶段用SQL窗口函数完全够用。6. 运行实录测试结果与排查记录6.1 采集效果实测在选定的50个测试商品中我统计了连续运行的15天数据。采集成功率在电商平台大概为92%在Steam平台为98%在藏宝阁这类反爬严格的平台只有68%。失败原因主要是三类接口参数变化导致解析失败、登录Cookie过期导致请求被重定向到登录页、请求频率触发临时封禁。采集成功率低于预期不是坏事。作为毕设这个数据恰恰能支撑你写“问题与改进”章节。你可以把失败原因细化成图标说明并对应给出解决方案比如Cookie自动续期的重试策略、代理池的动态切换机制、解析规则的异常兜底。答辩时老师看到你做了这些加分效果非常明显。6.2 请求频率临界值测试针对目标平台的反爬策略我做了几组对比测试。区间分别是平均请求间隔1秒、3秒、5秒和10秒。测试结果如下平均间隔请求成功率是否触发验证码说明1秒高频封禁是连续请求约50次后开始出现4033秒约85%成功率偶尔部分平台仍会触发限流5秒约95%成功率极少长期稳定运行推荐值10秒约98%成功率几乎没有采集速度明显下降这个结果说明反爬对抗不完全是技术对抗更多是节奏博弈。保持一个“看起来像人”的请求频率比任何高级技术都重要。6.3 典型报错与排查记录做爬虫系统最不缺的就是报错。我把15天里最高频的几种问题整理成一份速查表。报错信息原因解决方案ConnectionError: HTTPSConnectionPoolIP被封或网络超时切换代理池降低并发数lxml.etree.XPathEvalError: Invalid expressionXPath表达式写错在命令行单独调试表达式KeyError: priceJSON接口字段名变更增加字段缺失兼容逻辑MySQLdb.OperationalError: Lock wait timeout高并发写入导致锁冲突改为批量提交减少单条写入RedisConnectionErrorRedis未启动或内存不足检查服务状态配置内存上限其中数据库锁等待是我最开始没预料到的问题。多个线程同时往同一张表插入数据时如果事务提交太慢很容易出现锁等待超时。后来我把写入操作改成批量提交每100条做一次事务效率提升非常明显。6.4 性能优化记录运行一段时间后我发现MySQL里的价格快照表增长速度很快。50个商品、每天两次全量采集每个商品平均5个平台的快照一天产生500条记录一个月就是15000条。如果扩大到上千个商品单表数据量很快就会到百万级别。我的优化方案有两步一是给价格快照表按月份建分区表每月月初自动切换新分区二是建联合索引以“平台商品ID采集时间”为索引前缀让历史价格查询走索引。优化后历史价格查询从原来的700毫秒左右降到了100毫秒以内。另一个优化是前端列表页的懒加载。比价结果页面如果一次性渲染所有数据首次加载会很慢。我改成一次只加载10个商品组滚动到底部再加载下一批。这个改动单独看很小但对用户体验的提升非常直接。7. 写在最后我做这个项目时踩过的坑最后聊点实操层面的心得可能比前面那些技术点更值钱。第一爬虫代码一定要做日志。不是简单print一下就行要分文件记录请求日志、解析日志、错误日志。出问题时直接看错误日志定位比在控制台里翻找快得多。我当时就是因为没有日志花了一个下午排查一个偶发的编码问题加完日志之后五分钟就把原因找到了。第二尽量早一点把代码放到服务器上跑不要等到全部写完才部署。爬虫系统的很多问题只在长时间运行中才会暴露比如内存泄漏、定时任务堆积、数据库连接不释放。如果你只在本地跑两个小时根本发现不了这些问题。我大概写到爬虫模块的时候就丢到一台最低配的云服务器上做7×24小时测试后面所有优化都基于真实的运行数据心态完全不一样。第三Java后端的Controller层一定要做参数校验不要信任前端传过来的任何值。比价系统有搜索接口如果搜索关键字直接拼SQL很容易被注入。我用MyBatis-Plus的QueryWrapper做条件查询从源头杜绝了SQL拼接问题。答辩老师问安全设计时你也可以讲得很有底气。第四这个项目后续可以扩展的空间很大。比如把爬虫任务管理界面做成可视化的在页面上新增平台、修改解析规则、查看采集进度再比如增加价格变动提醒当目标商品价格跌破用户设定的阈值时通过微信或邮件推送通知。这两条扩展方向都是真实用户会使用的功能放在论文的“展望”章节里也不违和。如果你正在做类似的毕设项目把这套思路吃透之后别急着抄代码。先按你的题目要求重新梳理技术选型和数据流再动手写。比价系统真正考验人的地方从来不是某一个技术单独有多深而是怎么把这些技术顺畅地串起来稳定地跑下去。项目做完之后我的体会是选定一个真实能用的场景比堆砌十个看起来高级但不落地的功能要重要得多。