news 2026/9/8 10:23:41

Python机器学习二手房价格预测系统全流程实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python机器学习二手房价格预测系统全流程实战

简介:一份基于Python机器学习的二手房交易预测及可视化系统毕业设计项目,面向计算机相关专业准备毕设的学生和需要项目实战练习的初学者,可实现二手房价格预测与数据可视化,代码完整可直接运行,配套答辩PPT,也可用于课程设计或期末大作业。资源共157个文件,压缩包约40MB,包含18个Python源码文件、18个CSV数据集、15个HTML页面和11个JavaScript脚本组成的可视化界面,另有65张截图和PPT演示文稿,便于按模块学习与答辩展示。项目完整覆盖二手房数据清洗、特征处理、模型训练、房价预测和可视化分析,数据集提供原始及多种清洗版本,方便复现实验细节。通过该系统的完整代码和说明,读者可掌握机器学习项目从数据到展示的整套流程,并借助高分毕设的答辩PPT梳理设计思路。这套项目已有166人学习,整体属于经导师认可、评审99分的高分毕业设计。 去年做毕设选这个题目,说实话不是因为它多热门,而是被链家上那串数字勾起了好奇心——同一栋楼,楼层差两层,总价能差二十万;同一个区域,朝向差一个窗户,报价能差十几万。谁在定价?规则藏在数据里吗?带着这个疑问,我把毕设题目定成了“基于Python机器学习的二手房交易预测及可视化系统”。

这套系统做的事情很直接:用Python采集或获取一批真实的二手房挂牌数据,完成清洗和特征工程,训练若干机器学习回归模型来预测房屋总价,然后通过Web页面把数据分布、特征关系、模型效果和在线预测完整展示出来。整个过程覆盖了一个机器学习项目从数据到落地的全链路,很适合计算机、数据科学、信息管理相关专业的毕业生作为毕设主体,也适合想补一个完整项目经历、或者想找一个“数据→建模→部署→展示”闭环案例练手的学习者。下面的内容就是我完整跑通之后的实现思路、选型理由、踩坑记录和答辩应对方案,照着复现基本能少走一半弯路。

1. 选题与系统整体设计:这个题目为什么“好做又好看”

1.1 选题逻辑:难度适中,故事完整,答辩不慌

毕设选题最怕两种:一种是太简单,一个月写完但没含金量,答辩老师问两句就露馅;另一种是太难,做一半卡住,最后只能硬着头皮糊弄。二手房交易预测这个方向,刚好卡在两者中间一个非常舒服的位置。

数据来源现成且真实。链家、贝壳这类平台的挂牌数据是公开的,字段齐全,有总价、单价、面积、户型、楼层、朝向、装修、区域、建筑年代等等。这些字段既有数值型又有类别型,天然适合做机器学习回归任务。哪怕不写爬虫,直接找一份整理好的公开数据集,也能撑起整个数据预处理和建模环节。

模型对比空间大。房价预测是典型的回归问题,线性回归能跑,决策树能跑,随机森林、XGBoost、LightGBM都能跑。模型之间可以做效果对比,出对比表格和图表,这正好是答辩老师最爱看的东西——有实验、有数据、有结论。

可扩展性强,能讲业务故事。系统不只是“训练一个模型交差”,还能落地成一个带页面的小工具:用户输入面积、户型、楼层、区域,系统返回一个预估总价。这件小事放到答辩现场演示,说服力比十页PPT都强。

1.2 系统架构与模块划分

我的实现把整个系统拆成了四个模块,按数据流动的顺序依次衔接:数据采集与预处理、模型训练与评估、结果持久化、可视化与在线预测展示。这样拆的好处是每块都能单独调试,出了问题不需要从头排查。

技术栈方面,我当时选了这样一套组合:

模块选型说明
数据获取requests + BeautifulSoup / 公开数据集写爬虫练手可以,赶进度建议直接用整理好的CSV
数据处理pandas + NumPy + scikit-learn清洗、特征工程、编码、划分数据全靠它
模型训练scikit-learn + LightGBM先跑基线模型,再上集成模型,对比出结论
模型持久化joblib保存训练好的模型,供Web端加载
可视化展示Flask + ECharts / Pyecharts图表交互性强,页面效果比matplotlib好一个档次
在线预测Flask + 已训练模型表单输入 → 特征转换 → 模型预测 → 返回价格

整体数据流是一条直线:原始数据 → 清洗 → 特征工程 → 训练集/测试集划分 → 模型训练 → 评估对比 → 保存最优模型 → Flask加载模型 → Web页面展示图表和在线预测功能。顺着这条线往下做,逻辑上不会有大的偏离。

提示:环境版本尽量固定。我当时用Python 3.9 + scikit-learn 1.2.x + pandas 1.5.x,整套流程非常稳。版本太新某些API有变动,太老又跟课程内容脱节。新建一个虚拟环境,一次性装齐依赖,避免后面边写边补包。

装依赖就一条命令:

pip install pandas numpy scikit-learn lightgbm flask pyecharts joblib

下载慢的话加清华镜像源:-i https://pypi.tuna.tsinghua.edu.cn/simple。这个细节很多人忽略,但能帮你省下不少时间。

2. 数据获取与预处理:模型的天花板在数据里

2.1 数据源与获取思路:能动手爬,但别陷进去

数据获取有三条路,效率完全不同。

第一条路,直接用公开数据集。Kaggle上有波士顿房价、加州房价等经典数据集,阿里天池、和鲸社区也有国内二手房数据。这些数据已经过初步整理,缺失值和异常值不多,适合把精力聚焦在建模和系统上。如果毕设时间紧张,这是最推荐的选择。

第二条路,自己写爬虫抓链家或贝壳。这个方案最有“项目感”,展示的时候可以说“数据是我自己采的”,但同时也是最容易翻车的环节。一是平台有反爬机制,访问频率太高会被封IP;二是页面结构经常调整,今天能跑的选择器明天可能就失效;三是数据量太大反而增加清洗负担。

我当时写了一个简单的爬虫思路,代码不复杂,关键是控制频率、带好Headers:

import requests from bs4 import BeautifulSoup def parse_lianjia_url(url): headers = { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" } resp = requests.get(url, headers=headers, timeout=10) soup = BeautifulSoup(resp.text, "html.parser") # 提取小区名称、户型、面积、朝向、装修、总价、单价等字段 # 遍历列表页,翻页时在URL上替换页码参数 # 每次请求之间加随机延时,比如 time.sleep(random.uniform(1, 3)) return items if __name__ == "__main__": # 指定城区的二手房列表页 # 循环抓取并保存到CSV pass

第三条路,找导师要数据。部分学校有合作企业的脱敏数据,这种数据最干净、最有说服力,但可遇不可求,有的话直接选它,没有也别强求。

我的建议很直接:爬虫可以作为系统的“数据获取模块”写进论文里展示思路,但实际建模用的数据,优先用整理好的公开数据集。把时间留给后面更重要的事情,而不是在网页解析上死磕。

2.2 清洗与特征工程实操:细节决定模型上限

数据拿到手之后,第一件事不是建模,而是看看数据长什么样。我当时用df.info()df.describe()扫了一遍,发现问题比想象中多。整理了一份处理清单,基本覆盖了二手房数据的常见坑:

字段处理方式原因
面积过滤掉小于20㎡或大于500㎡的记录极小或极大值多为录入错误或非住宅类房产
总价/单价用箱线图识别极端值,按需剔除个别挂牌价严重偏离市场,影响模型训练
朝向把“南、北、东南、西南”保留,其余归为“其他”类别太散会导致独热编码维度爆炸
楼层从字符串中提取“低/中/高/顶层/底层”楼层高低对房价影响显著,属于核心特征
装修映射为“毛坯/简装/精装/豪装”这四个档位业务含义清晰,直接做序数编码
建筑年代计算房龄(当前年份-建筑年代),缺失用同小区众数填充房龄比绝对年份更有建模意义
区域/商圈区域做目标编码或标签编码位置是房价最核心因素,但独热编码维度太高

关键一步是处理价格长尾分布。房价数据通常右偏严重,几百万的房子占多数,几千万的豪宅拉高了均值,直接丢给线性模型很容易被极端值带偏。我当时对总价做了对数变换:

import pandas as pd import numpy as np df = pd.read_csv("house_data.csv", encoding="utf-8") df = df[df["面积"] >= 20].copy() df["房龄"] = 2025 - df["建筑年代"] df["朝向_group"] = df["朝向"].apply( lambda x: x if x in ["南", "北", "东南", "西南"] else "其他" ) df["总价_log"] = np.log1p(df["总价"]) df.to_csv("house_clean.csv", index=False, encoding="utf-8")

np.log1p相当于log(1+x),好处是即使价格为0也不会出现负无穷,还原的时候用np.expm1就行。

区域这种高基数类别特征,直接独热编码会让特征矩阵变得非常稀疏。我当时先把区域做了标签编码,后来又试了目标编码——用该区域的历史平均单价替代类别值。效果提升很明显,但这里有一个必须注意的禁忌:目标编码的均值只能在训练集上计算,再映射到测试集,否则会造成数据泄漏,测试指标会虚高得离谱,答辩时被老师点出来就很难收场。

注意:特征工程的顺序千万记牢——先切分训练集和测试集,再在训练集上做各种编码和填充。任何用到全局统计量(均值、中位数、频次)的操作,都不能把测试集的信息混进来。

3. 机器学习模型选型与调优:从基线到最优的完整路径

3.1 先跑基线模型,再上集成模型

很多初学者上来就调XGBoost、LightGBM,结果超参调了一整天,效果也没比线性回归好多少。正确做法是先跑几个基线模型,建立一个参照系,再逐步上更复杂的模型。

我当时按“线性回归 → 随机森林 → GBDT → LightGBM”的顺序一步步来。每一步都计算MAE、RMSE、R²三个指标,记录到表格里。这样产生的模型对比表非常扎实,答辩时直接呈现“我的模型是逐步优化出来的”,而不是“我随便选了一个模型”。

from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score X = df_encoded.drop(columns=["总价", "总价_log"]) y = df_encoded["总价_log"] X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, random_state=42 ) models = { "LinearRegression": LinearRegression(), "RandomForest": RandomForestRegressor(n_estimators=200, max_depth=10, random_state=42), "GBDT": GradientBoostingRegressor(n_estimators=200, learning_rate=0.05, random_state=42), } for name, model in models.items(): model.fit(X_train, y_train) pred = model.predict(X_test) mae = mean_absolute_error(y_test, pred) r2 = r2_score(y_test, pred) print(f"{name}: MAE={mae:.4f}, R2={r2:.4f}")

注意我在划分数据之前,把总价总价_log都从特征里剔掉了,这是防止数据泄漏的基本操作。训练目标是总价_log,评估出来的MAE是在对数空间里的,想转回真实的万元单位,要用np.expm1(pred)还原后再算一次误差。

3.2 LightGBM与XGBoost的参数实践

基线模型跑完,正常情况下LightGBM或XGBoost应该能拿到最优结果。我当时选的是LightGBM,主要原因是对特征工程的要求相对宽松、训练速度快,而且原生接口支持early stopping,省去手动调参的大量时间。

LightGBM的初始参数和调参思路,我整理成了一张表:

参数建议初值说明
learning_rate0.05学习率,越小精度上限越高,但训练更慢
n_estimators / num_boost_round1000配合early stopping,实际训练轮数由验证集决定
num_leaves31叶子节点数,越大越容易过拟合
max_depth-1不限制深度时靠num_leaves控制复杂度
feature_fraction0.8每轮迭代随机使用80%特征,防过拟合
bagging_fraction0.8每轮迭代随机采样80%样本,防过拟合
lambda_l20.1L2正则,对高维稀疏特征有稳定作用

核心训练代码:

import lightgbm as lgb params = { "objective": "regression", "metric": "rmse", "learning_rate": 0.05, "num_leaves": 31, "max_depth": -1, "feature_fraction": 0.8, "bagging_fraction": 0.8, "lambda_l2": 0.1, "verbose": -1, "seed": 42 } train_data = lgb.Dataset(X_train, label=y_train) valid_data = lgb.Dataset(X_test, label=y_test, reference=train_data) model = lgb.train( params, train_data, num_boost_round=1000, valid_sets=[valid_data], callbacks=[lgb.early_stopping(50), lgb.log_evaluation(100)] )

early_stopping(50)的意思是连续50轮验证集RMSE没有下降就停止训练,返回的最优模型会被保留。这一招对控制过拟合特别有效,我在多个项目里实测下来,比手动指定轮数稳定得多。

3.3 模型评估与特征重要性解读

模型跑完,要输出一份类似这样的对比表,这是论文和答辩的核心素材:

模型MAE(万元)RMSE(万元)整体表现
线性回归23.535.10.71解释性强,拟合不足
随机森林18.228.70.81稳健,无明显过拟合
GBDT16.425.90.84比随机森林更好
LightGBM15.824.30.86综合最优,训练最快

看到这个结果先别急着高兴,一定要看看特征重要性,这是答辩时的加分项。LightGBM原生接口能直接输出:

importance = pd.Series( model.feature_importance("gain"), index=X_train.columns ).sort_values(ascending=False) print(importance.head(10))

我当时的Top特征基本是区域编码、面积、房龄、楼层、“是否南向”。这些特征放到业务里完全说得通:位置决定基础价位,面积和房龄决定单价,楼层和朝向决定同一小区内部的差价。能用自己的数据解释业务规律,这个项目就不再是“调包”,而是真正有业务理解力的作品。

提示:R²不是唯一标准。房价这种波动大的场景,MAE的业务含义更直观——预测误差15.8万,意味着模型给出的估价与真实挂牌价平均只差15.8万。答辩时先用MAE解释模型价值,再用R²说明拟合优度。

4. 可视化子系统:让数据和模型“讲人话”

4.1 技术选型:为什么选Flask + ECharts

可视化是这套系统的门面,也是答辩时最容易出彩的部分。可选的方案不少,我对比过三条路:

方案优点缺点
matplotlib / seaborn 静态图简单、经典、论文插图顺手交互性差,展示效果平淡
Pyecharts 生成HTML中文友好,生成图表方便,适合快速做出Demo版本差异大,部分组件定制受限
Flask + ECharts交互强、可自定义、能结合表单预测前后端联调需要自己写代码

最终选了Flask + ECharts。原因很简单:Flask作为轻量级Web框架,几百行代码就能搭起一个完整的展示站点;ECharts是纯前端图表库,散点图、热力图、地图、漏斗图全都开箱即用,交互缩放、悬停提示都是现成的。前后端通过JSON交互,逻辑清晰,答辩时现场演示一套流程非常流畅。

4.2 核心图表与在线预测页面设计

系统页面我拆成了两部分:数据展示页面和在线预测页面。

数据展示页面放了6个核心图表,都是答辩时必须能讲清楚的:

  • 总价分布直方图/KDE曲线,展示价格呈长尾分布,这也是做对数变换的原因
  • 面积-总价散点图,按区域着色,可以看到不同区域的价格带差异
  • 特征相关性热力图,展示总价与面积、房龄等特征的相关关系
  • 各区域平均单价柱状图,支持按商圈下钻,直观对比区域价格
  • 真实值与预测值对比散点图,越接近对角线说明预测越准
  • 特征重要性条形图,展示模型到底“看中”哪些因素

在线预测页面更关键。用户填写面积、户型、楼层、朝向、装修、区域等字段,后端把输入转成和训练时完全一致的特征格式,调用已保存的LightGBM模型预测总价,返回结果。核心代码:

from flask import Flask, request, jsonify import joblib app = Flask(__name__) model = joblib.load("lgb_model.pkl") @app.route("/predict", methods=["POST"]) def predict(): data = request.get_json() # 把用户输入转换为训练时完全一致的特征格式 features = transform_input(data) pred_log = model.predict([features])[0] pred_price = round(float(np.expm1(pred_log)), 2) return jsonify({"price": pred_price}) if __name__ == "__main__": app.run(debug=True, port=5000)

其中transform_input是整个预测接口的灵魂——它必须把用户在网页上填的“原始值”转换成训练时的“模型输入”,包括相同的编码映射、相同的填充逻辑、相同的顺序。这段函数直接复用训练脚本里的特征处理函数,不要重写一遍,否则很容易出现线上预测结果离谱的问题。

模型保存我用的是joblib:

import joblib joblib.dump(model, "lgb_model.pkl")

相比pickle,joblib对大数组和模型对象的序列化更高效,加载速度也更快。

5. 答辩PPT设计与项目表达:高分的关键在逻辑

5.1 PPT结构:像讲故事一样讲项目

做完系统只是成功了一半,答辩PPT决定老师怎么评价你的工作。一个高分毕设PPT,核心不是堆砌代码和技术名词,而是把“为什么做、做了什么、怎么做、结果如何”讲清楚。

我的PPT结构是8个部分:

  1. 选题背景与研究意义(用真实数据引出房价预测的实际价值)
  2. 国内外研究现状(简单回顾,引用3到5篇文献,别铺开)
  3. 系统总体设计(架构图 + 技术栈说明)
  4. 数据获取与处理(展示原始数据长什么样,清洗前后对比)
  5. 模型构建与实验对比(核心页,放模型对比表和特征重要性图)
  6. 可视化系统展示(截图 + 现场演示或录屏)
  7. 总结与展望(突出可扩展方向:接入实时房价数据、推荐引擎等)
  8. 致谢

第5页模型对比那一页,至少留两分钟讲:从基线到LightGBM每一步的优化点在哪里,最后一页放“模型对比表”,直接对比四个模型的MAE、RMSE、R²。这一页就是你的核心论据。演示环节建议提前录制一份完整录屏备份,防止现场网络出问题或端口被占用。

注意:PPT每页的讲解时间控制在10到15秒,重点页可以到30秒以上。答辩规定时间通常8到10分钟,讲完记得留出时间给老师提问。

5.2 老师最爱追问的5个问题

答辩前我把老师可能问的高频问题整理成一个速查表,尤其针对“创新点”和“模型选择”这两个方向做了准备:

高频追问回答思路
为什么选这几个特征?从业务相关性讲:面积、区域、房龄是房价的核心影响因素;再补充数据上的证据,如相关性热力图和特征重要性
为什么不用神经网络?样本量有限,树模型可解释性强,模型对比实验更符合毕设工作量;如果换更大数据集可以尝试DNN
如何避免数据泄漏?所有编码和填充操作都只在训练集上拟合后再映射到测试集,线上预测复用同一个转换函数
模型泛化能力如何?交叉验证 + 独立区域测试集验证,展示模型在未见过的数据上依然稳定
创新点是什么?不追求算法创新,强调业务闭环:从数据采集、特征工程到模型部署可视化一个完整链路

提前找人模拟答辩,把自己当听众,用最简洁的语言把项目讲一遍,直到不卡壳为止。这个环节比多跑十个模型都有用。

6. 常见问题与调试排查:这些坑我替你踩过了

6.1 数据与模型阶段的经典问题

整套流程走下来,我把最容易踩的坑分类整理了一下,都附上了解决方案和问题根源。先看数据和模型部分:

问题原因解决方案
pandas读取CSV中文乱码文件编码是GBK,读取时默认UTF-8pd.read_csv(path, encoding="gbk"),或者先转成UTF-8再读
OneHotEncoder报错新版sklearn默认返回稀疏矩阵直接塞进DataFrame会报错指定sparse_output=False,旧版本参数名是sparse=False
joblib加载模型报错训练和部署环境的sklearn版本不一致保持同一环境运行,写清楚requirements.txt
预测结果出现负数对数空间还原后极小值导致np.clip(pred, 0, None)限制下界
LightGBM安装失败网络源不稳定或平台不兼容用清华镜像源,或conda install -c conda-forge lightgbm

另外提一个容易被忽视的问题:Windows下用matplotlib画图时中文显示成方块,原因是没有设置中文字体。需要手动指定:

plt.rcParams["font.sans-serif"] = ["SimHei"] # Windows自带黑体 plt.rcParams["axes.unicode_minus"] = False

6.2 可视化与前后端联调的常见问题

Web端的排查思路和模型端完全不同,核心是搞清楚问题出在浏览器还是服务器:

问题原因解决方案
Flask端口被占用默认5000被上次进程占用换端口:app.run(debug=True, port=5001),或先杀掉占用进程
ECharts图表加载不出来CDN文件路径失效或网络不通把echarts.min.js下载到本地static目录,用相对路径引用
表单提交后返回500后端特征处理函数抛异常开启debug模式,看浏览器的Network面板和Flask控制台栈信息
刷新后页面数据丢失图表数据存在内存变量里用sessionStorage临时存储,或者直接重新请求后端接口拉数据

排查的顺序也有讲究:先看Flask控制台有没有报错,再看浏览器Network面板的请求状态码,最后看Console的JS错误。大多数问题三步之内就能定位。

还有一种情况我遇到过一次,前端传过来的字段名和后端函数对不上,导致模型预测结果完全不对。后来我在transform_input函数内部加了一行打印,把转换后的特征打印出来,和训练时的特征做对比,问题一眼就暴露了。这种“先打印再调试”的习惯,在前后端联调阶段特别管用。

提示:演示环境记得把Flask的debug关掉,否则出错时弹出的调试器页面会暴露源码路径,而且不安全。

我个人在实际操作中的最大体会是:这个题目的价值不是算法有多新,而是它逼着你走完一个机器学习项目的完整生命周期——从拿数据、清洗、建模到部署、展示、答辩。房价预测很难做到绝对准确,影响一个房子成交价的因素,远多于我们数据里能拿到的字段,比如学区变动、业主急售、成交周期这些信息根本不在挂牌数据里。但“用数据逼近规律”这个过程本身就很有说服力,也是面试官和答辩老师真正看重的能力。如果你也正在做这个题目,我最后多说一句:不要在爬虫和花哨组件上花超过三分之一的时间,把精力留给特征工程和模型对比,那才是整个系统真正的核心。

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

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

PyCharm中部署pytest运行自动化测试:配置、用例与实战

很多刚接触自动化测试的朋友,都喜欢直接打开PyCharm写代码,写完一个 if __name__ "__main__" 就跑起来看结果。但等到用例数量一多、项目一复杂,这种原始方式就会迅速翻车。这时候pytest几乎就是Python测试领域的默认答案&#…

作者头像 李华
网站建设 2026/9/8 10:18:01

英伟达与OpenAI合作建设全球最大AI数据中心的技术解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 10:17:37

聚焦数据安全治理:从全景图看2026年网络安全行业趋势与能力需求

1. 这份全景图的分量:深圳网安协会榜单背后的筛选逻辑 这两年国内网络安全领域的榜单、全景图、产业报告层出不穷,但含金量差异极大。有些是商业机构为了卖报告搞的排名,交钱就能上;有些是媒体为了凑内容做的盘点,收录…

作者头像 李华
网站建设 2026/9/8 10:17:21

维度灾难详解:高维数据为何让KNN等算法失效及应对策略

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/8 10:16:10

数据集成实战指南:从多源接入到共享服务封装

1. 为什么“数据共享”卡在了数据集成这一环 先聊个实际的场景。你所在的企业或者机构,大概率已经经历过这么一遭:各个业务部门各自为政建了一堆系统,ERP一套库,CRM一套库,还有一堆历史遗留的Excel、CSV、老旧数据库&a…

作者头像 李华
网站建设 2026/9/8 10:15:27

三模机械键盘科普:有线、2.4G与蓝牙如何选与用

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华