重庆市二手房价格数据分析与可视化系统
重庆市二手房价格数据分析与可视化系统 - 技术教学文档项目地址: https://github.com/NewDay412/MetroHouseLab第一章项目架构设计1.1 整体架构本项目采用模块化设计分为以下几个核心模块┌─────────────────────────────────────────────────────────────┐ │ 用户界面层 (Web) │ │ Flask ECharts HTML/CSS/JS │ ├─────────────────────────────────────────────────────────────┤ │ 业务逻辑层 │ │ 数据爬取 | 数据清洗 | 数据分析 | 增量更新 │ ├─────────────────────────────────────────────────────────────┤ │ 数据存储层 │ │ CSV 文件 | MongoDB 数据库 │ ├─────────────────────────────────────────────────────────────┤ │ 外部数据源 │ │ 安居客 | 链家 (API/HTML) │ └─────────────────────────────────────────────────────────────┘1.2 模块职责划分模块职责核心技术spiders数据采集Selenium、requests、BeautifulSoupdata_cleaning数据清洗pandas、正则表达式data_mining数据分析scikit-learnK-Means、线性回归storage数据存储CSV、MongoDBweb可视化展示Flask、ECharts第二章爬虫技术实现2.1 爬虫架构设计# 爬虫基类设计原则classBaseSpider:def__init__(self):self.headersself._build_headers()self.sessionself._create_session()self.storageCSVStorage()def_build_headers(self):构建请求头模拟浏览器return{User-Agent:self.ua.random,Accept:text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8,Accept-Language:zh-CN,zh;q0.9,en;q0.8,Accept-Encoding:gzip, deflate, br,Connection:keep-alive,Cache-Control:max-age0,}2.2 Selenium 反爬绕过技术2.2.1 隐藏 WebDriver 特征# 关键配置隐藏自动化特征chrome_options.add_argument(--disable-blink-featuresAutomationControlled)chrome_options.add_experimental_option(excludeSwitches,[enable-automation])chrome_options.add_experimental_option(useAutomationExtension,False)# CDP 命令注入彻底隐藏 webdriver 属性driver.execute_cdp_cmd(Page.addScriptToEvaluateOnNewDocument,{source: Object.defineProperty(navigator, webdriver, { get: () undefined }); Object.defineProperty(navigator, plugins, { get: () [1, 2, 3, 4, 5] }); Object.defineProperty(navigator, languages, { get: () [zh-CN, zh, en] }); })2.2.2 模拟人类行为defsimulate_human(self):模拟人类行为降低被识别风险# 随机滚动for_inrange(random.randint(2,4)):scroll_amountrandom.uniform(100,300)self.driver.execute_script(fwindow.scrollBy(0,{scroll_amount}))self.random_delay(0.3,0.8)# 随机鼠标移动actionActionChains(self.driver)for_inrange(random.randint(2,3)):xrandom.randint(100,800)yrandom.randint(100,600)action.move_by_offset(x,y).pause(random.uniform(0.1,0.3))action.perform()2.3 反爬检测与处理defis_blocked(self):检测是否被反爬拦截page_sourceself.driver.page_source.lower()# 网易易盾验证码检测ifyiduninpage_sourceorcaptcha-wyinpage_source:returnTrue# 通用验证码检测if请验证您是真人inpage_sourceor人机验证inpage_source:returnTrue# 反垃圾检测ifantispaminpage_sourceorcaptchainpage_source:returnTruereturnFalse2.4 数据解析技术defparse_anjuke(self,html,district):解析安居客页面数据soupBeautifulSoup(html,lxml)# 多重选择器策略应对页面结构变化itemssoup.select(.property-ex)orsoup.select(.list-item)orsoup.select([class*property])foriteminitems:# 价格解析核心逻辑price_total_textitem.select_one(.property-price-total).get_text(stripTrue)price_avg_textitem.select_one(.property-price-average).get_text(stripTrue)# 正则提取数字total_pricefloat(re.search(r(\d(?:\.\d)?),price_total_text).group(1))# 单价解析从元转换为元/㎡unit_price_valfloat(re.search(r(\d),price_avg_text).group(1))# 面积解析area_matchre.search(r(\d(?:\.\d)?)㎡,info_text)areafloat(area_match.group(1))ifarea_matchelse0# 备用计算通过总价和面积反推单价ifunit_price_val0andtotal_price0andarea0:unit_price_valround(total_price*10000/area,0)第三章数据清洗技术3.1 数据清洗流程原始数据 → 缺失值处理 → 去重 → 异常值检测 → 格式转换 → 特征提取 → 清洗后数据3.2 核心清洗逻辑classDataCleaner:defclean(self):数据清洗主流程# 1. 加载原始数据dfself.load_raw_data()# 2. 去重dfdf.drop_duplicates(subset[title,district,total_price,area])# 3. 缺失值处理dfdf.dropna(subset[title,total_price,area])df[unit_price]df[unit_price].fillna(0)# 4. 异常值过滤dfdf[(df[total_price]0)(df[total_price]10000)]dfdf[(df[area]20)(df[area]500)]# 5. 计算缺失的单价mask(df[unit_price]0)(df[total_price]0)(df[area]0)df.loc[mask,unit_price]round(df.loc[mask,total_price]*10000/df.loc[mask,area],0)returndf第四章数据挖掘算法4.1 K-Means 聚类分析4.1.1 算法原理K-Means 是一种无监督学习算法用于将数据划分为 K 个簇。算法步骤随机选择 K 个初始质心计算每个样本到质心的距离分配到最近的簇更新每个簇的质心取平均值重复步骤 2-3直到质心稳定4.1.2 代码实现defclustering_analysis(self,n_clusters5):K-Means 聚类分析# 特征选择与标准化featuresself.df[[unit_price,area,total_price]].dropna()scalerStandardScaler()scaled_featuresscaler.fit_transform(features)# K-Means 聚类kmeansKMeans(n_clustersn_clusters,random_state42)labelskmeans.fit_predict(scaled_features)# 统计每个簇的特征cluster_stats[]foriinrange(n_clusters):cluster_datafeatures[labelsi]cluster_stats.append({cluster:i,count:len(cluster_data),avg_unit_price:round(cluster_data[unit_price].mean(),2),avg_area:round(cluster_data[area].mean(),2),avg_total_price:round(cluster_data[total_price].mean(),2)})returncluster_stats4.2 线性回归分析4.2.1 算法原理线性回归用于建立自变量与因变量之间的线性关系y β₀ β₁x₁ β₂x₂ ... βₙxₙ评估指标MSE均方误差衡量预测值与真实值的平均偏差R²决定系数衡量模型解释方差的能力0-14.2.2 代码实现defregression_analysis(self):线性回归分析# 特征与目标变量Xself.df[[area,unit_price]].dropna()yself.df.loc[X.index,total_price]# 数据分割X_train,X_test,y_train,y_testtrain_test_split(X,y,test_size0.2,random_state42)# 训练模型modelLinearRegression()model.fit(X_train,y_train)# 预测与评估y_predmodel.predict(X_test)msemean_squared_error(y_test,y_pred)r2r2_score(y_test,y_pred)return{coefficients:{area:model.coef_[0],unit_price:model.coef_[1]},r2:r2,mse:mse}第五章数据可视化技术5.1 后端 API 设计app.route(/api/data/district)defget_district_data():获取区县房价数据analyzerDataAnalyzer()analyzer.use_csvTrueresultanalyzer.district_analysis()returnjsonify({status:success,data:result})app.route(/api/data/clustering)defget_clustering():获取聚类分析数据analyzerDataAnalyzer()analyzer.use_csvTrueresultanalyzer.clustering_analysis()returnjsonify({status:success,data:result})5.2 前端 ECharts 集成// 区县房价对比柱状图functioninitDistrictChart(){fetch(/api/data/district).then(resres.json()).then(data{constdistrictsdata.data.map(itemitem.district);constpricesdata.data.map(itemitem.平均单价);constchartecharts.init(document.getElementById(districtChart));chart.setOption({title:{text:各区县二手房平均单价},xAxis:{type:category,data:districts},yAxis:{type:value,name:单价(元/㎡)},series:[{type:bar,data:prices,itemStyle:{color:newecharts.graphic.LinearGradient(0,0,0,1,[{offset:0,color:#83bff6},{offset:0.5,color:#188df0},{offset:1,color:#188df0}])}}]});});}第六章增量更新机制6.1 增量更新策略classIncrementalUpdater:defupdate(self):增量更新主流程# 1. 获取上次更新时间last_update_timeself.get_last_update_time()# 2. 爬取新数据只获取更新时间之后的new_dataself.crawl_new_data(sincelast_update_time)# 3. 去重与已有数据对比unique_dataself.remove_duplicates(new_data)# 4. 保存新数据self.save_new_data(unique_data)# 5. 更新时间戳self.update_last_update_time()return{updated_count:len(unique_data)}第七章性能优化技巧7.1 多线程爬取fromconcurrent.futuresimportThreadPoolExecutor,as_completeddefcrawl_district(self,district,url):爬取单个区县数据data_list[]forpageinrange(1,51):page_urlf{url}p{page}/htmlself.fetch_page(page_url)dataself.parse_anjuke(html,district)data_list.extend(data)returndata_listdefcrawl_with_threads(self):多线程爬取withThreadPoolExecutor(max_workers4)asexecutor:futures[]fordistrict,urlinself.district_urls.items():futureexecutor.submit(self.crawl_district,district,url)futures.append(future)forfutureinas_completed(futures):data_listfuture.result()self.storage.insert_many(data_list)7.2 请求缓存与重试retry(stopstop_after_attempt(3),waitwait_exponential(multiplier1,min2,max10))deffetch_page(self,url):带重试机制的页面请求responseself.session.get(url,headersself.headers,timeout30)response.raise_for_status()returnresponse.text第八章常见问题与解决方案8.1 反爬拦截问题问题原因解决方案验证码页面频繁请求被识别使用浏览器模式人工验证IP 封禁单 IP 请求过多使用代理 IP 池Cookie 失效会话过期定期更新 Cookie8.2 数据解析问题问题原因解决方案价格为 0选择器失效多重选择器策略页面结构变化网站更新动态选择器适配中文乱码编码不一致指定 UTF-8 编码8.3 性能问题问题原因解决方案爬取速度慢单线程串行多线程/异步爬取内存溢出数据量大分批处理、流式存储存储慢频繁 IO批量写入、缓存机制第九章扩展建议9.1 增加更多数据源贝壳网Ke.com58 同城房天下9.2 增强反爬能力代理 IP 池浏览器指纹随机化分布式爬取架构9.3 深入数据分析时间序列分析房价趋势地理空间分析热力图文本挖掘房源描述分析9.4 部署优化Docker 容器化云服务器部署定时任务自动执行附录技术栈版本说明技术版本用途Python3.13编程语言Flask2.2.5Web 框架Selenium4.18.1浏览器自动化BeautifulSoup4.12.2HTML 解析pandas2.1.4数据处理numpy1.26.3数值计算scikit-learn1.3.2机器学习ECharts5.4.3数据可视化MongoDB6.0数据库存储文档版本: v1.0创建时间: 2026-07-01适用场景: 学年设计、课程设计、毕业设计