1. 项目概述:基于Hadoop生态链的商品比价系统
这个项目是我去年为某电商代运营公司开发的分布式比价系统,核心目标是通过爬虫抓取全网商品数据,利用Hadoop生态进行存储分析,最终实现可视化比价。系统上线后帮助客户节省了15%的采购成本,我来完整复盘下技术实现细节。
整套系统采用Lambda架构设计,兼顾实时与离线处理:
- 实时层:Python爬虫集群 + Spark Streaming
- 批处理层:HDFS + Hive + Spark SQL
- 服务层:Flask API + ECharts可视化
关键设计原则:爬虫模块要遵守robots协议,设置合理的请求间隔;Hadoop集群采用伪分布式部署降低硬件成本;可视化侧重价格波动趋势和商家分布热力图。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 爬虫系统设计与反反爬实践
2.1 分布式爬虫架构
采用Scrapy-Redis搭建分布式爬虫集群,关键配置包括:
python复制# settings.py 核心配置
CONCURRENT_REQUESTS = 16
DOWNLOAD_DELAY = 3 # 遵守目标站点要求
REDIS_URL = 'redis://cluster-node:6379/0'
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
针对商品页面的特殊处理:
- 使用Selenium应对动态渲染页面
- 自定义中间件处理验证码
- XPath与CSS选择器混合定位价格元素
2.2 突破反爬的三重防护
实测有效的反反爬方案:
- 请求头伪装:动态轮换User-Agent池
python复制headers = {
'User-Agent': random.choice(USER_AGENTS),
'Referer': generate_fake_referer(),
'Accept-Encoding': 'gzip, deflate'
}
- IP代理策略:
- 免费代理:每小时更换(适合测试)
- 付费代理:Luminati按量计费
- 自建代理:AWS Lightsail多地域部署
- 行为模拟:
- 随机滚动页面
- 模拟鼠标移动轨迹
- 请求间随机延时(2-5秒)
血泪教训:某次因未设置延迟导致IP被封,后来增加了自动熔断机制——当连续5次请求失败时暂停1小时。
3. Hadoop数据湖搭建
3.1 伪分布式环境部署
使用Docker快速搭建Hadoop 3.x单节点集群:
bash复制# 拉取镜像
docker pull bde2020/hadoop-base:2.0.0-hadoop3.2.1-java8
# 启动容器
docker run -it --name hadoop-cluster \
-p 50070:50070 -p 8088:8088 \
-v /data/hdfs:/data \
bde2020/hadoop-base:2.0.0-hadoop3.2.1-java8
关键配置文件调整:
- core-site.xml:设置默认FS路径
- hdfs-site.xml:配置副本数为1(伪分布式)
- yarn-site.xml:启用内存限制
3.2 Hive元数据管理
使用MySQL存储Hive元数据,优化技巧:
sql复制-- 建表示例(分区表按日期存储)
CREATE EXTERNAL TABLE product_prices (
sku STRING,
price FLOAT,
platform STRING,
crawl_time TIMESTAMP
) PARTITIONED BY (dt STRING)
STORED AS PARQUET
LOCATION '/data/price';
每日自动添加分区:
bash复制#!/bin/bash
dt=$(date +%Y%m%d)
hive -e "ALTER TABLE product_prices ADD PARTITION (dt='$dt')"
4. Spark数据处理流水线
4.1 价格数据ETL流程
scala复制val rawDF = spark.read.parquet("hdfs:///data/price/dt=202305*")
// 数据清洗
val cleanedDF = rawDF
.filter($"price" > 0) // 过滤无效价格
.dropDuplicates("sku", "platform") // 去重
// 计算每日最低价
val minPriceDF = cleanedDF
.groupBy($"sku", date_format($"crawl_time", "yyyy-MM-dd").alias("day"))
.agg(min("price").alias("min_price"))
// 存储到Hive
minPriceDF.write.mode("overwrite").saveAsTable("price_analysis")
4.2 实时价格监控
Spark Streaming处理Kafka数据:
scala复制val kafkaParams = Map(
"bootstrap.servers" -> "kafka:9092",
"group.id" -> "price-monitor"
)
val stream = KafkaUtils
.createDirectStream[String, String](
ssc,
LocationStrategies.PreferConsistent,
ConsumerStrategies.Subscribe[String, String](Set("price-updates"), kafkaParams)
)
// 实时计算价格波动
stream.map(record => parsePrice(record.value))
.window(Minutes(30), Minutes(5))
.foreachRDD { rdd =>
rdd.filter(_.changePercent > 0.1)
.foreach(sendAlert)
}
5. 可视化系统实现
5.1 Flask API设计
python复制# app.py 核心路由
@app.route('/api/price_trend')
def price_trend():
sku = request.args.get('sku')
days = int(request.args.get('days', 7))
query = f"""
SELECT day, min_price
FROM price_analysis
WHERE sku='{sku}'
ORDER BY day DESC
LIMIT {days}
"""
result = spark.sql(query).toPandas()
return jsonify({
'dates': result['day'].tolist(),
'prices': result['min_price'].tolist()
})
5.2 ECharts动态图表
前端关键配置:
javascript复制option = {
tooltip: {
trigger: 'axis',
formatter: params => {
return `日期:${params[0].axisValue}<br/>
最低价:${params[0].data}元<br/>
平台数:${params[1].data}家`
}
},
xAxis: { data: dates },
yAxis: [{ type: 'value' }],
series: [
{
name: '最低价格',
type: 'line',
smooth: true,
data: prices
}
]
}
6. 性能优化实战经验
6.1 Hive表优化技巧
- 分区裁剪:查询时必须指定分区字段
sql复制-- 低效
SELECT * FROM product_prices WHERE sku='ABC123'
-- 高效
SELECT * FROM product_prices
WHERE sku='ABC123' AND dt='20230501'
- ORC格式+Snappy压缩:
sql复制CREATE TABLE optimized_prices (
...
) STORED AS ORC
TBLPROPERTIES ("orc.compress"="SNAPPY")
6.2 Spark调参指南
关键配置项(提交任务时指定):
bash复制spark-submit \
--executor-memory 4G \
--num-executors 8 \
--conf spark.sql.shuffle.partitions=200 \
--conf spark.default.parallelism=200 \
your_app.py
实测数据:调整shuffle partitions从默认200到500后,ETL作业时间从42分钟降至28分钟。
7. 踩坑与解决方案
7.1 时区混乱问题
现象:Hive显示时间比实际晚8小时
根本原因:Hive使用UTC时区而服务器是CST
解决方案:
sql复制-- 在Hive会话开始时设置
SET hive.session.timezone=Asia/Shanghai;
7.2 小文件问题
现象:HDFS存在数千个小文件导致NameNode压力大
解决方案:
- Spark输出时调整分区数
scala复制df.repartition(10).write.parquet(...)
- 定期执行Hive合并
sql复制ALTER TABLE product_prices
CONCATENATE;
8. 扩展应用场景
这套架构稍作改造即可用于:
- 竞品监控系统:追踪对手价格策略
- 历史价格查询:支持"半年内最低价"等功能
- 库存预警系统:结合库存数据预测补货时机
我最近正在尝试加入机器学习模块,用LSTM预测未来价格走势。初期测试显示,对促销商品的预测准确率能达到75%左右。
