从事日本电商数据聚合工作时,最大的难点在于要同时应对雅虎拍卖、煤炉(Mercari)、乐天和亚马逊日本站等截然不同的平台。以往使用单机爬虫,经常出现运行中崩溃的情况——单点故障、带宽利用率不足、数据存储混乱,这三大痛点令人困扰。
本文分享一套基于Scrapy + Redis的分布式爬虫方案,专门解决多平台数据稳定聚合的难题。接下来先介绍整体架构,再详解实现细节。
一、业务需求与技术选型
要高效聚合日本多个电商平台的商品数据,单机爬虫显然难以胜任。核心痛点主要体现在三个方面:
- 单点故障风险:一旦机器宕机,所有采集任务立即中断。
- 带宽资源浪费:单台机器的带宽有限,抓取速度难以提升。
- 数据存储瓶颈:不同平台的数据格式差异大,存储过程容易遇到各种问题。
因此选择了Scrapy + Redis这套成熟的技术组合,构建分布式爬虫系统。其中Redis负责管理任务队列和去重,各个Worker节点独立执行任务,最终数据统一入库。
二、系统架构设计
┌─────────────────────────────────────────────────────────────┐
│ 分布式爬虫系统架构 │
├─────────────────────────────────────────────────────────────┤
│ Master节点 │
│ ├── Redis (任务队列 + 去重) │
│ ├── Scheduler (URL调度) │
│ └── Monitor (节点监控) │
├─────────────────────────────────────────────────────────────┤
│ Worker节点1 Worker节点2 Worker节点N │
│ ├── Scrapy Engine ├── Scrapy Engine ├── Scrapy Engine │
│ ├── Downloader ├── Downloader ├── Downloader │
│ └── Pipeline └── Pipeline └── Pipeline │
├─────────────────────────────────────────────────────────────┤
│ 数据存储层 │
│ ├── MongoDB (商品数据) │
│ ├── Elasticsearch (搜索索引) │
│ └── MySQL (元数据) │
└─────────────────────────────────────────────────────────────┘
三、核心实现
3.1 Redis任务队列配置
首先来看Redis的配置,这是分布式调度的核心基础。
# 配置文件 settings.py
REDIS_HOST = 'localhost'
REDIS_PORT = 6379
REDIS_DB = 0
# 设置Redis为调度器队列
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER_PERSIST = True
# 并发控制参数
CONCURRENT_REQUESTS = 32
DOWNLOAD_DELAY = 0.5

3.2 多平台Spider实现
接下来是爬虫的核心部分,单个Spider同时处理雅虎拍卖、煤炉、乐天等多个平台。关键在于使用 RedisSpider 从Redis队列中拉取任务,然后根据平台参数选择对应的解析逻辑。
import scrapy
from scrapy_redis.spiders import RedisSpider
class MultiPlatformSpider(RedisSpider):
"""多平台电商数据爬虫"""
name = 'multi_platform'
redis_key = 'multi_platform:start_urls'
def __init__(self, platform=None, *args, **kwargs):
super().__init__(*args, **kwargs)
self.platform = platform or 'yahoo' # 默认使用雅虎拍卖
self.platform_configs = {
'yahoo': {
'base_url': 'https://auctions.yahoo.co.jp',
'parse_method': self.parse_yahoo
},
'mercari': {
'base_url': 'https://jp.mercari.com',
'parse_method': self.parse_mercari
},
'rakuten': {
'base_url': 'https://item.rakuten.co.jp',
'parse_method': self.parse_rakuten
}
}
def parse(self, response):
"""统一解析入口函数"""
config = self.platform_configs.get(self.platform, {})
parse_method = config.get('parse_method')
if parse_method:
yield from parse_method(response)
def parse_yahoo(self, response):
"""解析雅虎拍卖平台商品"""
for item in response.css('.Product'):
yield {
'platform': 'yahoo',
'title': item.css('.Product__title::text').get(),
'price': item.css('.Product__price::text').get(),
'url': response.urljoin(item.css('a::attr(href)').get()),
'end_time': item.css('.Product__end-time::attr(data-time)').get()
}
# 处理翻页
next_page = response.css('.next-page::attr(href)').get()
if next_page:
yield scrapy.Request(
response.urljoin(next_page),
callback=self.parse
)
def parse_mercari(self, response):
"""解析煤炉平台商品(API返回JSON)"""
data = response.json()
for item in data.get('items', []):
yield {
'platform': 'mercari',
'title': item.get('name'),
'price': item.get('price'),
'url': f"https://jp.mercari.com/item/{item.get('id')}"
}
3.3 分布式部署与启动
部署流程非常简单:Master节点启动Redis,Worker节点运行Spider,最后向Redis推送任务即可。
# Master节点启动Redis服务
redis-server
# Worker节点启动爬虫
scrapy runspider multi_platform_spider.py
# 向Redis推送初始任务链接
redis-cli lpush multi_platform:start_urls "https://auctions.yahoo.co.jp/category/2084006137"
四、数据去重与监控
去重是分布式爬虫的重难点。这里使用Redis的集合配合MD5哈希实现布隆过滤器思想,既保证了效率又节省内存。
import hashlib
import redis
class DuplicateFilter:
"""基于Redis的URL去重过滤器"""
def __init__(self):
self.redis_client = redis.Redis(host='localhost', port=6379, db=1)
self.key = "crawled_urls"
def is_duplicate(self, url: str) -> bool:
url_hash = hashlib.md5(url.encode()).hexdigest()
return self.redis_client.sismember(self.key, url_hash)
def mark_crawled(self, url: str):
url_hash = hashlib.md5(url.encode()).hexdigest()
self.redis_client.sadd(self.key, url_hash)
五、性能数据
这套系统实际部署了5个Worker节点,日均稳定采集超过50万条数据,系统可用性达到99.5%。实践中最大的体会是:分布式爬虫的成败,往往取决于任务调度的合理性与去重机制的可靠性,只要这两方面做到位,后续运行基本不会出现大问题。
