1. 项目概述与技术选型
这个基于Python和Django的租房数据分析系统,是我在指导计算机专业毕业设计时开发的一个典型案例。系统通过爬取58同城租房数据,实现了从数据采集、清洗到分析展示的全流程处理,特别适合作为大数据分析类毕业设计的参考项目。
技术栈选择上,我们采用了以下组合:
- 后端框架:Django 3.2(LTS版本)
- 数据库:MySQL 8.0
- 数据可视化:ECharts 5.3
- 爬虫工具:Requests + BeautifulSoup
- 前端技术:HTML5 + Bootstrap 5 + jQuery
选择Django而非Flask的主要考虑是其自带的管理后台和ORM系统,对于需要快速开发管理功能的项目特别友好。MySQL作为关系型数据库,在数据一致性和复杂查询方面表现优异,适合存储结构化的租房数据。
提示:实际开发中建议使用Django REST framework构建API,这样前后端可以完全分离,方便后续扩展移动端应用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 数据采集模块实现
2.1 爬虫设计要点
爬虫模块的核心代码位于spider/58_spider.py,主要处理以下几个关键问题:
- 反爬策略应对:
python复制headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Referer': 'https://bj.58.com/',
'Cookie': '你的cookie'
}
def get_page(url):
try:
time.sleep(random.uniform(1, 3)) # 随机延迟
response = requests.get(url, headers=headers, timeout=10)
if response.status_code == 200:
return response.text
except Exception as e:
print(f"请求失败: {str(e)}")
return None
- 数据解析关键点:
python复制def parse_detail(html):
soup = BeautifulSoup(html, 'lxml')
item = {}
# 提取标题
title = soup.select('.house-title h1')
item['title'] = title[0].get_text().strip() if title else ''
# 提取价格(需要处理单位转换)
price = soup.select('.price span.num')
item['price'] = float(price[0].get_text()) if price else 0.0
# 提取面积(需要正则提取数字)
area = soup.select('.house-basic-item span.strongbox')
if area:
area_num = re.search(r'(\d+)㎡', area[0].get_text())
item['area'] = float(area_num.group(1)) if area_num else 0.0
# 其他字段类似处理...
return item
2.2 数据清洗策略
采集到的原始数据需要经过严格清洗:
- 异常值处理:过滤掉价格为0或异常高的记录
- 单位统一:将"万元/月"统一转换为数字
- 地址标准化:提取省市区三级信息
- 去重处理:根据房源ID和标题进行去重
清洗后的数据通过Django ORM批量入库:
python复制from django.db import transaction
@transaction.atomic
def batch_save_houses(house_list):
objs = [House(
title=item['title'],
price=item['price'],
area=item['area'],
# 其他字段...
) for item in house_list]
House.objects.bulk_create(objs, batch_size=100)
3. 数据分析与可视化
3.1 核心分析维度
系统实现了6个关键分析维度:
- 区域价格分布(散点图)
- 房源数量TOP10区域(柱状图)
- 房型占比(饼图)
- 面积区间分布(折线图)
- 价格-面积相关性(散点图)
- 热门小区词云
3.2 ECharts集成技巧
后端数据处理视图示例:
python复制def price_analysis(request):
# 按区域分组计算平均价格
queryset = House.objects.values('district').annotate(
avg_price=Avg('price'),
count=Count('id')
).order_by('-avg_price')[:10]
data = {
'xAxis': [item['district'] for item in queryset],
'series': [round(item['avg_price'], 2) for item in queryset]
}
return JsonResponse(data)
前端ECharts配置关键点:
javascript复制// 在vue组件中
methods: {
initChart() {
const chart = echarts.init(this.$refs.chartDom);
axios.get('/api/price-analysis/').then(response => {
const option = {
tooltip: {...},
xAxis: {
type: 'category',
data: response.data.xAxis
},
yAxis: {type: 'value'},
series: [{
data: response.data.series,
type: 'bar',
itemStyle: {
color: new echarts.graphic.LinearGradient(...)
}
}]
};
chart.setOption(option);
});
}
}
4. 系统功能实现细节
4.1 条件查询功能
房源查询API实现了多条件筛选:
python复制class HouseFilter(FilterSet):
min_price = NumberFilter(field_name='price', lookup_expr='gte')
max_price = NumberFilter(field_name='price', lookup_expr='lte')
min_area = NumberFilter(field_name='area', lookup_expr='gte')
room_type = CharFilter(field_name='room_type', lookup_expr='icontains')
class Meta:
model = House
fields = ['district', 'min_price', 'max_price', 'min_area', 'room_type']
@api_view(['GET'])
def house_list(request):
queryset = House.objects.all()
filterset = HouseFilter(request.GET, queryset=queryset)
# 分页处理
paginator = Paginator(filterset.qs, 20)
page = paginator.get_page(request.GET.get('page'))
serializer = HouseSerializer(page, many=True)
return Response({
'data': serializer.data,
'count': paginator.count
})
4.2 评论系统设计
采用Django的GenericForeignKey实现多模型关联:
python复制class Comment(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
text = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['-created_at']
5. 部署与性能优化
5.1 生产环境部署
推荐使用Docker Compose部署:
yaml复制version: '3'
services:
db:
image: mysql:8.0
environment:
MYSQL_ROOT_PASSWORD: yourpassword
MYSQL_DATABASE: house_db
volumes:
- mysql_data:/var/lib/mysql
web:
build: .
command: gunicorn housing.wsgi:application --bind 0.0.0.0:8000
volumes:
- .:/code
ports:
- "8000:8000"
depends_on:
- db
volumes:
mysql_data:
5.2 性能优化建议
-
数据库优化:
- 为常用查询字段添加索引
- 使用
select_related和prefetch_related减少查询次数
-
缓存策略:
python复制from django.core.cache import cache
def get_hot_houses():
key = 'hot_houses'
data = cache.get(key)
if not data:
data = House.objects.order_by('-view_count')[:10]
cache.set(key, data, timeout=3600) # 缓存1小时
return data
- 异步任务:
使用Celery处理耗时操作:
python复制@app.task
def async_update_house_data():
spider = HousingSpider()
data = spider.crawl()
batch_save_houses(data)
6. 常见问题与解决方案
6.1 爬虫被屏蔽问题
解决方案:
- 使用代理IP池
- 随机User-Agent
- 设置合理的请求间隔
- 模拟浏览器行为(可考虑使用selenium)
6.2 数据不一致问题
处理方案:
python复制try:
with transaction.atomic():
# 你的数据库操作
house = House.objects.select_for_update().get(pk=1)
house.view_count += 1
house.save()
except Exception as e:
logger.error(f"数据库操作失败: {str(e)}")
6.3 可视化性能问题
优化建议:
- 对大数据集进行采样展示
- 使用WebWorker处理数据
- 实现数据的分页加载
7. 项目扩展方向
-
机器学习集成:
- 价格预测模型
- 房源推荐系统
-
移动端适配:
- 开发微信小程序版本
- 使用React Native开发跨平台APP
-
实时数据更新:
- 使用WebSocket实现数据实时推送
- 设置定时爬虫任务
这个项目完整实现了从数据采集到分析展示的全流程,涉及的技术栈非常全面,特别适合作为计算机专业的毕业设计选题。在实际开发过程中,我建议同学们重点关注数据清洗和可视化展示这两个核心模块,它们最能体现数据分析项目的价值。
