
简介本资源是一套完整的南京二手房数据分析实战项目面向Python初学者、数据科学入门者及计算机专业本科毕业设计/期末大作业需求者聚焦真实业务场景下的数据采集、清洗、可视化与聚类分析全流程。资源包共157个文件含18个核心Python脚本爬虫、清洗、绘图、聚类、18个CSV数据文件含原始与多版本清洗后数据、65张可视化图表PNG、15个HTML地理热力图页面、11个JS高德地图交互脚本以及配套PPT汇报文档整体压缩包大小为40.04MB。已有997人学习下载内容覆盖RequestsBeautifulSoup链家网动态反爬应对、Pandas数据清洗技巧、MatplotlibSeaborn多维度可视化、k-means房源聚类建模及高德地图JS API地理空间呈现等关键技术点所有代码可直接运行数据字段清晰标注目录结构按“采集→清洗→分析→展示”逻辑组织便于理解工程化数据分析闭环。1. 项目概述与核心价值最近在整理过往的数据分析项目翻到了一个挺有意思的“老伙计”——一个基于Python的南京二手房数据可视化分析项目。这个项目麻雀虽小五脏俱全从数据爬取、清洗、分析到可视化最后还生成了PPT报告算是一个完整的数据分析流程闭环。我把它重新整理了一下源码和PPT都打包好了今天就来详细拆解一下这个项目的来龙去脉、技术实现以及那些只有实操过才知道的“坑”。这个项目的核心目标很明确利用Python技术栈对南京二手房市场的公开数据进行一次“体检”。我们想知道的不仅仅是房价数字更是数字背后的故事哪些区域是价格高地房价和面积、楼层、装修情况有什么关系市场的整体热度如何通过数据可视化的方式把这些抽象的问题变成直观的图表无论是用于个人购房参考、市场趋势研究还是作为数据分析的学习案例都很有价值。对于刚入门Python数据分析的朋友来说这是一个非常好的练手项目涵盖了Pandas数据处理、Matplotlib/Seaborn绘图、Jupyter Notebook使用等核心技能对于有经验的分析师其中的数据清洗逻辑、可视化故事线设计以及自动化报告生成的思路或许也能带来一些启发。2. 项目整体设计与技术栈选型2.1 需求分析与数据源确定做任何数据分析项目第一步永远是明确“要什么”和“有什么”。对于南京二手房市场我们关心几个维度的信息基础属性总价、单价、建筑面积、户型室厅卫、楼层、朝向、装修情况、建筑年代。位置属性行政区如鼓楼、建邺、秦淮、具体板块或商圈、小区名称。市场属性挂牌时间、关注人数、带看次数如果可获得。数据源方面我们选择了国内主流的房产信息平台。这里必须强调合规与伦理我们的爬虫仅针对公开的、非敏感的房源列表页信息进行采集严格遵守网站的robots.txt协议控制请求频率添加延时避免对目标服务器造成压力。项目源码中会使用requests库和BeautifulSoup或pyquery进行页面解析绝对不涉及任何绕过反爬、破解加密等违规操作所有数据获取均在合法合规的框架内进行模拟人工浏览。注意网络数据采集具有时效性和法律风险。本项目代码及思路仅用于学习交流在实际应用中务必尊重数据版权遵守相关法律法规和网站的使用条款。建议优先考虑使用官方API或购买合规数据。2.2 技术栈选型与理由为什么选择这套技术组合这是经过权衡的核心数据处理Pandas。这是Python数据分析的事实标准。它的DataFrame结构非常适合处理表格型数据内置的聚合、分组、合并、透视表功能强大到不可思议清洗和转换数据的效率极高。基础可视化Matplotlib Seaborn。Matplotlib是绘图库的基石高度可定制能画出任何你想要的图表。但它的默认样式比较“学术”。Seaborn基于Matplotlib提供了更美观的统计图形默认样式和高级接口如pairplot,heatmap两者结合既能快速出图又能精细调整。交互式探索Jupyter Notebook。数据分析是一个不断试错和探索的过程。Notebook的单元格执行模式完美契合这一流程可以边写代码边看结果即时可视化非常适合数据清洗和初步分析阶段的探索性工作。报告生成Python-pptx。这是项目的亮点之一。我们不是手动做PPT而是用代码自动化生成python-pptx库允许我们以编程方式创建和编辑PowerPoint文件将分析结论和核心图表自动插入到预设好的幻灯片模板中实现分析报告的一键生成极大提升了效率。辅助工具NumPy, Scipy.stats。用于数值计算和基本的统计分析如计算相关性、进行分布检验等。这套组合拳的优势在于全流程Python化、从数据到见解再到报告的高度自动化、强大的社区支持和丰富的学习资源。3. 数据获取、清洗与预处理实战3.1 数据爬取策略与代码结构爬虫部分的核心是稳健性和可维护性。我们采用面向对象的思想来设计爬虫模块。定义数据模型首先定义一个HouseItem类包含我们需要的所有字段如标题、总价、单价、面积等。这样每条房源信息都是一个对象结构清晰。请求与解析分离使用requests.Session()维持会话并设置合理的请求头User-Agent、Referer等模拟浏览器。解析函数专门负责从HTML中提取信息使用BeautifulSoup配合CSS选择器代码更易读。异常处理与日志记录网络请求充满不确定性。必须用try...except包裹核心请求和解析代码捕获超时、解析失败等异常并记录到日志文件中便于后续排查是哪一页或哪一个房源出了问题。遵守爬虫礼仪在循环请求页面时使用time.sleep(random.uniform(1, 3))添加随机延时避免请求过于密集。同时监控返回状态码遇到403/429等可能被封禁的提示时自动暂停。# 示例代码结构简化版 import requests from bs4 import BeautifulSoup import time import random import logging logging.basicConfig(levellogging.INFO, format%(asctime)s - %(levelname)s - %(message)s) class NanjingSecondHandHouseSpider: def __init__(self): self.session requests.Session() self.headers {User-Agent: 你的浏览器User-Agent} self.data_list [] def parse_page(self, url): try: resp self.session.get(url, headersself.headers, timeout10) resp.raise_for_status() soup BeautifulSoup(resp.text, html.parser) # 使用CSS选择器定位房源列表 house_list soup.select(.sellListContent li) for house in house_list: item self.parse_item(house) if item: self.data_list.append(item) time.sleep(random.uniform(1.5, 3)) except Exception as e: logging.error(f请求或解析页面失败: {url}, 错误: {e}) def parse_item(self, house_element): # 提取具体字段这里需要根据实际网页结构调整选择器 try: title house_element.select_one(.title a).text.strip() # 提取价格并处理“万”字 total_price_str house_element.select_one(.totalPrice span).text total_price float(total_price_str.replace(万, )) # ... 提取其他字段 return {title: title, total_price: total_price, ...} except AttributeError as e: logging.warning(f解析单个房源信息时字段缺失: {e}) return None # 使用 spider NanjingSecondHandHouseSpider() base_url https://nj.ke.com/ershoufang/pg{}/ for page in range(1, 51): # 假设爬取前50页 url base_url.format(page) spider.parse_page(url) logging.info(f已爬取第{page}页)3.2 数据清洗的“脏活累活”爬下来的原始数据通常很“脏”清洗是保证分析质量的关键。我们用Pandas来高效完成。加载与初步观察将data_list转换为Pandas DataFrame使用df.info()和df.head()查看数据概览和缺失情况。处理缺失值对于数值型特征如单价、面积如果缺失比例不高可以用中位数或均值填充df[column].fillna(df[column].median(), inplaceTrue)。对于楼层、朝向等类别特征可以填充为“未知”。如果某条记录关键字段如总价、面积缺失直接删除该行df.dropna(subset[关键列], inplaceTrue)。处理异常值单价/总价异常通过描述性统计df[price_per_sqm].describe()和箱线图发现那些极高或极低的离谱价格比如单价1000元/平或50万元/平这类数据需要查明原因通常是数据提取错误或特殊房源如车位应予以剔除。面积异常同样对于过小20平或过大500平的住宅面积需要谨慎对待可能是商铺或别墅根据分析目标决定是否保留。数据格式标准化字符串处理从“3室2厅1卫”中提取数字。可以使用正则表达式df[layout].str.extract(r(\d)室。单位统一确保面积单位是“平方米”总价单位是“万元”。分类数据编码将“楼层”中的“高楼层”、“中楼层”、“低楼层”等信息提取出来或将“装修情况”转化为有序类别毛坯 简装 精装 豪装。特征工程计算“房龄”用当前年份减去“建筑年代”。创建“行政区”特征从地址或小区信息中提取出区名如鼓楼区、玄武区。import pandas as pd import numpy as np # 假设df是原始DataFrame # 1. 处理面积和总价去除单位并转换为数值型 df[area] df[area_str].str.replace(平米, ).astype(float) df[total_price] df[total_price_str].str.replace(万, ).astype(float) df[price_per_sqm] df[total_price] * 10000 / df[area] # 计算单价元/平 # 2. 处理户型 df[rooms] df[layout].str.extract(r(\d)室).astype(float) df[halls] df[layout].str.extract(r(\d)厅).astype(float) # 3. 处理楼层示例将字符串分类 def map_floor(floor_str): if 高 in floor_str: return high elif 中 in floor_str: return middle elif 低 in floor_str: return low else: return unknown df[floor_category] df[floor].apply(map_floor) # 4. 处理房龄 current_year pd.Timestamp.now().year df[house_age] current_year - df[build_year].astype(float) # 5. 删除关键信息缺失或异常的行 df_clean df[ (df[price_per_sqm] 5000) (df[price_per_sqm] 150000) # 合理的单价范围 (df[area] 20) (df[area] 500) # 合理的面积范围 (df[total_price].notna()) ].copy()4. 多维数据可视化分析与洞察数据清洗完毕后就进入了最有趣的部分——用图表讲故事。我们按照从宏观到微观的逻辑来展开。4.1 宏观市场概览分布与趋势首先对市场有一个整体把握。价格分布直方图与核密度估计使用seaborn.histplot绘制单价的分布并叠加kdeTrue显示密度曲线。这能立刻看出南京二手房单价主要集中在哪个区间例如3-5万/平分布是正态还是偏态。各行政区均价对比用seaborn.barplot绘制每个行政区的平均单价条形图并按价格排序。一眼就能看出鼓楼、建邺是价格高地而六合、高淳是洼地。总价-面积散点图这是看房源分布的核心图表。用matplotlib.pyplot.scatter或seaborn.scatterplot以面积为X轴总价为Y轴每个点代表一个房源。可以清晰地看到正相关趋势以及那些“面积小但总价高”可能学区房或“面积大但单价低”可能偏远的离群点。import matplotlib.pyplot as plt import seaborn as sns sns.set_style(whitegrid) # 设置Seaborn样式 plt.figure(figsize(15, 10)) # 子图1单价分布 plt.subplot(2, 2, 1) sns.histplot(datadf_clean, xprice_per_sqm, bins50, kdeTrue) plt.title(南京二手房单价分布) plt.xlabel(单价 (元/平方米)) plt.ylabel(数量) # 子图2各区均价 plt.subplot(2, 2, 2) # 先按区分组计算均价 district_avg_price df_clean.groupby(district)[price_per_sqm].mean().sort_values(ascendingFalse) sns.barplot(xdistrict_avg_price.index, ydistrict_avg_price.values) plt.title(各行政区二手房平均单价) plt.xlabel(行政区) plt.ylabel(平均单价 (元/平方米)) plt.xticks(rotation45) # 旋转x轴标签 # 子图3总价-面积散点图 plt.subplot(2, 2, 3) plt.scatter(df_clean[area], df_clean[total_price], alpha0.6, s10) # alpha透明度s点大小 plt.title(二手房总价与面积关系) plt.xlabel(建筑面积 (平方米)) plt.ylabel(总价 (万元)) # 可以添加一条趋势线 z np.polyfit(df_clean[area], df_clean[total_price], 1) p np.poly1d(z) plt.plot(df_clean[area], p(df_clean[area]), r--, linewidth2) plt.tight_layout() plt.show()4.2 微观因素解析什么在影响价格接下来深入挖掘影响房价的因素。房龄与单价关系绘制散点图或箱线图。通常房龄越新单价越高但超过一定年限后影响减弱。可以计算两者的相关系数。楼层与单价关系使用seaborn.boxplot绘制不同楼层分类高、中、低的单价箱线图。通常中楼层最受欢迎价格可能也最高。装修情况与单价关系同样使用箱线图或小提琴图seaborn.violinplot比较“毛坯”、“简装”、“精装”、“豪装”之间的价格差异。精装和豪装会有明显的溢价。户型室/厅与总价/单价关系可以绘制“室数”与平均总价的条形图或者用热力图展示“室数”和“厅数”组合下的平均单价。plt.figure(figsize(16, 12)) # 子图1房龄与单价关系散点图回归线 plt.subplot(2, 3, 1) sns.regplot(datadf_clean, xhouse_age, yprice_per_sqm, scatter_kws{alpha:0.3, s:10}, line_kws{color:red}) plt.title(房龄对单价的影响) plt.xlabel(房龄 (年)) plt.ylabel(单价 (元/平方米)) # 子图2楼层与单价关系箱线图 plt.subplot(2, 3, 2) order [low, middle, high, unknown] sns.boxplot(datadf_clean, xfloor_category, yprice_per_sqm, orderorder) plt.title(不同楼层单价对比) plt.xlabel(楼层类别) plt.ylabel(单价 (元/平方米)) # 子图3装修情况与单价关系小提琴图 plt.subplot(2, 3, 3) # 假设有decoration列值为毛坯,简装,精装,豪装 decoration_order [毛坯, 简装, 精装, 豪装] sns.violinplot(datadf_clean, xdecoration, yprice_per_sqm, orderdecoration_order) plt.title(装修情况对单价的影响) plt.xlabel(装修情况) plt.ylabel(单价 (元/平方米)) plt.xticks(rotation15) # 子图4室数与平均总价关系 plt.subplot(2, 3, 4) avg_price_by_room df_clean.groupby(rooms)[total_price].mean() sns.barplot(xavg_price_by_room.index.astype(int), yavg_price_by_room.values) plt.title(不同室数房源的平均总价) plt.xlabel(室数) plt.ylabel(平均总价 (万元)) plt.tight_layout() plt.show()4.3 高级分析与可视化为了更深入的洞察我们可以进行一些高级分析。相关性热力图计算数值型字段单价、总价、面积、房龄、室数等之间的皮尔逊相关系数矩阵并用seaborn.heatmap绘制。这能快速发现强相关变量如面积与总价正相关房龄与单价负相关。基于地理信息的可视化进阶如果我们有小区或板块的经纬度信息可以通过地理编码API获取就可以使用folium或geopandascontextily库绘制交互式或静态地图。例如将每个房源以圆点形式标注在地图上颜色和大小代表单价或总价可以直观看到城市内部的价格空间分布和聚集情况。5. 自动化报告生成与PPT制作分析完成图表也出来了最后一步是把成果固化成一份专业的报告。手动复制粘贴图表到PPT里既低效又容易出错。我们用python-pptx实现自动化。5.1 设计PPT模板首先在PowerPoint里手动设计一个简单的报告模板.pptx文件包含几种版式的幻灯片标题页报告标题、作者、日期。目录页。章节标题页如“市场概览”、“因素分析”。内容页主要是“标题内容”或“仅内容”版式用于放置图表和文字描述。总结页。将设计好的模板保存为template.pptx。5.2 使用python-pptx填充内容然后编写Python脚本读取模板在指定位置插入分析结论和保存好的图表图片。from pptx import Presentation from pptx.util import Inches, Pt from pptx.dml.color import RGBColor import os def create_analysis_ppt(chart_image_paths, summary_texts, output_path南京二手房分析报告.pptx): chart_image_paths: 字典键为幻灯片序号从0开始值为图表图片路径列表 summary_texts: 字典键为幻灯片序号值为该页的文本内容列表 # 1. 加载模板 prs Presentation(template.pptx) # 假设我们的模板中第二张幻灯片索引1是目录后的第一个内容页 # 2. 添加市场概览页 slide_layout prs.slide_layouts[2] # 选择“标题和内容”版式 slide prs.slides.add_slide(slide_layout) title slide.shapes.title title.text 一、市场宏观概览 # 在内容占位符中添加文字 content_box slide.placeholders[1] # 通常索引1是内容占位符 tf content_box.text_frame tf.clear() # 清空默认文本 p tf.paragraphs[0] p.text 本节主要展示南京二手房市场的整体价格分布、区域差异及基本特征。 # 插入图片这里需要根据模板调整位置和大小 # 例如我们有三张图表price_dist.png, district_avg.png, scatter.png left Inches(0.5) top Inches(1.5) height Inches(4) pic slide.shapes.add_picture(price_dist.png, left, top, heightheight) left2 Inches(5.0) pic2 slide.shapes.add_picture(district_avg.png, left2, top, heightheight) # 可以添加文本框描述图表 from pptx.util import Pt left_txt Inches(0.5) top_txt Inches(5.6) width Inches(9) height_txt Inches(0.5) textbox slide.shapes.add_textbox(left_txt, top_txt, width, height_txt) tf textbox.text_frame p tf.paragraphs[0] p.text 图1: 单价呈右偏分布主力区间在3-4万元/平。图2: 鼓楼、建邺均价领先。 p.font.size Pt(10) # 3. 添加因素分析页类似操作 # ... 重复以上步骤插入房龄、楼层、装修等分析图表 # 4. 添加总结页 slide_layout_summary prs.slide_layouts[3] # “仅标题”版式 slide_summary prs.slides.add_slide(slide_layout_summary) slide_summary.shapes.title.text 核心结论与建议 left Inches(1) top Inches(1.5) width Inches(8) height Inches(5) textbox slide_summary.shapes.add_textbox(left, top, width, height) tf textbox.text_frame summary_points [ 1. 南京二手房市场单价分布集中区域分化明显。, 2. 房龄、楼层、装修是影响单价的重要因素其中装修溢价最显著。, 3. 面积与总价强相关但部分学区房表现出‘面积小、总价高’特征。, 4. 建议购房者结合自身需求重点关注中楼层、精装以上的房源并权衡通勤与学区。 ] for point in summary_points: p tf.add_paragraph() p.text point p.font.size Pt(18) p.space_after Pt(6) # 保存最终报告 prs.save(output_path) print(f报告已生成: {output_path}) # 使用函数传入图表路径和文本 chart_map { 2: [price_dist.png, district_avg.png, scatter.png], # 第三页的图表 3: [age_vs_price.png, floor_box.png, decoration_violin.png], # 第四页的图表 } text_map { 2: [宏观市场分析...], 3: [微观因素解析...] } create_analysis_ppt(chart_map, text_map)5.3 实操心得让PPT更专业先设计后编码一定要先在PPT里把模板的版式和占位符位置确定好记录下每个内容框的索引slide.placeholders[index]或通过代码遍历查找。python-pptx对位置的操控是精确到英寸的提前规划事半功倍。图片预处理在插入PPT前最好用matplotlib或PIL库将生成的图表图片统一分辨率如300 DPI和尺寸确保在PPT中清晰且比例协调。字体嵌入问题这是个大坑如果你在代码中设置了特殊字体而对方电脑没有显示会 fallback 到默认字体。python-pptx本身不解决字体嵌入。一个变通方法是将包含特殊文字的图表以图片形式插入而不是在PPT里直接添加文字框。或者在保存PPT后手动在PowerPoint里进行“文件”-“选项”-“保存”-“将字体嵌入文件”操作但这需要客户端有PowerPoint。动态内容对于需要每次更新数字的文本如“本次共分析{len(df)}条房源”可以在代码中通过字符串格式化f-string动态生成再插入到文本框中。6. 项目复盘、常见问题与优化方向6.1 爬虫环节的典型问题反爬虫机制网站可能会更换HTML结构、添加动态加载JavaScript、要求验证码或封禁高频IP。应对定期检查并更新解析逻辑对于动态加载可以考虑使用Selenium或Playwright模拟浏览器但速度慢使用代理IP池最重要的还是遵守爬虫礼仪控制频率。数据字段缺失或错位网页结构可能因房源类型不同而有细微差别导致正则或选择器提取失败。应对加强异常处理对每个字段的提取都进行try-except并记录日志。采用更健壮的提取方法比如先定位到房源卡片整体区域再在其内部逐个尝试提取字段。数据更新二手房数据变化快需要定期运行爬虫。应对将爬虫脚本部署到云服务器使用cron或APScheduler设置定时任务如每周运行一次。注意数据去重可以将新爬取的数据与历史数据库对比只新增或更新变化的房源。6.2 数据分析与可视化环节的坑图表信息过载在一张图上堆砌太多元素导致可读性差。应对遵循“一张图说清一件事”的原则。多使用子图plt.subplot来并排展示相关图表。合理运用颜色、图例和标注。忽略数据分布直接对非正态分布的数据求平均值和标准差可能产生误导。应对在分析前先用直方图、Q-Q图或scipy.stats中的函数检验数据分布。对于偏态分布报告中可能使用中位数和四分位数更合适。相关性不等于因果发现房龄和单价负相关就断言“房龄导致降价”。应对在报告中谨慎表述指出这是“相关性”或“关联关系”并提示可能存在的混杂因素如地段、学区等。6.3 项目扩展与优化方向这个项目作为一个起点有很多可以深化的地方数据源融合结合租房数据、新房数据、土地拍卖数据、宏观经济指标如利率、人口流入进行交叉分析构建更全面的市场模型。机器学习应用将本项目作为一个回归预测问题的数据集。使用清洗后的特征面积、房龄、楼层、区位编码等训练模型如线性回归、随机森林、XGBoost来预测房价并分析特征重要性。构建交互式仪表盘使用Plotly Dash或Streamlit框架将分析结果做成一个Web应用。用户可以通过下拉菜单选择行政区、拖动滑块筛选价格区间图表会动态更新体验更佳。自动化流水线使用Airflow或Prefect等工具将数据爬取、清洗、分析、报告生成的全流程编排成一个自动化DAG有向无环图实现真正的“一键运行报告自来”。回过头看这个项目最大的收获不是那几张图表而是完整走通了一个数据项目的闭环。从确定目标、获取数据、清洗整理、分析挖掘到成果呈现每一步都有其挑战和乐趣。对于初学者我建议不要一开始就追求大而全可以先把每个环节的小demo跑通比如先学会爬一页数据再学会画一张漂亮的图最后把它们串起来。过程中遇到的每一个报错都是最好的学习材料。本文还有配套的精品资源点击获取