尧图精选

链家租房数据分析实战:从爬虫到可视化完整项目解析

🕒 发布时间:2026/9/3 4:53:44 📁 来源:尧图网络
简介本资源是一份面向高校Python课程学习者与毕业设计学生的高完成度实战项目聚焦上海链家出租房数据的采集、分析与可视化全流程。资源包含可直接运行的爬虫脚本、Jupyter Notebook数据分析代码、多维度可视化图表如区域单价热力图、户型/装修类型分布图、总价-面积聚类散点图等及配套文档报告覆盖数据获取、清洗、探索性分析、统计建模与成果展示完整链条。压缩包共20个文件含1个核心爬虫py脚本、1个分析用ipynb文件、1个原始csv数据集、1份Word期末报告、1份答辩PPT、1个README说明及13张高质量分析图表png整体大小20.91MB结构清晰、模块分明便于理解与复用。已有102人学习下载源码经本地实测可运行评审分达95分以上适合作为期末大作业或毕业设计参考方案尤其适合需快速上手真实数据分析场景的初学者与进阶实践者。1. 项目概述从数据视角洞察租房市场最近帮一个学弟处理他的课程大作业题目是“链家出租房上海为例数据分析”。这项目听起来挺简单不就是爬点数据、画几个图嘛但真做下来发现里面门道不少完全是一个从数据采集、清洗、分析到可视化的完整数据科学小项目实战。对于正在学习Python数据分析、或者想找个有深度的练手项目的朋友来说这个选题非常合适。它不像爬取静态网页那么简单链家的反爬机制需要一些技巧去应对数据清洗时面对“押一付三”、“近地铁”这类非结构化文本如何提取有效信息也是个挑战最后的分析维度从简单的价格分布到复杂的商圈热度与通勤成本关联能玩出很多花样。如果你正愁找不到一个能串联起requests、pandas、matplotlib/seaborn甚至简单机器学习的实战案例这个项目会给你带来不少启发。接下来我就以“上海链家租房数据”为例拆解一遍从零到一的完整流程并附上核心源码思路和避坑指南。2. 项目核心思路与技术选型2.1 为什么选择链家与上海作为样本链家作为国内头部房产信息平台其数据具有代表性、实时性和结构化相对较好的特点。选择上海是因为其租房市场庞大、房源类型丰富从老破小到豪华公寓、租金梯度明显且区域特征如浦东金融区、静安寺商圈、张江科学城差异巨大这使得数据分析结果会非常有趣且有说服力。一个成功的课程项目或数据分析报告样本的典型性和分析的深度是关键。在技术路径上我们采用经典的“数据流水线”模式采集 - 存储 - 清洗/处理 - 分析/建模 - 可视化。这个流程是数据科学项目的通用框架掌握它比单纯学会几个库函数更重要。2.2 技术栈拆解与工具选型理由数据采集层Requests BeautifulSoup / PyQueryRequests负责发送HTTP请求获取网页HTML内容。这是爬虫的基石必须熟练掌握其会话Session管理、请求头Headers设置、代理和超时处理。BeautifulSoup 或 PyQuery用于解析HTML提取目标数据。BeautifulSoup语法更接近自然语言PyQuery则类似jQuery对于有前端经验的朋友更友好。本项目页面结构规整两者皆可。选型考量为什么不直接用Scrapy对于这种中等规模几千条数据、页面结构统一的定向爬取轻量级的Requests解析库组合更灵活、学习曲线更平缓。Scrapy框架更适用于大规模、需要分布式、去重、队列管理等复杂场景的爬虫。数据存储层Pandas CSV/ExcelPandas不仅是数据分析的核心其DataFrame结构也是暂存和初步处理数据的绝佳容器。我们可以先将爬取的数据存入DataFrame再进行清洗和转换。存储格式选择CSV或Excel。CSV通用性好易于用文本编辑器查看Excel则便于直接交付和做简单的图表预览。对于课程作业将清洗后的数据保存为一份干净的Excel文件是加分项。数据处理与分析层Pandas NumPyPandas承担了80%的数据处理工作包括数据清洗处理缺失值、异常值、数据转换类型转换、字符串分割、特征工程从地址提取行政区、从标题提取户型等、分组聚合、排序过滤等。NumPy提供高效的数值计算支持特别是在进行一些统计运算如标准差、分位数时其底层性能优于纯Python。数据可视化层Matplotlib Seaborn (可选) PyEchartsMatplotlib基础绘图库高度自定义用于绘制一些需要精细控制的图表如组合图、地图底图。Seaborn基于Matplotlib的高级接口默认样式更美观且统计图表如分布图、箱线图、热力图绘制极其方便几行代码就能生成信息丰富的图表。PyEcharts如果需要制作交互式图表如可下钻的地图、动态筛选的折线图并嵌入HTML报告PyEcharts是一个很好的选择。对于静态报告前两者足够。开发环境Jupyter Notebook / Lab优势交互式、分段执行非常适合数据探索和分析过程。你可以爬取一段数据后立刻查看、清洗并马上绘制图表观察效果形成快速迭代。最终的代码和图文分析可以整合在一个Notebook中导出为PDF或HTML就是一份完整的报告。注意链家网有反爬机制。直接、高频的访问可能会遇到验证码或IP被封。在代码中务必设置合理的请求间隔如time.sleep(random.uniform(1, 3))并伪装真实的浏览器请求头User-Agent。这是项目能否顺利进行的第一个关键点。3. 爬虫核心实现与数据采集细节3.1 页面结构与URL规律分析链家上海租房列表页的URL具有清晰的规律这是我们设计爬虫循环的基础。例如https://sh.lianjia.com/zufang/pg{page_num}/#contentList其中{page_num}是页码。我们需要先访问第一页解析出总房源数和总页数然后循环构建URL进行爬取。列表页上每个房源条目包含核心信息标题、链接、价格、户型、面积、朝向、楼层、小区名、区域等。但详细信息如具体地址、交通情况、房源描述、配套设施等需要进入详情页才能获取。因此爬虫策略通常分为两步遍历列表页获取所有房源的基本信息和其详情页链接。遍历详情页根据上一步收集的链接逐个访问补充详细信息。3.2 爬虫代码核心模块解析下面是一个高度简化的核心代码框架展示了关键步骤import requests from bs4 import BeautifulSoup import pandas as pd import time import random # 1. 请求头设置关键 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36 } # 2. 初始化会话和存储容器 session requests.Session() all_data [] # 3. 获取总页数 def get_total_pages(base_url): resp session.get(base_url, headersheaders) soup BeautifulSoup(resp.text, html.parser) # 解析页面底部的页码元素这里需要根据实际HTML结构调整选择器 page_data soup.select(.content__pg)[0] total_pages int(page_data[data-totalpage]) return total_pages # 4. 解析单个列表页提取房源基本信息及详情链接 def parse_list_page(page_url): resp session.get(page_url, headersheaders) soup BeautifulSoup(resp.text, html.parser) house_list soup.select(.content__list--item) # 列表项选择器 page_houses [] for item in house_list: try: title_elem item.select(.content__list--item--aside a)[0] title title_elem[title] detail_url https://sh.lianjia.com title_elem[href] price item.select(.content__list--item-price em)[0].text.strip() # 户型、面积、朝向等信息通常在同一个标签内需要拆分 info item.select(.content__list--item--des)[0].text.strip() # 这里需要复杂的字符串处理来拆分info # ... house_info { title: title, detail_url: detail_url, price: price, # ... 其他字段 } page_houses.append(house_info) except Exception as e: print(f解析列表项出错: {e}) continue return page_houses # 5. 解析详情页补充信息 def parse_detail_page(detail_url): time.sleep(random.uniform(1, 2)) # 重要设置随机延迟 resp session.get(detail_url, headersheaders) soup BeautifulSoup(resp.text, html.parser) detail_info {} try: # 提取具体地址、交通、房源描述等 # 这些信息位于不同的标签中需要仔细查看页面结构 address soup.select(.aroundInfo .areaName .info)[0].text.strip() subway soup.select(.aroundInfo .subway)[0].text.strip() if soup.select(.aroundInfo .subway) else 无 description soup.select(.content__article__table)[0].text.strip() detail_info[address] address detail_info[subway] subway detail_info[description] description except Exception as e: print(f解析详情页 {detail_url} 出错: {e}) return detail_info # 6. 主循环 base_url https://sh.lianjia.com/zufang/pg1/#contentList total_pages get_total_pages(base_url) for page in range(1, min(total_pages, 10) 1): # 示例只爬前10页 print(f正在爬取第 {page} 页...) list_url fhttps://sh.lianjia.com/zufang/pg{page}/#contentList houses_on_page parse_list_page(list_url) for house in houses_on_page: detail_info parse_detail_page(house[detail_url]) # 合并基本信息与详情信息 house.update(detail_info) all_data.append(house) time.sleep(random.uniform(0.5, 1.5)) # 详情页访问间隔 time.sleep(random.uniform(2, 4)) # 列表页访问间隔 # 7. 保存为DataFrame和CSV df pd.DataFrame(all_data) df.to_csv(lianjia_shanghai_rent.csv, indexFalse, encodingutf-8-sig) print(f爬取完成共获取 {len(df)} 条数据。)3.3 爬虫环节的实战心得与避坑指南反爬应对链家会对频繁请求进行拦截。除了设置随机延迟和真实User-Agent外有时需要处理Cookie。使用requests.Session()可以自动管理Cookie。如果遇到验证码本项目规模下可以考虑手动处理或跳过或者尝试降低爬取速度。解析稳定性网页结构可能微调。你的选择器如.content__list--item可能在某天失效。因此代码中的try...except异常处理非常重要确保个别解析失败不会导致整个程序崩溃。爬取完成后务必检查数据量是否合理是否有大量缺失字段。数据去重同一房源可能在多个列表页出现虽然链家做了去重但自己处理一遍更稳妥。可以根据房源ID或详情页URL进行去重。伦理与法律本项目数据仅用于学习研究。务必控制爬取速度和数量不要对目标网站服务器造成压力。在最终报告中应声明数据来源及用途。4. 数据清洗与特征工程实战爬取到的原始数据是“脏”的无法直接分析。数据清洗是决定分析质量的关键一步。4.1 典型脏数据问题及处理将CSV数据读入Pandas的DataFrame后我们通常会面临以下问题import pandas as pd import numpy as np df pd.read_csv(lianjia_shanghai_rent_raw.csv) print(df.head()) print(df.info())价格字段原始数据可能是“8500元/月”需要提取数字并转换为整型。df[price_num] df[price].str.extract(r(\d)).astype(int)面积字段可能是“85.3平米”需要提取数字转换为浮点型。df[area_num] df[area].str.extract(r(\d\.?\d*)).astype(float)户型字段可能是“3室1厅1卫”。我们需要将其拆分为“室”、“厅”、“卫”三个独立的数值特征。# 使用正则表达式分别提取 df[rooms] df[layout].str.extract(r(\d)室).astype(float) df[halls] df[layout].str.extract(r(\d)厅).astype(float) df[baths] df[layout].str.extract(r(\d)卫).astype(float) # 处理可能存在的缺失值 df[[rooms, halls, baths]] df[[rooms, halls, baths]].fillna(0)楼层信息可能是“高楼层/共6层”。可以拆分为“所在楼层”和“总楼层”并尝试判断是否为“低/中/高楼层”。# 提取楼层和总楼层 floor_split df[floor].str.split(/, expandTrue) df[floor_level] floor_split[0] # 如“高楼层” df[total_floor] floor_split[1].str.extract(r(\d)).astype(float) # 如“6”地址信息从详情页爬取的地址可能很长。我们需要提取出“行政区”如“浦东新区”、“徐汇区”和“板块/街道”信息这是后续做区域分析的基础。# 假设地址格式为“上海浦东新区张江镇xxx” # 可以简单通过关键词匹配行政区 districts [浦东新区, 徐汇区, 长宁区, 普陀区, 静安区, 虹口区, 杨浦区, 黄浦区, 闵行区, 宝山区, 嘉定区, 金山区, 松江区, 青浦区, 奉贤区, 崇明区] def extract_district(address): for d in districts: if d in address: return d return 其他 df[district] df[address].apply(extract_district)缺失值与异常值缺失值对于关键字段如价格、面积的缺失如果比例很小可以直接删除该行。如果比例大需要根据业务逻辑填充如用同小区均价填充价格。异常值价格或面积出现极值如月租100元或100万元面积1平米或1000平米需要结合业务常识进行筛选或修正。可以使用箱线图或df.describe()辅助识别。4.2 特征工程创造更有价值的分析维度清洗后的基础数据可以直接分析但通过特征工程我们能问出更有深度的问题。单位面积租金这是衡量租金性价比的核心指标。df[price_per_sqm] df[price_num] / df[area_num]地铁距离从“subway”字段中提取“距离X号线Y站Z米”并提取出数值距离。可以将其分为“地铁房”1000米和“非地铁房”。df[subway_dist] df[subway].str.extract(r距(\d)号线.*?(\d)米).iloc[:, 1].astype(float) df[is_near_subway] df[subway_dist].apply(lambda x: 1 if pd.notnull(x) and x 1000 else 0)商圈/地标关联从标题或描述中提取关键词如“陆家嘴”、“静安寺”、“徐家汇”、“大学城”等作为虚拟特征。装修情况从标题中提取“精装”、“简装”、“毛坯”等信息。清洗和特征工程完成后保存一份干净的数据集df_clean df[[title, district, price_num, area_num, price_per_sqm, rooms, halls, baths, floor_level, total_floor, is_near_subway, subway_dist, address]].copy() df_clean.to_csv(lianjia_shanghai_rent_clean.csv, indexFalse, encodingutf-8-sig)5. 数据分析与可视化探索有了干净的数据就可以开始探索了。分析的核心是提出问题并用数据和图表来回答。5.1 整体市场概况分析这是分析的起点旨在对上海租房市场有一个宏观把握。租金分布绘制租金和单位面积租金的分布直方图与核密度估计图。import seaborn as sns import matplotlib.pyplot as plt plt.figure(figsize(15, 5)) plt.subplot(1, 2, 1) sns.histplot(df_clean[price_num], bins50, kdeTrue) plt.title(月租金分布) plt.xlabel(月租金元) plt.subplot(1, 2, 2) sns.histplot(df_clean[price_per_sqm], bins50, kdeTrue) plt.title(单位面积租金分布) plt.xlabel(元/平米/月) plt.tight_layout() plt.show() # 输出关键统计量 print(df_clean[[price_num, price_per_sqm]].describe())分析点租金分布是右偏的吗大部分房源集中在什么价格区间单位面积租金的中位数是多少房源区域分布统计各行政区的房源数量并绘制条形图。district_count df_clean[district].value_counts() plt.figure(figsize(12,6)) sns.barplot(xdistrict_count.index, ydistrict_count.values) plt.title(各行政区房源数量分布) plt.xticks(rotation45) plt.ylabel(房源数量) plt.show()分析点哪个区出租房源最多是浦东、闵行这些面积大、人口多的区吗5.2 深入维度交叉分析这是体现分析深度的部分将多个维度结合起来看。区域租金对比比较不同行政区的平均租金和单位面积租金。district_price df_clean.groupby(district)[price_num, price_per_sqm].mean().sort_values(price_per_sqm, ascendingFalse) fig, axes plt.subplots(2, 1, figsize(14, 10)) sns.barplot(xdistrict_price.index, ydistrict_price[price_num], axaxes[0]) axes[0].set_title(各行政区平均月租金) axes[0].set_xticklabels(axes[0].get_xticklabels(), rotation45) axes[0].set_ylabel(平均月租金元) sns.barplot(xdistrict_price.index, ydistrict_price[price_per_sqm], axaxes[1]) axes[1].set_title(各行政区平均单位面积租金) axes[1].set_xticklabels(axes[1].get_xticklabels(), rotation45) axes[1].set_ylabel(平均元/平米/月) plt.tight_layout() plt.show()分析点黄浦、静安等核心城区的单位面积租金是否远高于郊区哪个区的租金性价比最高户型与租金关系分析不同卧室数量1室、2室、3室的租金分布。plt.figure(figsize(10,6)) sns.boxplot(xrooms, yprice_num, datadf_clean[df_clean[rooms] 4]) plt.title(不同户型月租金箱线图) plt.xlabel(卧室数量) plt.ylabel(月租金元) plt.show()分析点租金随卧室数量增加是线性增长吗2室户型的租金范围是否最广地铁因素对租金的影响对比地铁房与非地铁房的租金差异。plt.figure(figsize(8,6)) sns.boxplot(xis_near_subway, yprice_per_sqm, datadf_clean) plt.title(地铁房 vs 非地铁房单位面积租金) plt.xlabel(是否近地铁 (1是, 0否)) plt.ylabel(单位面积租金元/平米/月) plt.xticks([0,1], [非地铁房, 地铁房]) plt.show() # 进行统计检验如t检验验证差异是否显著 from scipy import stats near df_clean[df_clean[is_near_subway]1][price_per_sqm] far df_clean[df_clean[is_near_subway]0][price_per_sqm] t_stat, p_val stats.ttest_ind(near.dropna(), far.dropna(), equal_varFalse) print(fT检验 p值: {p_val:.4f})分析点地铁房的租金溢价有多大这个溢价在不同区域是否一致面积-租金散点图观察面积与总租金的关系并引入区域作为第三维度用颜色区分。plt.figure(figsize(12,8)) # 为了图例清晰可以选择房源最多的几个区 top_districts df_clean[district].value_counts().head(5).index.tolist() df_top df_clean[df_clean[district].isin(top_districts)] sns.scatterplot(xarea_num, yprice_num, huedistrict, datadf_top, alpha0.6) plt.title(房源面积与月租金关系按区域着色) plt.xlabel(面积平米) plt.ylabel(月租金元) plt.legend(title行政区) plt.show()分析点是否存在明显的线性关系在同一面积下不同区域的租金“散点带”位置是否不同有没有一些面积小但租金奇高的异常点可能是豪华装修或特殊地段5.3 高级可视化与报告整合热力图可以绘制“行政区-户型”矩阵的平均租金热力图直观显示哪些区域、哪种户型最贵。# 创建透视表 pivot_table df_clean.pivot_table(valuesprice_per_sqm, indexdistrict, columnsrooms, aggfuncmean) plt.figure(figsize(12,8)) sns.heatmap(pivot_table, annotTrue, fmt.1f, cmapYlOrRd) plt.title(各行政区-不同户型单位面积租金热力图元/平米/月) plt.xlabel(卧室数量) plt.ylabel(行政区) plt.show()地理分布图进阶如果爬取了足够精确的经纬度或小区名可以利用geopandas、folium等库绘制房源在地图上的分布并用颜色或大小表示租金高低。这对于展示“核心商圈辐射效应”非常有力。Jupyter报告整合在Jupyter Notebook中将上述所有代码块、分析文字、图表有序组织。使用Markdown单元格撰写分析结论形成一份数据驱动、图文并茂的分析报告。最后可以通过File - Download as - HTML或使用nbconvert工具将其导出为独立的HTML或PDF报告。6. 项目难点、常见问题与优化方向6.1 爬虫阶段常见问题请求被拒或返回空数据检查请求头确保User-Agent是真实的浏览器字符串可以添加Referer等字段。检查Cookie有时需要先访问一次首页获取初始Cookie。使用Session对象通常能自动处理。降低频率大幅增加请求间隔时间尤其是在爬取详情页时。模拟滚动/点击对于部分通过JavaScript加载的数据简单的Requests可能无法获取。可以考虑使用Selenium模拟浏览器行为但会大幅降低效率。解析标签失效原因网站前端改版。解决重新审查网页元素结构更新CSS选择器或XPath。编写更健壮的解析逻辑多用try-except并考虑使用多个备选选择器。数据不完整原因网络波动、反爬导致部分页面抓取失败。解决实现断点续爬功能。将已成功爬取的URL列表保存下来每次启动时先加载这个列表避免重复爬取。对于失败的URL可以将其放入重试队列。6.2 数据分析阶段常见问题数据清洗逻辑错误问题正则表达式写错导致提取出的数字包含多余字符或为空。排查清洗后务必使用df.head()、df[‘column’].unique()、df.describe()等方式抽样检查数据质量。绘制一些简单的散点图或箱线图也能快速发现异常值。分析结论片面问题只做了单变量分析结论缺乏深度。提升多进行交叉分析如区域vs户型vs地铁。尝试提出假设并用数据验证如“地铁对郊区租金的提升效应比市区更明显”。6.3 项目扩展与优化方向如果想把这个项目做得更出彩可以考虑以下方向数据层面扩大数据量爬取更多页面覆盖更长时间段需处理翻页和去重分析租金随时间月度/季度的变化趋势。丰富数据维度尝试爬取房源图片数量、经纪人信息、带看次数如有等分析这些因素是否影响租金。引入外部数据将各行政区的地铁线路数、商业中心数量、平均工资水平等数据与租金进行关联分析。分析层面租金预测模型将“月租金”或“单位面积租金”作为目标变量使用区域、面积、户型、楼层、是否近地铁等作为特征构建一个简单的机器学习模型如线性回归、决策树预测租金。这能极大提升项目的技术含量。聚类分析对房源进行聚类发现不同的房源群落如“高端豪华型”、“刚需地铁型”、“郊区性价比型”等。工程层面将爬虫脚本化、自动化使用argparse库接收参数如爬取页码范围、区域配置化请求头并增加日志记录功能。构建简单数据看板使用Dash或Streamlit框架将分析结果做成一个交互式Web应用允许用户选择区域、户型等条件动态查看租金分布和统计。这个项目从爬虫到分析再到可视化的全流程走下来不仅能巩固Python数据处理技能更能培养解决实际问题的数据思维。最关键的是过程中遇到的每一个报错和每一个数据异常都是宝贵的经验。本文还有配套的精品资源点击获取
上一篇/下一篇内容由系统自动关联 返回资讯列表 →