汽车之家电车评论包含车型体验、续航表现等关键信息,是产品分析与市场调研的核心数据源。单台机器运行Scrapy爬虫易触发反爬、效率低下,分布式爬虫通过多机器协同,可有效解决这一问题。本文将精简讲解Scrapy分布式爬虫的搭建、配置、开发及部署,附带完整可运行代码,助力开发者快速实现大规模评论采集。
一、核心技术栈与环境准备
搭建Scrapy分布式爬虫需多组件协同,核心配置如下:
1.1 核心技术选型
- Scrapy:核心爬虫框架,负责请求、解析与调度,支持中间件扩展。
- Scrapy-Redis:分布式核心,替代本地调度器,实现多机器任务共享与去重。
- Redis:存储待爬URL、去重集合,支持高并发访问。
- Python 3.8+:确保依赖兼容性;MySQL:结构化存储评论数据。
- 亿牛云代理(www.16yun.cn):动态切换IP,规避反爬封禁。
1.2 环境搭建步骤
- 安装依赖:
pip install scrapy scrapy-redis redis pymysql requests - Redis配置:安装后修改redis.conf,注释
bind 127.0.0.1、关闭protected-mode no,允许分布式节点访问。 - MySQL准备:创建数据库和表,SQL语句如下:
CREATE DATABASE IF NOT EXISTS car_home_ev;USE car_home_ev;CREATE TABLE IF NOT EXISTS ev_comments (id INT PRIMARY KEY AUTO_INCREMENT,car_model VARCHAR(50) NOT NULL,user_name VARCHAR(30),comment_time VARCHAR(20),comment_content TEXT NOT NULL,score INT,mileage VARCHAR(20),crawl_time DATETIME DEFAULT CURRENT_TIMESTAMP) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; - 代理配置:注册亿牛云代理,获取服务器地址、账号密码备用。
二、Scrapy分布式爬虫核心原理
传统Scrapy爬虫数据存储于单台机器内存,无法协同。Scrapy-Redis重写调度器与去重器,将核心数据存入Redis,实现多机器共享任务:
- 任一节点启动爬虫,将初始URL存入Redis待爬队列(
scrapy:requests)。 - 所有节点连接Redis,获取URL执行爬取,解析数据与新URL。
- 新URL经Redis去重(
scrapy:dupefilter)后加入队列,数据存入MySQL。 - 待爬队列为空时,爬虫终止,单节点故障不影响整体任务。
三、实战开发:Scrapy分布式爬虫搭建(附代码)
以下代码可直接复制运行,仅需修改代理、数据库等个人配置。
3.1 创建项目与分布式配置
3.1.1 创建项目
1# 创建项目 2scrapy startproject car_home_ev_spider 3cd car_home_ev_spider 4# 创建爬虫(指定域名) 5scrapy genspider ev_comment autohome.com.cn 6
3.1.2 settings.py核心配置
1# -*- coding: utf-8 -*- 2BOT_NAME = 'car_home_ev_spider' 3SPIDER_MODULES = ['car_home_ev_spider.spiders'] 4NEWSPIDER_MODULE = 'car_home_ev_spider.spiders' 5 6ROBOTSTXT_OBEY = False # 关闭robots协议 7# 分布式核心配置 8SCHEDULER = "scrapy_redis.scheduler.Scheduler" 9DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" 10REDIS_URL = 'redis://192.168.1.100:6379/0' # 替换自身Redis地址 11SCHEDULER_PERSIST = False 12SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue' 13 14# 反爬与请求配置 15DEFAULT_REQUEST_HEADERS = { 16 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', 17 'Accept-Language': 'zh-CN,zh;q=0.9', 18 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36', 19 'Referer': 'https://www.autohome.com.cn/', 20 'Cookie': '填写自身汽车之家Cookie' 21} 22DOWNLOAD_DELAY = 1.5 23CONCURRENT_REQUESTS = 16 24 25# 代理与数据库配置 26PROXY_URL = 'http://www.16yun.cn:8080' 27PROXY_USER = '你的代理用户名' 28PROXY_PASS = '你的代理密码' 29MYSQL_HOST = 'localhost' 30MYSQL_USER = 'root' 31MYSQL_PASSWORD = '你的MySQL密码' 32MYSQL_DB = 'car_home_ev' 33MYSQL_PORT = 3306 34 35# 启用Pipeline 36ITEM_PIPELINES = { 37 'car_home_ev_spider.pipelines.CarHomeEvSpiderPipeline': 300, 38} 39LOG_LEVEL = 'INFO' 40LOG_FILE = 'car_home_ev.log' 41
3.2 数据模型(items.py)
1# -*- coding: utf-8 -*- 2import scrapy 3 4class CarHomeEvSpiderItem(scrapy.Item): 5 car_model = scrapy.Field() # 车型 6 user_name = scrapy.Field() # 用户名 7 comment_time = scrapy.Field() # 评论时间 8 comment_content = scrapy.Field() # 评论内容 9 score = scrapy.Field() # 评分 10 mileage = scrapy.Field() # 行驶里程 11 crawl_time = scrapy.Field() # 爬取时间 12
3.3 代理中间件(middlewares.py)
1# -*- coding: utf-8 -*- 2import base64 3from scrapy import signals 4from scrapy.downloadermiddlewares.httpproxy import HttpProxyMiddleware 5from car_home_ev_spider.settings import PROXY_URL, PROXY_USER, PROXY_PASS 6 7class CarHomeEvSpiderMiddleware: 8 @classmethod 9 def from_crawler(cls, crawler): 10 s = cls() 11 crawler.signals.connect(s.spider_opened, signal=signals.spider_opened) 12 return s 13 def spider_opened(self, spider): 14 spider.logger.info('Spider opened: %s' % spider.name) 15 16# 亿牛云代理中间件 17class ProxyMiddleware(HttpProxyMiddleware): 18 def __init__(self, auth_encoding='latin-1'): 19 self.auth_encoding = auth_encoding 20 self.proxy_auth = base64.b64encode(f"{PROXY_USER}:{PROXY_PASS}".encode(self.auth_encoding)).decode(self.auth_encoding) 21 def process_request(self, request, spider): 22 request.meta['proxy'] = PROXY_URL 23 request.headers['Proxy-Authorization'] = f'Basic {self.proxy_auth}' 24 25# 反反爬中间件 26class AntiAntiCrawlMiddleware: 27 def process_request(self, request, spider): 28 request.headers['Cookie'] = spider.settings.get('DEFAULT_REQUEST_HEADERS')['Cookie'] 29 return None 30
在settings.py中添加中间件配置:
1DOWNLOADER_MIDDLEWARES = { 2 'car_home_ev_spider.middlewares.ProxyMiddleware': 543, 3 'car_home_ev_spider.middlewares.AntiAntiCrawlMiddleware': 544, 4 'scrapy.downloadermiddlewares.httpproxy.HttpProxyMiddleware': None, 5} 6
3.4 核心爬虫(ev_comment.py)
1# -*- coding: utf-8 -*- 2import scrapy 3from car_home_ev_spider.items import CarHomeEvSpiderItem 4 5class EvCommentSpider(scrapy.Spider): 6 name = 'ev_comment' 7 allowed_domains = ['autohome.com.cn'] 8 start_urls = ['https://www.autohome.com.cn/electric/'] # 电车列表页 9 10 def parse(self, response): 11 # 解析车型列表,获取评论页URL 12 car_list = response.xpath('//div[@class="list-cont"]/div[@class="cont-box"]') 13 for car in car_list: 14 car_model = car.xpath('.//h3/a/text()').extract_first().strip() 15 comment_url = car.xpath('.//div[@class="btn-box"]/a[contains(text(), "口碑")]/@href').extract_first() 16 if comment_url: 17 yield scrapy.Request( 18 url=response.urljoin(comment_url), 19 callback=self.parse_comment_list, 20 meta={'car_model': car_model}, 21 dont_filter=True 22 ) 23 # 列表页分页 24 next_page = response.xpath('//a[@class="page-item-next"]/@href').extract_first() 25 if next_page: 26 yield scrapy.Request(url=response.urljoin(next_page), callback=self.parse, dont_filter=True) 27 28 def parse_comment_list(self, response): 29 # 解析评论数据 30 car_model = response.meta['car_model'] 31 comment_list = response.xpath('//div[@class="koubei-list"]/div[@class="koubei-item"]') 32 for comment in comment_list: 33 item = CarHomeEvSpiderItem() 34 item['car_model'] = car_model 35 item['user_name'] = comment.xpath('.//div[@class="user-info"]/a/text()').extract_first() or '未知用户' 36 item['comment_time'] = comment.xpath('.//div[@class="user-info"]/span/text()').extract_first() or '' 37 comment_content = comment.xpath('.//div[@class="content"]/p/text()').extract() 38 item['comment_content'] = ''.join([text.strip() for text in comment_content]) if comment_content else '' 39 score_class = comment.xpath('.//div[@class="score"]/span/@class').extract_first() 40 item['score'] = int(score_class.replace('star', '')) if score_class else None 41 item['mileage'] = comment.xpath('.//div[@class="info"]/span[1]/text()').extract_first().strip() if comment.xpath('.//div[@class="info"]/span[1]/text()').extract_first() else '未知' 42 item['crawl_time'] = None 43 yield item 44 # 评论页分页 45 next_comment_page = response.xpath('//a[@class="page-next"]/@href').extract_first() 46 if next_comment_page: 47 yield scrapy.Request( 48 url=response.urljoin(next_comment_page), 49 callback=self.parse_comment_list, 50 meta={'car_model': car_model}, 51 dont_filter=True 52 ) 53
3.5 数据存储Pipeline(pipelines.py)
1# -*- coding: utf-8 -*- 2import pymysql 3from datetime import datetime 4from car_home_ev_spider.settings import MYSQL_HOST, MYSQL_USER, MYSQL_PASSWORD, MYSQL_DB, MYSQL_PORT 5 6class CarHomeEvSpiderPipeline: 7 def __init__(self): 8 # 初始化数据库连接 9 self.conn = pymysql.connect( 10 host=MYSQL_HOST, user=MYSQL_USER, password=MYSQL_PASSWORD, 11 database=MYSQL_DB, port=MYSQL_PORT, charset='utf8mb4' 12 ) 13 self.cursor = self.conn.cursor() 14 15 def process_item(self, item, spider): 16 item['crawl_time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S') 17 # 插入数据,避免重复 18 sql = """ 19 INSERT INTO ev_comments (car_model, user_name, comment_time, comment_content, score, mileage, crawl_time) 20 VALUES (%s, %s, %s, %s, %s, %s, %s) 21 ON DUPLICATE KEY UPDATE comment_time = VALUES(comment_time), score = VALUES(score) 22 """ 23 try: 24 self.cursor.execute(sql, (item['car_model'], item['user_name'], item['comment_time'], 25 item['comment_content'], item['score'], item['mileage'], item['crawl_time'])) 26 self.conn.commit() 27 except Exception as e: 28 self.conn.rollback() 29 spider.logger.error(f"插入失败:{str(e)}") 30 return item 31 32 def close_spider(self, spider): 33 self.cursor.close() 34 self.conn.close() 35
四、分布式部署与运行
4.1 节点准备
多台机器需与Redis服务器同局域网,安装相同环境,复制项目并确保所有节点REDIS_URL、代理、数据库配置一致。
4.2 启动步骤
- 启动Redis服务,通过
redis-cli ping测试连接。 - 各节点进入项目目录,执行
scrapy crawl ev_comment启动爬虫。 - 通过
redis-cli keys *查看任务队列,通过MySQL查询实时查看爬取数据。
五、反爬应对与优化
- 控制请求频率,调整
DOWNLOAD_DELAY,节点数量控制在5-10台。 - 定期更新Cookie,及时适配汽车之家页面变化,调整XPath表达式。
- 增加异常捕获与重试机制,通过日志排查问题,可实现增量爬取减少重复工作。
六、总结与扩展
本文实现了Scrapy分布式爬虫采集汽车之家电车评论,解决了单机器爬取效率低、易反爬的问题。后续可扩展情感分析、数据可视化、定时爬取等功能,适配多汽车平台,进一步提升数据价值。分布式爬虫的核心是协同高效,需根据反爬强度灵活调整策略,确保稳定采集。
《Scrapy 分布式爬虫:大规模采集汽车之家电车评论》 是转载文章,点击查看原文。