news 2026/9/11 23:22:09

Python二手车爬虫与可视化毕业设计实战指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python二手车爬虫与可视化毕业设计实战指南

简介:本资源是一套完整落地的本科毕业设计项目,面向计算机、数据科学及相关专业学生,聚焦二手车市场数据采集与商业分析实战场景。项目基于Python实现全流程:从主流平台动态爬取车辆信息,到SQLite本地数据库存储,再到Pandas清洗、Matplotlib/Seaborn可视化及基础统计分析,覆盖数据获取、处理、呈现与解读全链路,亦适合作为课程设计或期末大作业直接复用。压缩包共2000个文件,主体为1745个Python源码(含爬虫主程序、数据清洗模块、可视化脚本及数据库操作逻辑),辅以112个头文件、88个说明文本、13个JSON配置及11个PDF文档(含设计说明书、答辩PPT与技术报告),整体54.99MB,结构清晰、模块解耦度高。目前已有656人学习下载,提供经导师指导、答辩获97分的高分方案,含可运行源码、完整数据库文件与详细文档,无需二次调试即可部署验证。

1. 为什么用 Python 爬二手车数据做毕业设计,反而比“高大上”选题更容易拿高分?

很多同学一想到毕业设计就默认要搞 AI、深度学习或分布式系统,结果卡在环境配置、模型调参、论文复现上,最后答辩时连数据来源都说不清。而这个基于 Python 的二手车爬虫与可视化项目,恰恰反其道而行之:它不追求算法复杂度,而是把「真实业务链路」跑通——从目标网站(如瓜子、人人车、懂车帝等主流平台)稳定抓取结构化车辆信息,清洗掉价格乱码、里程异常、年份错位等典型脏数据,再用 Matplotlib + Seaborn + Plotly 构建可交互的多维分析视图,最终导出带统计结论的 PDF 报告。97 分答辩成绩背后,是导师最看重的三点:数据可溯源、流程可复现、结论有业务支撑。它适合两类人:一是计算机/信管/大数据专业本科生,需要一个完整闭环、无版权风险、本地即可运行的课程设计;二是想快速建立数据工程实感的转行者——你不需要自己搭服务器、不用申请 API、不碰反爬对抗黑盒,所有请求头、重试逻辑、代理池占位符都已预置,解压即 run,改两行 URL 就能迁移到其他垂直品类。

2. 爬虫模块解析:requests + BeautifulSoup 为何仍是毕业设计首选组合

2.1 为什么不用 Scrapy 或 Selenium?——选型背后的教学合理性

Scrapy 功能强大但学习曲线陡峭,需理解中间件、管道、信号机制,对仅需抓取静态列表页+详情页的二手车场景属于过度设计;Selenium 虽能渲染 JS,但启动浏览器开销大、易被检测、CI/CD 部署困难。本项目采用 requests + BeautifulSoup 组合,核心逻辑集中在crawler/spider.py中,共 217 行代码,覆盖了从首页翻页到详情页解析的全路径。这种轻量架构让导师能一眼看清数据流向:get_list_page()parse_car_list()extract_detail_url()get_detail_page()parse_car_detail()。更重要的是,所有请求均模拟真实用户行为:设置User-Agent为 Chrome 最新版本,Accept-Language指定中文,Referer回填上一页 URL,且每请求间隔random.uniform(1.2, 2.8)秒——这既规避了基础频率限制,又符合教学项目“可控、可解释”的要求。

2.2 关键字段解析逻辑与容错处理

二手车数据中,价格、里程、上牌时间三类字段极易出错。例如某平台将“¥12.5万”存为<span class="price">12.5</span><i>万</i>,而另一平台用“125000元”纯数字格式。项目在parser/car_parser.py中定义了统一清洗函数:

def clean_price(text: str) -> Optional[float]: """标准化价格字段:支持'12.5万'、'125000元'、'面议'等多种格式""" if not text or "面议" in text: return None # 提取所有数字和小数点 digits = re.findall(r'[\d.]+', text) if not digits: return None num_str = ''.join(digits) try: if '万' in text: return float(num_str) * 10000 else: return float(num_str) except ValueError: return None

提示:该函数被parse_car_detail()调用时,会对price_raw字段执行clean_price(price_raw)。若返回None,则数据库对应字段写入NULL,而非报错中断。这种“失败静默+标记缺失”的策略,比强行转换更符合真实数据工程规范。

2.3 数据库持久化:SQLite 作为毕业设计存储方案的不可替代性

项目使用sqlite3模块直连data/cars.db,建表语句在db/init_db.py中定义:

CREATE TABLE IF NOT EXISTS cars ( id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT NOT NULL, price REAL, -- 清洗后价格(单位:元) mileage REAL, -- 里程(单位:万公里) reg_date DATE, -- 上牌日期(格式:YYYY-MM-DD) brand TEXT, model TEXT, transmission TEXT, -- 变速箱类型:自动/手动/手自一体 fuel_type TEXT, -- 燃料类型:汽油/柴油/新能源 source_url TEXT UNIQUE, crawl_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP );

注意:source_url UNIQUE约束确保同一车辆不会重复入库,避免翻页时详情页 URL 重复采集。crawl_time自动记录抓取时间,后续做时间维度分析(如“近3个月价格走势”)时直接可用。

下表列出各字段在爬虫中的映射关系及常见异常值处理方式:

数据库字段爬虫来源 HTML 元素典型异常值示例处理方式
price<div class="price">¥15.8万</div>“面议”、“暂无报价”、“电联”clean_price()返回None
mileage<li>行驶里程:<span>6.2万公里</span></li>“未填写”、“<1万公里”、“过户2次”正则提取数字,非数字返回None
reg_date<li>上牌时间:<span>2019年05月</span></li>“2019.5”、“19年5月”、“约2019年”标准化为2019-05-01,年份缺失则补当年

3. 可视化分析模块:从 Matplotlib 基础图到 Plotly 交互式仪表盘

3.1 价格-车龄散点图:揭示二手车市场核心规律

analysis/price_age_analysis.py中的核心绘图代码如下:

import matplotlib.pyplot as plt import seaborn as sns import pandas as pd # 读取清洗后数据 df = pd.read_sql_query("SELECT price, reg_date FROM cars WHERE price IS NOT NULL AND reg_date IS NOT NULL", conn) df['age'] = pd.to_datetime('today').year - pd.to_datetime(df['reg_date']).dt.year # 绘制散点图 + 回归线 plt.figure(figsize=(10, 6)) sns.scatterplot(data=df, x='age', y='price', alpha=0.6, s=30, color='#1f77b4') sns.regplot(data=df, x='age', y='price', scatter=False, color='red', line_kws={'linestyle': '--'}) plt.title('二手车价格 vs 使用年限(散点图+趋势线)', fontsize=14) plt.xlabel('车龄(年)', fontsize=12) plt.ylabel('售价(元)', fontsize=12) plt.grid(True, alpha=0.3) plt.savefig('output/price_vs_age.png', dpi=300, bbox_inches='tight') plt.show()

这段代码输出的图像会清晰显示:车龄每增加 1 年,平均售价下降约 1.8 万元(回归线斜率),且 3 年内车型价格衰减最快——这正是二手车评估师口中的“黄金折旧期”。图中每个点代表一辆真实车辆,透明度alpha=0.6避免重叠点遮挡,尺寸s=30保证打印清晰。关键参数说明:bbox_inches='tight'自动裁剪白边,dpi=300满足毕业论文印刷要求。

3.2 品牌-价格箱线图:识别高溢价与高性价比阵营

analysis/brand_price_boxplot.py使用 Seaborn 绘制品牌价格分布:

# 筛选销量前10品牌 top_brands = df['brand'].value_counts().head(10).index df_top = df[df['brand'].isin(top_brands)] plt.figure(figsize=(12, 7)) ax = sns.boxplot(data=df_top, x='brand', y='price', order=top_brands) ax.set_xticklabels(ax.get_xticklabels(), rotation=30) plt.title('销量TOP10品牌售价分布(箱线图)', fontsize=14) plt.ylabel('售价(元)', fontsize=12) plt.xlabel('汽车品牌', fontsize=12) plt.tight_layout() plt.savefig('output/brand_price_boxplot.png', dpi=300, bbox_inches='tight')

该图能直观回答答辩常问问题:“为什么宝马均价比丰田高?”——箱线图不仅显示中位数,更暴露离散程度:宝马的上四分位数(Q3)远高于丰田的上限(whisker),说明其高端车型拉高了整体区间;而五菱宏光的箱体极窄且中位数低,印证其“工具车”定位。order=top_brands强制按销量排序,避免品牌名随机排列影响解读。

3.3 Plotly 交互式仪表盘:用 30 行代码实现毕业答辩加分项

dashboard/app.py基于 Flask + Plotly 构建轻量 Web 仪表盘,核心逻辑仅需初始化一个Dash实例并注册回调:

from dash import Dash, dcc, html, Input, Output, callback import plotly.express as px app = Dash(__name__) server = app.server # 供 Gunicorn 部署用 # 读取数据 df = pd.read_sql_query("SELECT * FROM cars", conn) app.layout = html.Div([ html.H1("二手车市场分析仪表盘", style={'textAlign': 'center'}), dcc.Dropdown( id='brand-filter', options=[{'label': b, 'value': b} for b in df['brand'].unique()], value='丰田', multi=True ), dcc.Graph(id='price-hist') ]) @callback( Output('price-hist', 'figure'), Input('brand-filter', 'value') ) def update_histogram(selected_brands): filtered_df = df[df['brand'].isin([selected_brands] if isinstance(selected_brands, str) else selected_brands)] fig = px.histogram(filtered_df, x='price', nbins=30, title=f'{selected_brands} 售价分布直方图') fig.update_xaxes(title_text='售价(元)') fig.update_yaxes(title_text='车辆数量') return fig

运行python dashboard/app.py后访问http://127.0.0.1:8050,即可交互筛选品牌、实时刷新直方图。答辩时演示此功能,比静态 PNG 图片更具说服力——导师可亲手操作验证结论可靠性。nbins=30控制分组粒度,过少则丢失细节,过多则噪声显著,经测试 30 是平衡点。

4. 数据库文件与文档说明:如何用现成 DB 文件跳过爬虫环节直接做分析

4.1cars.db文件结构验证与快速导入

项目附带的data/cars.db是 SQLite3 格式,无需安装数据库服务。验证其完整性只需两步:

# 1. 检查文件是否可读 file data/cars.db # 输出应含 "SQLite 3.x database" # 2. 查看表结构与数据量 sqlite3 data/cars.db "PRAGMA table_info(cars);" sqlite3 data/cars.db "SELECT COUNT(*) FROM cars;"

若输出COUNT(*)1247(示例值),说明数据库已预置千级样本,足够支撑毕业设计全部分析。此时可跳过crawler/run_spider.py,直接在analysis/目录下运行任意.py脚本——所有pd.read_sql_query()默认连接data/cars.db

4.2 文档说明文件README.md的实战价值拆解

docs/README.md不是空泛介绍,而是精准标注了每个模块的修改点:

  • 爬虫 URL 配置crawler/config.pyBASE_URL = "https://www.guazi.com/bj/buy/"—— 修改城市编码bjsh(上海)、gz(广州)即可切换区域;
  • 字段映射表:明确写出HTML selector → 数据库字段对应关系,如".js-price" → price,避免学生在BeautifulSoup中盲目试错;
  • 答辩 PPT 结构建议:第3页必放“数据采集流程图”,第5页必须包含“价格-车龄回归分析结果”,第7页展示“交互式仪表盘截图”——直击评审关注点。

提示:文档中requirements.txt已锁定pandas==1.5.3matplotlib==3.7.1等版本,避免因 pip 升级导致Seaborn绘图报错。若遇ModuleNotFoundError,优先执行pip install -r requirements.txt而非pip install --upgrade

4.3 一键生成分析报告:PDF 导出的 LaTeX 与 WeasyPrint 双路径

report/generate_report.py提供两种导出方案:

  • LaTeX 路径(推荐用于正式提交):调用pandoc将 Markdown 报告转 PDF,公式渲染精准,页眉页脚规范;
  • WeasyPrint 路径(零依赖):纯 Python 库,pip install weasyprint后直接运行,生成带 CSS 样式的 PDF。

关键代码段:

from weasyprint import HTML import markdown # 将 analysis_results.md 渲染为 HTML with open('analysis/analysis_results.md', 'r', encoding='utf-8') as f: md_content = f.read() html_content = markdown.markdown(md_content, extensions=['tables', 'fenced_code']) # 注入 CSS 样式 styled_html = f""" <html> <head><style> body {{ font-family: "Microsoft YaHei"; }} table {{ border-collapse: collapse; width: 100%; }} th, td {{ border: 1px solid #ccc; padding: 8px; text-align: left; }} </style></head> <body>{html_content}</body> </html> """ HTML(string=styled_html).write_pdf('output/analysis_report.pdf')

该脚本生成的 PDF 包含:标题页、数据概览表(总车数、均价、最高/最低价)、3 张核心图表(散点图、箱线图、直方图)、结论段落(如“新能源车占比仅 7.2%,但平均车龄比燃油车短 2.1 年”)。所有文字使用微软雅黑,表格带边框,完全满足本科毕业论文格式要求。

5. 运行排错与参数调优:当爬虫卡在第 3 页、图表中文乱码、数据库写入失败时怎么办

5.1 爬虫中断恢复:基于 SQLite 的断点续爬机制

项目在crawler/spider.py中实现了状态检查:

def get_last_crawled_page(): """查询数据库中最新抓取的页面序号""" cursor.execute("SELECT MAX(page_num) FROM crawl_log WHERE status='success'") result = cursor.fetchone()[0] return result if result else 0 def log_crawl_status(page_num: int, status: str): """记录爬取状态,支持断点续爬""" cursor.execute("INSERT INTO crawl_log (page_num, status, timestamp) VALUES (?, ?, ?)", (page_num, status, datetime.now())) conn.commit()

若爬虫在第 3 页崩溃,下次运行python crawler/run_spider.py会自动从第 4 页开始,无需手动删库重来。crawl_log表结构已在db/init_db.py中定义,包含page_numstatus字段,status值为'success''failed',便于后期统计成功率。

5.2 中文乱码终极解决方案:Matplotlib 字体配置三步法

所有.py脚本开头强制设置字体:

import matplotlib matplotlib.rcParams['font.sans-serif'] = ['SimHei', 'Arial Unicode MS', 'DejaVu Sans'] matplotlib.rcParams['axes.unicode_minus'] = False # 解决负号 '-' 显示为方块的问题

若仍乱码,执行以下命令定位系统字体路径:

# Linux/macOS fc-list :lang=zh | grep -i simhei # Windows(PowerShell) Get-ChildItem -Path "$env:windir\Fonts" -Filter "*simhei*" | Select-Object FullName

将返回路径(如C:\Windows\Fonts\simhei.ttf)复制到matplotlib.font_manager.findSystemFonts()可识别位置,或直接在代码中指定:

plt.rcParams['font.family'] = 'sans-serif' plt.rcParams['font.sans-serif'] = ['SimHei'] plt.rcParams['font.size'] = 12

5.3 数据库写入失败排查表

现象可能原因快速验证命令解决方案
sqlite3.IntegrityError: UNIQUE constraint failed: cars.source_url同一 URL 被重复提交sqlite3 data/cars.db "SELECT source_url FROM cars WHERE source_url LIKE '%guazi%';"检查spider.pyextract_detail_url()是否去重逻辑失效
OperationalError: database is locked多进程同时写库lsof -i :8050(查占用端口)改用单进程模式,或在db/init_db.py中添加timeout=20参数
ValueError: could not convert string to floatprice字段含非数字字符sqlite3 data/cars.db "SELECT price FROM cars WHERE price NOT GLOB '[0-9.]*';"clean_price()中增加re.sub(r'[^\d.]', '', text)预清洗

注意:所有 SQL 查询均使用?占位符防止注入,如cursor.execute("SELECT * FROM cars WHERE brand=?", (brand_name,)),这是 SQLite 官方推荐的安全写法,比字符串拼接更可靠。

运行python analysis/price_age_analysis.py时若报KeyError: 'reg_date',说明数据库中存在reg_date为空的记录。此时应先执行清洗:

DELETE FROM cars WHERE reg_date IS NULL OR reg_date = '';

再重新运行分析脚本——这比在 Python 中加try-except更高效,因为 SQLite 的DELETE操作在千级数据量下耗时不足 0.01 秒。

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 23:19:57

鸿蒙系统乡村文化社区APP源码设计:从工程结构到离线缓存实践

简介&#xff1a;基于鸿蒙系统的乡村文化振兴网络社区应用开发源码&#xff0c;面向移动端开发者与高校学生&#xff0c;提供一套完整可参考的社区类项目实现。资源包共含283个文件&#xff0c;涵盖95个XML界面配置、58个Java源文件、42个PNG与20个JPG图片素材、18个JSON数据文…

作者头像 李华
网站建设 2026/9/11 23:19:39

STM32F103驱动ST7789 IPS屏:SPI时序、DMA传输与局部刷新实战

简介&#xff1a;一份面向嵌入式开发者与电子爱好者的ST7789液晶驱动源码包&#xff0c;基于STM32F103微控制器&#xff0c;适用于小尺寸彩色TFT屏幕的快速接入与显示控制&#xff0c;可移植到各类物联网终端、手持设备或学习项目中。压缩包共4个文件&#xff0c;以ST7789.c与S…

作者头像 李华
网站建设 2026/9/11 23:19:35

GhostTrack:快速查 IP 归属地、号码归属地与用户名

GhostTrack&#xff1a;快速查 IP 归属地、号码归属地与用户名 【免费下载链接】GhostTrack Useful tool to track location or mobile number 项目地址: https://gitcode.com/GitHub_Trending/gh/GhostTrack 拿到一条陌生 IP&#xff0c;想立刻确认它落在哪座城市、挂在…

作者头像 李华
网站建设 2026/9/11 23:18:57

Unity DOTS+NetCode实时对战框架实战指南

简介&#xff1a;这是一套基于Unity 3D开发的策略卡牌对战类游戏完整项目源码&#xff0c;面向Unity初学者与中级游戏开发者&#xff0c;聚焦MOBA卡牌构筑玩法的学习与复现。项目以《皇室战争》为设计蓝本&#xff0c;实现了英雄收集、卡牌编组&#xff08;最多8张&#xff09;…

作者头像 李华
网站建设 2026/9/11 23:18:55

词法分析+LL(1)+LR(1):编译原理实验链完整解析

简介&#xff1a;这是编译原理课程设计实验的完整源码包&#xff0c;提供词法分析器、LL(1)语法分析器、LR(1)语法分析器三部分实现&#xff0c;适合正在学习编译原理或准备课程设计的高校学生参考。实验最初为词法分析器热身练习&#xff0c;支持匹配关键字、标记符、运算符、…

作者头像 李华