1. 项目背景与核心价值
贵州作为典型的山地农业省份,农产品价格波动直接影响当地居民生活成本。传统菜价监测主要依靠人工采集,存在数据滞后、样本量有限等问题。本项目通过爬虫技术自动抓取贵州主要农贸市场菜价数据,结合Django框架构建可视化平台,实现了三个核心价值:
- 实时性突破:每日自动更新数据,相比传统周报模式效率提升7倍
- 可视化分析:通过折线图、热力图等直观展示价格趋势和区域差异
- 决策支持:为家庭采购、商户定价、政策制定提供数据依据
实操中发现:贵州部分农贸市场网站采用动态加载技术,需要特殊处理才能获取完整数据
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 技术架构设计
2.1 整体技术栈
mermaid复制graph TD
A[数据采集层] -->|Scrapy/Requests| B(数据处理层)
B -->|Pandas清洗| C[数据存储层]
C -->|MySQL| D[业务逻辑层]
D -->|Django REST| E[前端展示层]
E -->|ECharts| F[用户界面]
2.2 关键技术选型对比
| 技术选项 | 选用方案 | 替代方案 | 选择理由 |
|---|---|---|---|
| 爬虫框架 | Scrapy | Requests | 内置去重/异步处理 |
| 数据库 | MySQL 5.7 | MongoDB | 事务支持更好 |
| 可视化 | ECharts | Pygal | 动态交互更强 |
| 部署方式 | Docker | 裸机部署 | 环境隔离更彻底 |
3. 核心实现细节
3.1 爬虫系统实现
python复制class GuizhouPriceSpider(scrapy.Spider):
name = 'guizhou_price'
custom_settings = {
'DOWNLOAD_DELAY': 2,
'CONCURRENT_REQUESTS': 1 # 避免触发反爬
}
def parse(self, response):
# 处理动态加载的特殊逻辑
script_data = response.xpath('//script[contains(., "var __DATA__")]')
if script_data:
json_str = re.search(r'var __DATA__ = ({.*?});',
script_data.get()).group(1)
items = json.loads(json_str)['items']
for item in items:
yield {
'name': item['vegetable'],
'price': float(item['price']),
'market': item['market'],
'date': datetime.now().strftime('%Y-%m-%d')
}
避坑指南:贵州政务服务网等政府网站需要添加合法User-Agent,建议使用
Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36
3.2 数据存储设计
sql复制CREATE TABLE `vegetable_price` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '蔬菜名称',
`price` decimal(10,2) NOT NULL COMMENT '价格(元/斤)',
`market` varchar(100) COLLATE utf8mb4_unicode_ci NOT NULL,
`region` varchar(20) GENERATED ALWAYS AS
(CASE
WHEN market LIKE '%贵阳%' THEN '贵阳'
WHEN market LIKE '%遵义%' THEN '遵义'
ELSE '其他'
END) VIRTUAL,
`date` date NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_market_date` (`market`,`date`),
KEY `idx_name_date` (`name`,`date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
4. 可视化功能实现
4.1 价格趋势分析
javascript复制// Django模板中的ECharts配置
option = {
tooltip: {
trigger: 'axis',
formatter: function(params) {
return `${params[0].name}<br/>
均价: ${params[0].value[1]}元<br/>
最高: ${params[0].value[2]}元<br/>
最低: ${params[0].value[3]}元`;
}
},
xAxis: {type: 'category', data: dates},
yAxis: {type: 'value', name: '价格(元)'},
series: [{
type: 'line',
data: avgPrices,
smooth: true,
markLine: {
data: [{type: 'average', name: '平均值'}]
}
}]
};
4.2 区域价格对比
python复制# views.py 中的数据处理逻辑
def region_comparison(request):
queryset = VegetablePrice.objects.filter(
date__gte=timezone.now()-timedelta(days=30)
).values('region', 'name').annotate(
avg_price=Avg('price'),
max_price=Max('price'),
min_price=Min('price')
)
# 构建前端需要的嵌套数据结构
result = {}
for item in queryset:
if item['name'] not in result:
result[item['name']] = []
result[item['name']].append({
'region': item['region'],
'values': [item['avg_price'], item['max_price'], item['min_price']]
})
return JsonResponse(result)
5. 部署与优化
5.1 Docker部署方案
dockerfile复制# docker-compose.yml
version: '3'
services:
web:
build: .
ports:
- "8000:8000"
depends_on:
- redis
- mysql
environment:
- DJANGO_SETTINGS_MODULE=core.settings.prod
mysql:
image: mysql:5.7
volumes:
- db_data:/var/lib/mysql
environment:
- MYSQL_ROOT_PASSWORD=yourpassword
- MYSQL_DATABASE=vegetable_price
redis:
image: redis:alpine
volumes:
db_data:
5.2 性能优化措施
-
缓存策略:
- 使用Redis缓存热门查询(TTL设置1小时)
- Django的cache_page装饰器缓存视图
python复制@cache_page(60 * 15) def price_trend(request): # 视图逻辑 -
查询优化:
- 使用select_related减少数据库查询
- 添加数据库索引(如前述表结构设计)
-
爬虫优化:
- 使用Rotating Proxy处理反爬
- 实现增量爬取(只抓取新数据)
6. 常见问题解决方案
6.1 爬虫被屏蔽处理
- 症状:返回403状态码或验证码页面
- 解决方案:
- 设置随机User-Agent
- 使用selenium模拟人工操作
- 添加请求延迟(DOWNLOAD_DELAY)
6.2 数据不一致处理
python复制# 数据清洗管道
class DataCleaningPipeline:
def process_item(self, item, spider):
# 价格异常值过滤
if item['price'] > 100: # 假设100元是合理上限
raise DropItem(f"异常价格: {item}")
# 单位统一转换
if '元/公斤' in item['price']:
item['price'] = float(item['price'].replace('元/公斤','')) / 2
return item
6.3 可视化性能优化
- 数据采样:当数据量>1万条时,自动切换为按周平均值显示
- 前端懒加载:初始只加载最近30天数据,滚动时动态加载历史数据
- WebWorker:将复杂计算移入WebWorker线程
7. 项目扩展方向
-
价格预测模型:集成LSTM时间序列预测
python复制from keras.models import Sequential from keras.layers import LSTM, Dense model = Sequential() model.add(LSTM(50, input_shape=(30, 1))) # 30天历史数据 model.add(Dense(1)) model.compile(loss='mae', optimizer='adam') -
移动端适配:开发微信小程序版本
-
供应链分析:结合天气、运输成本等多维数据分析
开发建议:先实现核心爬虫和基础可视化,再逐步添加高级功能。我在实际开发中发现,过早引入复杂功能会导致项目难以维护。
