# 14.3-Scrapy分布式实现

接下来，我们会利用 Scrapy-Redis 来实现分布式的对接。

## 1. 准备工作

请确保已经成功实现了 Scrapy 新浪微博爬虫，Scrapy-Redis 库已经正确安装，如果还没安装，请参考第 1 章的安装说明。

## 2. 搭建 Redis 服务器

要实现分布式部署，多台主机需要共享爬取队列和去重集合，而这两部分内容都是存于 Redis 数据库中的，我们需要搭建一个可公网访问的 Redis 服务器。

推荐使用 Linux 服务器，可以购买阿里云、腾讯云、Azure 等提供的云主机，一般都会配有公网 IP，具体的搭建方式可以参考第 1 章中 Redis 数据库的安装方式。

Redis 安装完成之后就可以远程连接了，注意部分商家（如阿里云、腾讯云）的服务器需要配置安全组放通 Redis 运行端口才可以远程访问。如果遇到不能远程连接的问题，可以排查安全组的设置。

需要记录 Redis 的运行 IP、端口、地址，供后面配置分布式爬虫使用。当前配置好的 Redis 的 IP 为服务器的 IP 120.27.34.25，端口为默认的 6379，密码为 foobared。

## 3. 部署代理池和 Cookies 池

新浪微博项目需要用到代理池和 Cookies 池，而之前我们的代理池和 Cookies 池都是在本地运行的。所以我们需要将二者放到可以被公网访问的服务器上运行，将代码上传到服务器，修改 Redis 的连接信息配置，用同样的方式运行代理池和 Cookies 池。

远程访问代理池和 Cookies 池提供的接口，来获取随机代理和 Cookies。如果不能远程访问，先确保其在 0.0.0.0 这个 Host 上运行，再检查安全组的配置。

如我当前配置好的代理池和 Cookies 池的运行 IP 都是服务器的 IP，120.27.34.25，端口分别为 5555 和 5556，如图 14-3 和图 14-4 所示。

![](/files/-Ll_JzxFp_pqEh22fEpg)

图 14-3 代理池接口

![](/files/-Ll_JzxJmpU1XymPps4Y)

图 14-4 Cookies 池接口

所以接下来我们就需要把 Scrapy 新浪微博项目中的访问链接修改如下：

```python
PROXY_URL = 'http://120.27.34.25:5555/random'
COOKIES_URL = 'http://120.27.34.25:5556/weibo/random'
```

具体的修改方式根据实际配置的 IP 和端口做相应调整。

## 4. 配置 Scrapy-Redis

配置 Scrapy-Redis 非常简单，只需要修改一下 settings.py 配置文件即可。

### 核心配置

首先最主要的是，需要将调度器的类和去重的类替换为 Scrapy-Redis 提供的类，在 settings.py 里面添加如下配置即可：

```python
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
```

### Redis 连接配置

接下来配置 Redis 的连接信息，这里有两种配置方式。

第一种方式是通过连接字符串配置。我们可以用 Redis 的地址、端口、密码来构造一个 Redis 连接字符串，支持的连接形式如下所示：

```python
redis://[:password]@host:port/db
rediss://[:password]@host:port/db
unix://[:password]@/path/to/socket.sock?db=db
```

password 是密码，比如要以冒号开头，中括号代表此选项可有可无，host 是 Redis 的地址，port 是运行端口，db 是数据库代号，其值默认是 0。

根据上文中提到我的 Redis 连接信息，构造这个 Redis 的连接字符串如下所示：

```python
redis://:foobared@120.27.34.25:6379
```

直接在 settings.py 里面配置为 REDIS\_URL 变量即可：

```python
REDIS_URL = 'redis://:foobared@120.27.34.25:6379'
```

第二种配置方式是分项单独配置。这个配置就更加直观明了，如根据我的 Redis 连接信息，可以在 settings.py 中配置如下代码：

```python
REDIS_HOST = '120.27.34.25'
REDIS_PORT = 6379
REDIS_PASSWORD = 'foobared'
```

这段代码分开配置了 Redis 的地址、端口和密码。

注意，如果配置了 REDIS\_URL，那么 Scrapy-Redis 将优先使用 REDIS\_URL 连接，会覆盖上面的三项配置。如果想要分项单独配置的话，请不要配置 REDIS\_URL。

在本项目中，我选择的是配置 REDIS\_URL。

### 配置调度队列

此项配置是可选的，默认使用 PriorityQueue。如果想要更改配置，可以配置 SCHEDULER\_QUEUE\_CLASS 变量，如下所示：

```python
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.PriorityQueue'
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.FifoQueue'
SCHEDULER_QUEUE_CLASS = 'scrapy_redis.queue.LifoQueue'
```

以上三行任选其一配置，即可切换爬取队列的存储方式。

在本项目中不进行任何配置，我们使用默认配置。

### 配置持久化

此配置是可选的，默认是 False。Scrapy-Redis 默认会在爬取全部完成后清空爬取队列和去重指纹集合。

如果不想自动清空爬取队列和去重指纹集合，可以增加如下配置：

```python
SCHEDULER_PERSIST = True
```

将 SCHEDULER\_PERSIST 设置为 True 之后，爬取队列和去重指纹集合不会在爬取完成后自动清空，如果不配置，默认是 False，即自动清空。

值得注意的是，如果强制中断爬虫的运行，爬取队列和去重指纹集合是不会自动清空的。

在本项目中不进行任何配置，我们使用默认配置。

### 配置重爬

此配置是可选的，默认是 False。如果配置了持久化或者强制中断了爬虫，那么爬取队列和指纹集合不会被清空，爬虫重新启动之后就会接着上次爬取。如果想重新爬取，我们可以配置重爬的选项：

```python
SCHEDULER_FLUSH_ON_START = True
```

这样将 SCHEDULER\_FLUSH\_ON\_START 设置为 True 之后，爬虫每次启动时，爬取队列和指纹集合都会清空。所以要做分布式爬取，我们必须保证只能清空一次，否则每个爬虫任务在启动时都清空一次，就会把之前的爬取队列清空，势必会影响分布式爬取。

注意，此配置在单机爬取的时候比较方便，分布式爬取不常用此配置。

在本项目中不进行任何配置，我们使用默认配置。

### Pipeline 配置

此配置是可选的，默认不启动 Pipeline。Scrapy-Redis 实现了一个存储到 Redis 的 Item Pipeline，启用了这个 Pipeline 的话，爬虫会把生成的 Item 存储到 Redis 数据库中。在数据量比较大的情况下，我们一般不会这么做。因为 Redis 是基于内存的，我们利用的是它处理速度快的特性，用它来做存储未免太浪费了，配置如下：

```python
ITEM_PIPELINES = {'scrapy_redis.pipelines.RedisPipeline': 300}
```

本项目不进行任何配置，即不启动 Pipeline。

到此为止，Scrapy-Redis 的配置就完成了。有的选项我们没有配置，但是这些配置在其他 Scrapy 项目中可能用到，要根据具体情况而定。

## 5. 配置存储目标

之前 Scrapy 新浪微博爬虫项目使用的存储是 MongoDB，而且 MongoDB 是本地运行的，即连接的是 localhost。但是，当爬虫程序分发到各台主机运行的时候，爬虫就会连接各自的的 MongoDB。所以我们需要在各台主机上都安装 MongoDB，这样有两个缺点：一是搭建 MongoDB 环境比较烦琐；二是这样各台主机的爬虫会把爬取结果分散存到各自主机上，不方便统一管理。

所以我们最好将存储目标存到同一个地方，例如都存到同一个 MongoDB 数据库中。我们可以在服务器上搭建一个 MongoDB 服务，或者直接购买 MongoDB 数据存储服务。

这里使用的就是服务器上搭建的的 MongoDB 服务，IP 仍然为 120.27.34.25，用户名为 admin，密码为 admin123。

修改配置 MONGO\_URI 为如下：

```python
MONGO_URI = 'mongodb://admin:admin123@120.27.34.25:27017'
```

到此为止，我们就成功完成了 Scrapy 分布式爬虫的配置了。

## 6. 运行

接下来将代码部署到各台主机上，记得每台主机都需要配好对应的 Python 环境。

每台主机上都执行如下命令，即可启动爬取：

```
scrapy crawl weibocn
```

每台主机启动了此命令之后，就会从配置的 Redis 数据库中调度 Request，做到爬取队列共享和指纹集合共享。同时每台主机占用各自的带宽和处理器，不会互相影响，爬取效率成倍提高。

## 7. 结果

一段时间后，我们可以用 RedisDesktop 观察远程 Redis 数据库的信息。这里会出现两个 Key：一个叫作 weibocn:dupefilter，用来储存指纹；另一个叫作 weibocn:requests，即爬取队列，如图 14-5 和图 14-6 所示。

![](/files/-Ll_JzxP3iF9hVFX9aVR)

图 14-5 去重指纹

![](/files/-Ll_JzxRgc3jrlj9DwXs)

图 14-6 爬取队列

随着时间的推移，指纹集合会不断增长，爬取队列会动态变化，爬取的数据也会被储存到 MongoDB 数据库中。

至此 Scrapy 分布式的配置已全部完成。

## 8. 本节代码

本节代码地址为：<https://github.com/Python3WebSpider/Weibo/tree/distributed>，注意这里是 distributed 分支。

## 9. 结语

本节通过对接 Scrapy-Redis 成功实现了分布式爬虫，但是部署还是有很多不方便的地方。另外，如果爬取量特别大的话，Redis 的内存也是个问题。在后文我们会继续了解相关优化方案。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://python3webspider.cuiqingcai.com/14.3scrapy-fen-bu-shi-shi-xian.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
