Scrapy-Redis
Scrapy和Scrapy-Redis的区别
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" contenteditable="true" cid="n4" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Scrapy 是一个通用的爬虫框架,但是不支持分布式,
Scrapy-Redis 是为了更方便地实现Scrapy分布式爬取,而提供了一些以redis为基础的组件(仅有组件)</pre>
安装Scrapy-Redis
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" contenteditable="true" cid="n6" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">pip install scrapy-redis</pre>
Scrapy-Redis介绍
提供了下面四种组件(components):(四种组件意味着这四个模块都要做相应的修改)
Scheduler
Duplication Filter
Item Pipeline
Base Spider
Scrapy-Redis架构
[图片上传失败...(image-14424a-1547106579480)]
Scrapy-Redis分布式策略
假设有四台电脑:Windows 10、Mac OS X、Ubuntu 16.04、CentOS 7.2,任意一台电脑都可以作为 Master端 或 Slaver端,比如:
Master端
(核心服务器) :使用 Windows 10,搭建一个Redis数据库,不负责爬取,只负责url指纹判重、Request的分配,以及数据的存储Slaver端
(爬虫程序执行端) :使用 Mac OS X 、Ubuntu 16.04、CentOS 7.2,负责执行爬虫程序,运行过程中提交新的Request给Master
[图片上传失败...(image-3221a0-1547106579480)]
首先Slaver端从Master端拿任务(Request、url)进行数据抓取,Slaver抓取数据的同时,产生新任务的Request便提交给 Master 处理;
Master端只有一个Redis数据库,负责将未处理的Request去重和任务分配,将处理后的Request加入待爬队列,并且存储爬取的数据。
Scrapy-Redis默认使用的就是这种策略,我们实现起来很简单,因为任务调度等工作Scrapy-Redis都已经帮我们做好了,我们只需要继承RedisSpider、指定redis_key就行了。
缺点是,Scrapy-Redis调度的任务是Request对象,里面信息量比较大(不仅包含url,还有callback函数、headers等信息),可能导致的结果就是会降低爬虫速度、而且会占用Redis大量的存储空间,所以如果要保证效率,那么就需要一定硬件水平。
Redis
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" contenteditable="true" cid="n37" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">Redis使用请参考<redis函数.txt>
redis简单回顾
配置
windows redis启动
redis-server.exe redis.windows.conf
redis客户端连接其他windows的服务器
redis-cli -h 10.8.153.5
配置windows的redis服务器可以让其它客户端连接和读写
第56行,把这个注释掉
bind 127.0.0.1
第75行
protected-mode no
</pre>
源码自带项目说明:
使用scrapy-redis的example来修改
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" contenteditable="true" cid="n41" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">2、分布式部署
scrapy : 一个框架,不能实现分布式爬取
scrapy-redis : 基于这个框架开发的一套组件,可以让scrapy实现分布式的爬取
(1)安装: pip install scrapy-redis
(2)样本查看
https://github.com/rmax/scrapy-redis
example-project\example\spiders
dmoz.py : 普通crawlspider,没有参考价值
myspider_redis.py : 分布式的Spider模板
mycrawler_redis.py : 分布式的CrawlSpider模板
Spider ====》 RedisSpider
CrawlSpider ====》 RedisCrawlSpider
name name
redis_key start_urls
init() allowed_domains
【注】init()是一个坑,现在还是使用allowed_domains这种列表的形式
(3)存储到redis中
scrapy-redis组件已经写好往redis中存放的管道,只需要使用即可,默认存储到本机的redis服务中
如果想存储到其它的redis服务中,需要在配置文件中配置
REDIS_HOST = 'ip地址'
REDIS_PORT = 6379
也可以使用 REDIS_URL = "redis://10.20.158.2:6379"
(4)部署分布式
爬虫文件按照模板文件修改
配置文件说明:
使用scrapy-redis组件的去重队列
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter"
使用scrapy-redis组件自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler"
是否允许暂停
SCHEDULER_PERSIST = True
【注】分布式爬取的时候,指令不是scrapy crawl xx
scrapy runspider xxx.py
在我的windows中往队列中添加起始url
lpush spider:start_urls "http://www.baike.com/"
</pre>
数据持久化
存入mysql
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="python" contenteditable="true" cid="n45" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 1em 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"># baike_mysql.py
import json
import redis
import MySQLdb
def main():
指定redis数据库信息
rediscli = redis.StrictRedis(host='127.0.0.1', port=6379, password='123456', db=0)
指定mysql数据库
mysqlcli = MySQLdb.connect(host='127.0.0.1', user='root', password='123456', db='db', port=3306,charset='utf8')
while True:
FIFO模式为 blpop,LIFO模式为 brpop,获取键值
source, data = rediscli.blpop(["mybaike_redis:items"])
item = json.loads(data)
print(item)
try:
使用cursor()方法获取操作游标
cur = mysqlcli.cursor()
sql = 'INSERT INTO BAIKE(url,masterTitle,secondTitle,content)
VALUES("%s","%s","%s","%s")' % (
item["url"], item["masterTitle"], item["secondTitle"], item["content"])
print(sql)
cur.execute(sql)
mysqlcli.commit()
关闭本次操作
cur.close()
print("inserted %s" % item['source_url'])
except MySQLdb.Error as e:
print("Mysql Error %d: %s" % (e.args[0], e.args[1]))
if name == 'main':
main()
</pre>