做一下分布式爬虫的教程
目前的scrapy分布式爬虫一般都是基于redis数据库的分布式爬虫。
并且真正的分布式是在多台机器上运行同一个项目,但是应该没有富哥/富婆有多台机器(电脑)。
所以还剩下两种方案:
1.直接在你电脑主机上(windows系统)上写一个分布式项目,然后同时运行多次这个项目(相当于多进程)
2.搞虚拟机来模拟多台机器
由于我电脑硬盘空间十分滴宝贵(没空间了),所以我使用第一种方法。
开始准备:下载redis数据库
redis数据库是一种类似mongodb的非关系型数据库,优点就是实时性以及速度非常滴快,占用空间也很小。
下面是下载链接:
redis-2.8.9: https://pan.baidu.com/s/1uRleT7jweEoays2yqekh-w?pwd=m9kw
只有redis数据库还是不够的,我们需要一个有操作界面的工具软件,类似mysql我们使用navicat来连接操控,毕竟没人喜欢用数据库自带的命令行一行一行的敲命令吧。
Tiny RDM :https://pan.baidu.com/s/1eyDsKGwGEgv7E24qVEvpiw?pwd=e5a6
redis数据库下载后是一个压缩包,解压后会出现一堆.exe文件,因为我们只是简单使用,所以不需要修改redis的配置(conf文件),直接双击 redis-server.exe 文件就启动了redis数据库。

然后就可以使用工具来连接redis数据库来看看数据库是否正常运行,Tiny RDM软件不需要安装,双击打开可以直接使用。

点击添加新连接。

因为我们是在本地运行多个分布式爬虫项目,所以没有配置redis数据库的远程连接,并且每有配置密码,所以连接地址默认使用127.0.0.1本地回环,端口也是默认6379,密码不需要填写,只需要填写一个连接名就可以了。

这是连接成功后的界面,如果连接失败看看自己是不是把redis服务启动后弹出的终端窗口关闭了,这个终端窗口在redis运行期间是不能关闭的。
redis数据库配置完成,目前我们还用不到它,我们先去创建scrapy项目
在PyCharm中创建一个新的项目,然后在左下角找到终端按钮打开,然后安装scrapy的库:pip install scrapy


然后创建一个scrapy的项目:scrapy startproject douban
然后进入scrapy项目的文件夹中:cd douban
创建一个示例爬虫代码文件:scrapy genspider douban250 douban.com

先修改一下基础的示例代码文件,然后运行项目:scrapy crawl douban250
测试一下是否能成功访问豆瓣250。

我们可以发现返回的状态码为空(当状态码为200时才是成功访问),原因是因为我们还没有进行访问头user-agent的配置,所以豆瓣250禁止我们访问,我们需要去修改settings.py配置文件。

USER_AGENT为访问头
同时我们发现输出中有大量的DEBUG信息和INFO信息,但是这些信息只是日志信息,既不是报错,也不是我们需要的信息,所以使用LOG_LEVEL来设置日志等级,设置为”WARNING”即只有警告以及报错以上的信息才会提示你。
同时我们可以顺便进行一下scrapy连接redis数据库的相关配置。

以及把管道打开(把ITEM_PIPELINES前面的注释删除掉),并且因为要使用scrapy-redis,所以还要把redis的管道加上
REDIS_HOST:redis数据库的连接地址,因为在本地,所以使用127.0.0.1
REDIS_PORT:为redis数据库服务用的端口号
REDIS_DB:为要使用redis的哪个数据库(初始默认有15个数据库,这里使用7号数据库)
REDIS_PASSWORD:redis数据库的密码,咱们没有配置密码,所以这一项可以不用添加
下面三行是使用redis的调度器替换scrapy调度器列表的相关配置
相关配置的文本
LOG_LEVEL = "WARNING"
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
DOWNLOAD_DELAY = 2 # 设置请求之间的延迟为 2 秒
# redis相关配置
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379
REDIS_DB = 7
# REDIS_PASSWORD = ''
# scrapy_redis相关配置
SCHEDULER = 'scrapy_redis.scheduler.Scheduler'
SCHEDULER_PERSIST = True # 如果为真,则在关闭时自动保存请求信息
DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter' # 去重的逻辑,要使用redis的
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 301,
"douban.pipelines.DoubanPipeline": 300,
}
不知道上面的配置文件怎么搞?没事,可以直接复制下面的完整文件覆盖掉你的settings.py文件
# Scrapy settings for douban project
#
# For simplicity, this file contains only settings considered important or
# commonly used. You can find more settings consulting the documentation:
#
# https://docs.scrapy.org/en/latest/topics/settings.html
# https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
# https://docs.scrapy.org/en/latest/topics/spider-middleware.html
BOT_NAME = "douban"
SPIDER_MODULES = ["douban.spiders"]
NEWSPIDER_MODULE = "douban.spiders"
LOG_LEVEL = "WARNING"
# Crawl responsibly by identifying yourself (and your website) on the user-agent
USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3'
DOWNLOAD_DELAY = 2 # 设置请求之间的延迟为 2 秒
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
# Configure maximum concurrent requests performed by Scrapy (default: 16)
# CONCURRENT_REQUESTS = 32
# Configure a delay for requests for the same website (default: 0)
# See https://docs.scrapy.org/en/latest/topics/settings.html#download-delay
# See also autothrottle settings and docs
# DOWNLOAD_DELAY = 3
# The download delay setting will honor only one of:
# CONCURRENT_REQUESTS_PER_DOMAIN = 16
# CONCURRENT_REQUESTS_PER_IP = 16
# Disable cookies (enabled by default)
# COOKIES_ENABLED = False
# Disable Telnet Console (enabled by default)
# TELNETCONSOLE_ENABLED = False
# Override the default request headers:
# DEFAULT_REQUEST_HEADERS = {
# "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
# "Accept-Language": "en",
# }
# Enable or disable spider middlewares
# See https://docs.scrapy.org/en/latest/topics/spider-middleware.html
# SPIDER_MIDDLEWARES = {
# "douban.middlewares.DoubanSpiderMiddleware": 543,
# }
# Enable or disable downloader middlewares
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html
# DOWNLOADER_MIDDLEWARES = {
# "douban.middlewares.DoubanDownloaderMiddleware": 543,
# }
# Enable or disable extensions
# See https://docs.scrapy.org/en/latest/topics/extensions.html
# EXTENSIONS = {
# "scrapy.extensions.telnet.TelnetConsole": None,
# }
# Configure item pipelines
# See https://docs.scrapy.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 301,
"douban.pipelines.DoubanPipeline": 300,
}
# redis相关配置
REDIS_HOST = '127.0.0.1'
REDIS_PORT = 6379
REDIS_DB = 7
# REDIS_PASSWORD = ''
# scrapy_redis相关配置
SCHEDULER = 'scrapy_redis.scheduler.Scheduler'
SCHEDULER_PERSIST = True # 如果为真,则在关闭时自动保存请求信息
DUPEFILTER_CLASS = 'scrapy_redis.dupefilter.RFPDupeFilter' # 去重的逻辑,要使用redis的
# Enable and configure the AutoThrottle extension (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/autothrottle.html
# AUTOTHROTTLE_ENABLED = True
# The initial download delay
# AUTOTHROTTLE_START_DELAY = 5
# The maximum download delay to be set in case of high latencies
# AUTOTHROTTLE_MAX_DELAY = 60
# The average number of requests Scrapy should be sending in parallel to
# each remote server
# AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# Enable showing throttling stats for every response received:
# AUTOTHROTTLE_DEBUG = False
# Enable and configure HTTP caching (disabled by default)
# See https://docs.scrapy.org/en/latest/topics/downloader-middleware.html#httpcache-middleware-settings
# HTTPCACHE_ENABLED = True
# HTTPCACHE_EXPIRATION_SECS = 0
# HTTPCACHE_DIR = "httpcache"
# HTTPCACHE_IGNORE_HTTP_CODES = []
# HTTPCACHE_STORAGE = "scrapy.extensions.httpcache.FilesystemCacheStorage"
# Set settings whose default value is deprecated to a future-proof value
TWISTED_REACTOR = "twisted.internet.asyncioreactor.AsyncioSelectorReactor"
FEED_EXPORT_ENCODING = "utf-8"
然后再回到爬虫代码



由于我在代码中使用了scrapy自带的items的数据结构,所以要将items.py导入到代码文件中。

右键scrapy项目”豆瓣”选择将其标记为源代码根目录

然后在代码中导入items.py

前面忘了说了,还需要导入scrapy-redis的第三方库
下载命令:pip install scrapy-redis
完整的爬虫douban250.py代码
import scrapy
from douban.items import DoubanItem
from scrapy_redis.spiders import RedisSpider, RedisCrawlSpider
class Douban250Spider(RedisSpider): # 换成RedisSpider
name = "douban250"
allowed_domains = ["douban.com"]
# start_urls = ["https://movie.douban.com/top250"]
redis_key = "douban_start_url" # 开始链接换成redis_key
def parse(self, response, **kwargs):
# 进入电影详情页
lis = response.xpath("//ol[@class='grid_view']/li")
for li in lis:
href = li.xpath("./div/div[2]/div[1]/a/@href").extract_first()
# 不用管是否重复,判断工作交给scrapy_redis完成
yield scrapy.Request(url=href, callback=self.parse_detail)
# 继续爬取下一页电影详情页的url
next_page = response.xpath("//*[@id='content']/div/div[1]/div[2]/span[3]/a/@href").extract_first()
yield scrapy.Request(url=response.urljoin(next_page), callback=self.parse)
def parse_detail(self, response, **kwargs):
douban = DoubanItem()
name = response.xpath("//*[@id='content']/h1/span[1]/text()").extract_first()
date = response.xpath("//*[@id='content']/h1/span[2]/text()").extract_first()
daoyan = response.xpath("//*[@id='info']/span[1]/span[2]/a/text()").extract_first()
bianjus = response.xpath("//*[@id='info']/span[2]/span[2]/a")
bianju = ""
for i in bianjus:
test = i.xpath("./text()").extract_first()
bianju += test + "/"
pingfen = response.xpath("//*[@id='interest_sectl']/div[1]/div[2]/strong/text()").extract_first()
pingjiarenshu = response.xpath(
"//*[@id='interest_sectl']/div[1]/div[2]/div/div[2]/a/span/text()").extract_first()
douban["name"] = name
douban["date"] = date
douban["daoyan"] = daoyan
douban["bianju"] = bianju
douban["pingfen"] = pingfen
douban["pingjiarenshu"] = pingjiarenshu
yield douban
完整的items.py代码
# Define here the models for your scraped items
#
# See documentation in:
# https://docs.scrapy.org/en/latest/topics/items.html
import scrapy
class DoubanItem(scrapy.Item):
# define the fields for your item here like:
name = scrapy.Field() # 电影名
date = scrapy.Field() # 首映时间
daoyan = scrapy.Field() # 导演
bianju = scrapy.Field() # 编剧
pingfen = scrapy.Field() # 豆瓣评分
pingjiarenshu = scrapy.Field() # 评价人数
开始启动项目
1.先启动redis数据库服务
2.打开连接redis数据库的软件
3.切换到7号数据库

4.使用终端启动多个scrapy爬虫项目
先使用 cd douban 进入项目文件夹
然后使用命令启动项目:scrapy crawl douban250


运行项目后你会发现终端里面一直是空白的,无论过多久都没有任何信息,这是正常的,因为爬虫还没有真正的开始运行,去查看一下代码

可以看到爬虫的开始url被注释掉了,因为我们需要完成分布式的爬取,也就是说多台设备共同做一个爬虫任务,那么scrapy本身自带的调度器我们就通过redis将其“合并”到了一起,多台设备共用一个用于存取需要爬取的url的列表,这个列表位于redis的7号数据库中,目前这个列表是空的,所以爬虫任务不能开始。
我们需要将第一个要爬的页面的url添加到调度器列表当中。
键:douban_start_url
元素:https://movie.douban.com/top250

这是在分别查看两个终端,两个终端都开始输出爬取的电影信息,且不一样(代表着两个进程在爬取不同的电影,最后都会将爬取到的信息返回到redis数据库中,完成了分布式爬虫的目的)
可以在redis的7号数据库中查看爬取到的电影信息


什么,你报错了?
那就对了,我也报错,报错内容如下

报错原因是因为scrapy-redis新版本中不用string了,推荐你使用json类型的数据,这里有三种解决办法:
1.按照人家的推荐把url相关的数据换成json类型(气抖冷,居然不让我想用什么数据用什么数据,当然不能惯着你,我就不用json)
2.先往redis中添加开始url,然后再启动项目(欸!官方代码更新了,但是没更新完全!先添加开始url再启动项目他就不报错正常运行了)
3.使用老版本的scrapy库,先pip uninstall scrapy卸载
然后再 pip install scrapy==2.90 下载老版本的库来使用(没办法,我跟着学的是21年的课程,那时候的库都是老版本,我只会些老版本的语法)
推荐使用第二种方法,啥也不用管,换个顺序就行了