Python大数据实战(一):Scrapy爬取二手车数据+GBDT价格预测完整项目

📅 2026/6/23 4:17:54
Python大数据实战(一):Scrapy爬取二手车数据+GBDT价格预测完整项目
Python大数据实战一Scrapy爬取二手车数据GBDT价格预测完整项目文章目录Python大数据实战一Scrapy爬取二手车数据GBDT价格预测完整项目前言一、项目概述1.1 项目架构总览1.2 学习目标1.3 数据来源1.4 项目目标二、Scrapy 爬虫数据采集2.1 创建爬虫项目2.2 定义数据模型Item2.3 编写爬虫逻辑Spider2.4 启动爬虫三、Pandas 数据分析与特征工程3.1 标签数据预处理3.2 标签 One-Hot 编码3.3 价格分析哪些品牌最贵3.4 销量分析哪些品牌最畅销3.5 价格分布分析以大众为例3.6 品牌 One-Hot 编码四、GBDT 建模与评估4.0 GBDT 算法原理一图看懂4.1 数据准备4.2 模型训练4.3 模型评估4.4 特征重要性分析五、常见错误与解决方案错误 1Scrapy 爬取被反爬拦截错误 2中文显示乱码错误 3One-Hot 编码后特征爆炸六、总结前言“这辆二手车值多少钱”——这是每个买车人和卖车人都想知道答案的问题。对于数据从业者来说二手车价格预测是一个经典的爬虫 数据分析 机器学习综合实战项目。它不像鸢尾花分类那样简单也不像推荐系统那样复杂刚好卡在能学到东西又不至于劝退的甜点区。本文带你从零开始用 Scrapy 爬取人人车真实数据用 Pandas 做数据清洗和特征工程最后用 GBDT梯度提升决策树建立价格预测模型。全程代码可运行适合有 Python 基础、想入门大数据项目的同学。一、项目概述1.1 项目架构总览人人车网站 → Scrapy爬虫 → 原始数据CSV → Pandas清洗 → 标签拆分OneHot → 品牌编码标签编码 → Matplotlib可视化分析 → GBDT回归模型 → 价格预测评估1.2 学习目标阶段技能工具/库数据采集Scrapy 爬虫框架Scrapy数据处理数据清洗、标签编码、One-HotPandas、NumPy数据分析价格分布、品牌销量、概率密度Matplotlib、Seaborn、SciPy机器学习回归预测、模型评估Scikit-learnGBDT1.3 数据来源爬取人人车网站https://www.renrenche.com/的二手车信息包含品牌brand大众、丰田、本田、宝马……价格price二手车挂牌价格标签tag如准新车“急售”0过户等1.4 项目目标通过已有数据建立回归模型对二手车价格进行预测最终用 MSE、MAE、RMSE、R² 四个指标评估模型效果。二、Scrapy 爬虫数据采集2.1 创建爬虫项目# 创建 Scrapy 项目scrapy startproject ershouche# 进入项目目录创建爬虫文件cdershouche scrapy genspider car www.renrenche.com执行后项目结构如下ershouche/ ├── ershouche/ │ ├── spiders/ │ │ └── car.py # 爬虫逻辑 │ ├── items.py # 数据模型 │ ├── pipelines.py # 数据处理管道 │ └── settings.py # 项目配置 └── scrapy.cfg2.2 定义数据模型Item# items.pyimportscrapyclassErshoucheItem(scrapy.Item):二手车数据模型brandscrapy.Field()# 品牌如大众pricescrapy.Field()# 价格如8.5万tagscrapy.Field()# 标签如准新车_急售2.3 编写爬虫逻辑Spider# spiders/car.pyimportscrapyfromershouche.itemsimportErshoucheItemclassCarSpider(scrapy.Spider):namecarallowed_domains[renrenche.com]start_urls[https://www.renrenche.com/xa/ershouche/]defparse(self,response):解析当前页的二手车列表# 获取当前页所有二手车列表项car_listresponse.xpath(//ul[classrow-fluid list-row js-car-list]/li)forliincar_list:itemErshoucheItem()# 提取品牌brandli.xpath(.//a[classbrand]/text()).extract_first()item[brand]brand.strip()ifbrandelse# 提取价格priceli.xpath(.//div[classprice]/text()).extract_first()item[price]price.strip()ifpriceelse# 提取标签多个标签用下划线连接tag_listli.xpath(.//div[classtag]/span/text()).extract()item[tag]_.join([t.strip()fortintag_list])yielditem# 翻页处理next_urlresponse.xpath(//ul[classpagination js-pagination]/li[last()]/a/href).extract_first()# 如果不是最后一页继续爬取ifnext_urlandnext_url!javascript:void(0);:next_urlresponse.urljoin(next_url)yieldscrapy.Request(next_url,callbackself.parse)2.4 启动爬虫# 在项目根目录执行scrapy crawl car-ocars.csv# 输出为 CSV 文件⚠️ 注意事项爬取前请确认目标网站的 robots.txt 协议合理设置下载延迟DOWNLOAD_DELAY 2避免对目标服务器造成压力。三、Pandas 数据分析与特征工程3.1 标签数据预处理爬取到的tag字段包含多个标签如准新车_急售_0过户需要拆分为独立的特征列。importpandasaspdimportnumpyasnp# 读取数据datasetpd.read_csv(cars.csv)# 过滤掉标签为空的数据数据量很少不影响整体datasetdataset[dataset[tag].notna()]# 收集所有唯一标签tag_list[]dataset[tag].apply(lambdax:tag_list.extend(x.split(_)))tag_listlist(set(tag_list))# 去重print(f共有{len(tag_list)}种标签:{tag_list[:10]})3.2 标签 One-Hot 编码将标签转换为 0/1 特征矩阵# 创建标签特征 DataFrame初始值全为 0tag_dfpd.DataFrame(columnstag_list)dfpd.concat([dataset,tag_df],sortFalse)df[tag_list]df[tag_list].fillna(0)defset_tag_status(series):将当前行的标签设置为 1tagsseries[tag].split(_)fortintags:iftintag_list:series[t]1returnseries# 对每一行应用标签编码df[tag_list]df[[tag,*tag_list]].apply(lambdax:set_tag_status(x),axis1).drop(tag,axis1)dfdf.drop(tag,axis1)# 删除原始 tag 列print(f特征矩阵形状:{df.shape})3.3 价格分析哪些品牌最贵importmatplotlib.pyplotaspltimportseabornassns# 设置中文字体避免乱码plt.rcParams[font.sans-serif][SimHei]plt.rcParams[axes.unicode_minus]False# 平均价格最高的前 10 个品牌num_topdf.groupby(brand)[price].mean().sort_values(ascendingFalse)[:10]# 绘制条形图figplt.figure(figsize(15,10))sns.barplot(xnum_top.index,ynum_top.values)plt.title(二手车平均价格 Top 10 品牌)plt.xticks(rotation90)plt.ylabel(平均价格万元)plt.tight_layout()plt.show()3.4 销量分析哪些品牌最畅销# 销量最多的前 10 个品牌amount_topdf[brand].value_counts().sort_values(ascendingFalse)[:10]# 条形图figplt.figure(figsize(12,8))sns.barplot(xamount_top.index,yamount_top.values)plt.title(二手车销量 Top 10 品牌)plt.xticks(rotation90)plt.ylabel(数量辆)plt.tight_layout()plt.show()# 饼图各品牌占比figplt.figure(figsize(10,10))plt.pie(amount_top.values,labelsamount_top.index,autopct%1.2f%%)plt.title(各大品牌车系数量占有比重前 10 位)plt.show()3.5 价格分布分析以大众为例fromscipy.statsimportnorm# 筛选大众品牌数据df_dazhongdf[df[brand]大众]dazhong_meandf_dazhong[price].mean()# 均值dazhong_stddf_dazhong[price].std()# 标准差# 绘制直方图 概率密度曲线num_bins20n,bins,patchesplt.hist(df_dazhong[price],num_bins,facecolorgreen,densityTrue,alpha0.5)# 叠加正态分布概率密度曲线ynorm.pdf(bins,dazhong_mean,dazhong_std)plt.plot(bins,y,r--,linewidth2,label正态分布拟合)plt.xlabel(价格万元)plt.ylabel(概率密度)plt.title(f大众二手车价格分布均值{dazhong_mean:.2f}万标准差{dazhong_std:.2f}万)plt.legend()plt.show()3.6 品牌 One-Hot 编码# 对品牌进行 One-Hot 编码one_hot_dfpd.get_dummies(df[brand])dfdf.drop(brand,axis1)# 删除原始 brand 列# 合并 One-Hot 列dfpd.merge(df,one_hot_df,left_indexTrue,right_indexTrue)print(fOne-Hot 编码后特征数:{df.shape[1]})四、GBDT 建模与评估4.0 GBDT 算法原理一图看懂GBDTGradient Boosting Decision Tree的核心思想是串行训练多棵决策树每棵树都在修正前面所有树的残差第1棵树: 预测 → 残差1 真实值 - 预测值1 第2棵树: 拟合残差1 → 残差2 残差1 - 预测值2 第3棵树: 拟合残差2 → 残差3 残差2 - 预测值3 ... 最终预测 树1预测 树2预测 树3预测 ...对比维度随机森林GBDT训练方式并行Bagging串行Boosting每棵树关系独立依赖前序树偏差较低更低方差较低较高需控制学习率适用场景通用追求高精度4.1 数据准备fromsklearn.model_selectionimporttrain_test_splitfromsklearn.ensembleimportGradientBoostingRegressorfromsklearn.metricsimportmean_squared_error,mean_absolute_error,r2_score# 分离特征和标签Xdf[df.columns.difference([price])].values# 样本特征Ydf[price].values# 目标变量价格# 划分训练集和测试集7:3X_train,X_test,Y_train,Y_testtrain_test_split(X,Y,test_size0.3,random_state666)print(f训练集:{X_train.shape[0]}条, 测试集:{X_test.shape[0]}条)4.2 模型训练# 创建 GBDT 回归模型gbdtGradientBoostingRegressor(n_estimators70,# 70 棵决策树learning_rate0.1,# 学习率max_depth5,# 树的最大深度random_state666)# 训练模型gbdt.fit(X_train,Y_train)# 预测predgbdt.predict(X_test)4.3 模型评估# 四大评估指标msemean_squared_error(Y_test,pred)maemean_absolute_error(Y_test,pred)rmsenp.sqrt(mse)r2r2_score(Y_test,pred)print(*40)print(GBDT 二手车价格预测模型评估)print(*40)print(fMSE (均方误差):{mse:.2f})print(fMAE (平均绝对误差):{mae:.2f}万元)print(fRMSE (均方根误差):{rmse:.2f}万元)print(fR² (决定系数):{r2:.4f})print(*40)# 解读ifr20.8:print(✅ 模型效果优秀可以用于实际预测)elifr20.6:print(⚠️ 模型效果一般建议调参优化)else:print(❌ 模型效果较差需要更多特征或数据)4.4 特征重要性分析# 查看哪些特征对价格预测影响最大feature_importancepd.DataFrame({feature:df.columns.difference([price]),importance:gbdt.feature_importances_}).sort_values(importance,ascendingFalse)print(Top 10 重要特征)print(feature_importance.head(10))五、常见错误与解决方案错误 1Scrapy 爬取被反爬拦截# ❌ 错误直接高频请求被网站封 IPscrapy crawl car# 无任何反爬措施# ✅ 正确配置下载延迟和 User-Agent# settings.pyDOWNLOAD_DELAY2# 每次请求间隔 2 秒RANDOMIZE_DOWNLOAD_DELAYTrue# 随机延迟DEFAULT_REQUEST_HEADERS{User-Agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36,}错误 2中文显示乱码# ❌ 错误matplotlib 默认不支持中文plt.title(二手车价格分布)# 显示为方框# ✅ 正确设置中文字体plt.rcParams[font.sans-serif][SimHei,DejaVu Sans]plt.rcParams[axes.unicode_minus]False# 解决负号显示问题错误 3One-Hot 编码后特征爆炸# ❌ 问题品牌有 100 种One-Hot 后特征数暴增# 解决方案只保留出现频率最高的 Top N 品牌top_brandsdf[brand].value_counts().head(20).index.tolist()df[brand]df[brand].apply(lambdax:xifxintop_brandselse其他)# 然后再做 One-Hot特征数从 100 降到 21六、总结本文完成了一个完整的爬虫 → 数据分析 → 机器学习项目实战Scrapy 爬虫从人人车采集真实二手车数据含翻页处理Pandas 数据处理标签拆分、One-Hot 编码、品牌编码数据可视化价格 Top 10、销量占比饼图、价格概率密度分布GBDT 建模70 棵决策树的梯度提升回归四大指标评估核心收获一个完整的数据项目不是调个包就完事了数据采集和特征工程往往占 80% 的工作量。爬虫写得好不好、特征做得细不细直接决定模型效果的上限。下一篇我们将实战工资分类预测项目用逻辑回归和随机森林预测你的薪资属于哪个区间——敬请期待参考链接Scrapy 官方文档Scikit-learn GBDT 文档Pandas 数据处理指南Matplotlib 中文显示方案