概述
我们一般在使用Request-Html,Requests爬取数据的时候,为了提高爬虫的速度和效率,会选择开多线程和多进程,但是有的时候还是避免不了网络拥堵,资源没有合理化利用的时候,还有我们在为每一个请求配置Cookie和UA以及Proxy的时候也很麻烦,小型的爬虫还要,一旦涉及到大的项目,这些请求库就显得捉襟见肘了
这个时候我们看Scrapy,他是一个我们可以看做是协程的异步爬虫框架,请求之间都是异步而且并发的,在快速开发方面有很大的优势,适合大的爬虫项目,但是在开发过程中能明显感觉到,一旦碰到账号受到限制,以及带宽等限制之后,请求就会产生拥堵,他确实能一次性发送大量的请求,但是也会发生请求丢失的风险
那么我们面对一些数据量很大,请求次数较多,限制较高的项目的时候,为了提升爬取的效率,以及减少请求的丢失率,就会用到下面介绍的分布式爬虫框架
什么是分布式
举一个简单的例子,就是我们平时逛淘宝的时候,会去想淘宝不可能一台机器就能顶住那么高的并发量以及压力,后面肯定是将我们用户的请求进行了分发,相当于我们nginx中的反向代理,你看着是想一个ip地址发送得请求,但是你不知道到底是哪一台机器处理了你的请求
那这些处理我们请求的多台服务器组成的系统,就是分布式系统,我们称之为集群
在爬虫里面,我们完全可以将大量的请求分发到多台服务器进行处理,这样一来,加快了抓取数据的速度,减少了请求的丢失率,唯一的缺点就是需要一个集群
Scrapy-redis
安装
pip install scrapy-redis
Scrapy-redis是一个分布式的爬虫框架,这是该框架在github上的地址github.com/rmax/scrapy…
我们在该框架中能找到上述图片中的样例,在开发过程中我们可以适度的参考作者提供的代码样例
下面对各个文件展开说明
settings.py
详情请见注释
SPIDER_MODULES = ['example.spiders']
NEWSPIDER_MODULE = 'example.spiders'
# x需要改成自己的ua,或者使用UserAgent库中的ua
USER_AGENT = 'scrapy-redis (+https://github.com/rolando/scrapy-redis)'
# 下面这两行行是对请求进行过滤的代码,copy即可
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
# 允许中途暂停,继续
SCHEDULER_PERSIST = True
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"
#SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"
# copy即可
ITEM_PIPELINES = {
# 'example.pipelines.ExamplePipeline': 300,
'scrapy_redis.pipelines.RedisPipeline': 400,
}
# 日志级别
LOG_LEVEL = 'DEBUG'
# Introduce an artifical delay to make use of parallelism. to speed up the
# crawl.
# 下载等待
DOWNLOAD_DELAY = 1
# redis的配置
REDIS_HOST = '182.92.178.111'
REDIS_PORT = 6379
REDIS_PARAMS = {
'password': '*********',
'db': 11
}
# 或者在解析器中加入
"""
custom_settings = {
'LOG_LEVEL': 'DEBUG',
'DOWNLOAD_DELAY': 1,
# 指定redis数据库的连接参数
'REDIS_HOST': '182.92.178.111',
'REDIS_PORT': 6379,
# 指定 redis链接密码,和使用哪一个数据库
'REDIS_PARAMS' : {
'password': '***************',
'db': 11
},
"""
# 如果要用到数据库的话
MYSQL_DB= {
"host": "182.92.178.111",
"database": "duanzi",
"user": "root",
"password": "*********",
"port": 3306
}
关于mysql和redis的linux环境的搭建可以看我往期博客
items.py和pipelines.py勇哥哥我就不赘述了,跟scrapy中一样的,详情请看www.osgeo.cn/scrapy/topi…
dmoz.py
我们可以看到dmoz.py中的代码是使用的CrawlSpider创建的,具体的创建脚本如下
scrapy genspider -t crawl qiushi qiushi.com
这里没有用到分布式获取request,我们再接着看(其实我们实在setting中配置好redis之后,获取到的数据就可以到redis中了)
mycrawler_redis.py
这里就用到了分布式了,继承了RedisCrawlSpider详情看注释
from scrapy.spiders import Rule
# 规则
from scrapy.linkextractors import LinkExtractor
# 这里让爬虫类继承RedisCrawlSpider
from scrapy_redis.spiders import RedisCrawlSpider
class MyCrawler(RedisCrawlSpider):
"""Spider that reads urls from redis queue (myspider:start_urls)."""
# 爬虫的名字
name = 'mycrawler_redis'
# 很重要啊,很快啊!!
# 这里是redis请求列表中的key
redis_key = 'mycrawler:start_urls'
# 请求过来之后获取的元素,一般这里会去获取url,方便下面parse函数对页面进行处理
rules = (
# follow all links
Rule(LinkExtractor(), callback='parse_page', follow=True),
)
# 这个我就感觉没必要,也没细看
def __init__(self, *args, **kwargs):
# Dynamically define the allowed domains list.
domain = kwargs.pop('domain', '')
self.allowed_domains = filter(None, domain.split(','))
super(MyCrawler, self).__init__(*args, **kwargs)
# 页面解析函数
def parse_page(self, response):
return {
'name': response.css('title::text').extract_first(),
'url': response.url,
}
myspider_redis
这里也用到了分布式了,继承了RedisSpider详情看注释
# 大致看看,后面有项目示例
from scrapy_redis.spiders import RedisSpider
class MySpider(RedisSpider):
"""Spider that reads urls from redis queue (myspider:start_urls)."""
name = 'myspider_redis'
redis_key = 'myspider:start_urls'
def __init__(self, *args, **kwargs):
# Dynamically define the allowed domains list.
domain = kwargs.pop('domain', '')
self.allowed_domains = filter(None, domain.split(','))
super(MySpider, self).__init__(*args, **kwargs)
def parse(self, response):
return {
'name': response.css('title::text').extract_first(),
'url': response.url,
}
使用scrapy-redis抓取“求实百科”的数据
项目结构
settings.py
首先看settings.py的配置
# Scrapy settings for duanzi 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
from fake_useragent import UserAgent
BOT_NAME = 'duanzi'
SPIDER_MODULES = ['duanzi.spiders']
NEWSPIDER_MODULE = 'duanzi.spiders'
USER_AGENT = UserAgent().random
ROBOTSTXT_OBEY = False
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
SCHEDULER_PERSIST = True
# SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
# SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"
# SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"
ITEM_PIPELINES = {
'scrapy_redis.pipelines.RedisPipeline': 400,
# 'duanzi.pipelines.DuanziPipeline': 300,
}
# 日志级别
LOG_LEVEL = 'DEBUG'
# Introduce an artifical delay to make use of parallelism. to speed up the
# crawl.
# 下载等待
DOWNLOAD_DELAY = 1
# redis的配置
REDIS_HOST = '182.92.178.111'
REDIS_PORT = 6379
REDIS_PARAMS = {
'password': '*********',
'db': 11
}
# 或者在解析器中加入
"""
custom_settings = {
'LOG_LEVEL': 'DEBUG',
'DOWNLOAD_DELAY': 1,
# 指定redis数据库的连接参数
'REDIS_HOST': '182.92.178.111',
'REDIS_PORT': 6379,
# 指定 redis链接密码,和使用哪一个数据库
'REDIS_PARAMS' : {
'password': '***************',
'db': 11
},
"""
# 如果要用到数据库的话
MYSQL_DB= {
"host": "182.92.178.111",
"database": "duanzi",
"user": "root",
"password": "*********",
"port": 3306
}
qiushi.py
from scrapy.linkextractors import LinkExtractor
from scrapy.spiders import CrawlSpider, Rule
from scrapy_redis.spiders import RedisCrawlSpider
from duanzi.items import DuanziItem
class QiushiSpider(RedisCrawlSpider):
name = 'qiushi'
allowed_domains = ['qiushibaike.com']
start_urls = ['https://www.qiushibaike.com/text/']
redis_key = 'qiushi:start_urls'
rules = (
Rule(LinkExtractor(allow=r'/article/\d+'), callback='parse_item', follow=True),
Rule(LinkExtractor(allow=r'/text/page/\d+'), callback='parse_item', follow=True),
)
def parse_item(self, response):
item = {}
item['title'] = response.xpath('//h1[@class="article-title"]//text()').get().strip()
item['content'] = response.xpath('//div[@class="content"]//text()').get().strip()
print(item)
yield item
start.py
from scrapy.cmdline import execute
execute('scrapy crawl qiushi'.split())
这样就完成了,是不是很简单,其实我们还有一个最重要的事情没有做,现在思考下人生
我是谁。。。。。。
我从何处来。。。。。。
要往何处去。。。。。。
其实数据也是这样,既然是分布式,我多台机器上部署项目之后,项目会一直停在那
程序会去我们在settings.py中配置的redis数据库中去找**key = 'qiushi:start_urls'**的这个列表
这里插一嘴,Redis五大数据类型(字符串,集合,有序集合,列表,hash)
往redis列表中推数据
import redis
REDIS_HOST = '182.92.178.111'
REDIS_PORT = 6379
REDIS_PARAMS = {
'password': '*********',
'db': 11
}
REDIS_KEY = 'qiushi:start_urls'
# 这里可以写很多的元素,后面大家可以自己去该写下
START_URLS = ['https://www.qiushibaike.com/text/']
my_redis = redis.Redis(host=REDIS_HOST, port=REDIS_PORT,
db=REDIS_PARAMS['db'], password=REDIS_PARAMS['password'])
for i in START_URLS:
my_redis.lpush(REDIS_KEY,i)
上面的步骤完成之后,我们分别开启项目,项目应该会一直等待,因为我们没有运行我们的push.py文件
运行完.py文件之后,应该在我们的redis中看着数据库中有一个qiushi的文件夹,下面有一个key为qiushi:start_urls的列表
当我们的多个项目启动起来之后,会将qiushi:start_urls管道中的元素进行分发,我们会在redis中看到这样的元素
qiushi:dupefilter:被调度器使用的url去重规则
qiushi:item:就是我们在爬虫类中解析出数据之后yield出来的字典结构的数据,现在存到的redis中,最后我们是要转移出来的
qiushi:start_urls:存放请求的地方,就是因为有这个,我们才可以实现“断点续抓”
将redis中抓取到的数据落地
数据是拿到了,但是现在都在redis中,如何永久保存呢,且看下面
MYSQL_DB= {
"host": "182.92.178.111",
"database": "duanzi",
"user": "root",
"password": "********",
"port": 3306
}
conn = pymysql.connect(charset='utf8', **MYSQL_DB)
cursor = conn.cursor()
REDIS_DB_WIN = {
"host": "localhost",
"password": "*******",
"db": 10
}
import redis
while True:
my_redis = redis.Redis(**REDIS_DB_WIN)
# 这样,就把redis中存储的数据取了出来
title,content = my_redis.blpop(['qiushi:item'])
# 存到mysql数据库
sql = "insert into duanzi_info('title','content') values {}".format(item.values())
cursor.execute(sql)
cursor.commit()
创作不易,点赞评论加关注^_^!!!!