概述
目录
一、Scrapy的简介
二、Scrapy的使用
1、安装
2、Scrapy工程的基本操作以及命令
3、项目结构
5、配置
6、scrapy持久化存储
7、scrapy中间件(下载中间件)
三、去重源码解析
四、scrapy-redis分布式爬虫
1、介绍
2、使用方法
一、Scrapy的简介
Scrapy 是基于twisted框架开发而来,twisted是一个流行的事件驱动的python网络框架。因此Scrapy使用了一种非阻塞(又名异步)的代码来实现并发。整体架构大致如下
它主要由五大组件和两中间件组成:
五大组件:
1、引擎(ENGINE):用来处理整个系统的数据流处理, 触发事务(框架核心)
2、调度器(SCHEDULER) : 用来接受引擎发过来的请求, 压入队列中, 并在引擎再次请求的时候返回. 可以想像成一个URL(抓取网页的网址或者说是链接)的优先队列, 由它来决定下一个要抓取的网址是什么, 同时去除重复的网址
3、下载器(DOWLOADER):用于下载网页内容, 并将网页内容返回给ENGINE,下载器是建立在twisted这个高效的异步模型上的
4、爬虫(Spiders):爬虫是主要干活的, 用于从特定的网页中提取自己需要的信息, 即所谓的实体(Item)。用户也可以从中提取出链接,让Scrapy继续抓取下一个页面
5、项目管道(Pipeline):负责处理爬虫从网页中抽取的实体(item),主要的功能是对item进行持久化、验证有效性、清理。当页面被操作。
两大中间件:
1、下载中间件:位于Scrapy引擎和下载器之间的框架,主要是处理Scrapy引擎与下载器之间的请求及响应。比如:添加请求头,更换user-agent、代理ip等
2、爬虫中间件:介于Scrapy引擎和spiders之间的框架,主要工作是处理spider的响应输入和请求输出。
二、Scrapy的使用
1、安装
#Windows平台
1、pip3 install wheel #安装后,便支持通过wheel文件安装软件,wheel文件官网:https://www.lfd.uci.edu/~gohlke/pythonlibs
3、pip3 install lxml
4、pip3 install pyopenssl
5、下载并安装pywin32:https://sourceforge.net/projects/pywin32/files/pywin32/
6、下载twisted的wheel文件:http://www.lfd.uci.edu/~gohlke/pythonlibs/#twisted
7、执行pip3 install 下载目录Twisted-17.9.0-cp36-cp36m-win_amd64.whl
8、pip3 install scrapy
#Linux平台
1、pip3 install scrapy
2、Scrapy工程的基本操作以及命令
#注意:都是在cmd下进行命令操作
# 1、创建scrapy工程
scrapy startproject 工程名字
# 2、切换到scrapy攻略目录下,创建爬虫文件
scrapy genspider 爬虫文件名字 www.xxx.com(允许爬取的域名)
# 3、执行scrapy工程
scrapy crawl 爬虫文件名字(不用带.py)
# 也可以创建一个py文件,内容如下,每次执行该py文件就可以
from scrapy.cmdline import execute
execute(['scrapy','crawl','爬虫文件名'])
'''
用法: scrapy <命令> [options] [args]
可用命令:
bench # scrapy压力测试
check # 检测项目有无语法错误
commands
crawl # 运行爬虫
edit # 编辑器,一般不用
fetch #独立于程单纯地爬取一个页面,可以拿到请求头
genspider #创建爬虫程序
list #列出项目中所包含的爬虫名
parse #scrapy parse url地址 --callback 回调函数 #以此可以验证我们的回调函数是否正确
runspider #运行一个独立的python文件,不必创建项目
settings #如果是在项目目录下,则得到的是该项目的配置
shell #scrapy shell url地址 在交互式调试,如选择器规则正确与否
startproject #创建项目
version #scrapy version 查看scrapy的版本,scrapy version -v查看scrapy依赖库的版本
view #下载完毕后直接弹出浏览器,以此可以分辨出哪些数据是ajax请求
'''
'''
例子:
注意:执行项目命令,要切到项目的目录下(check、list、parse、bench)
'''
scrapy settings --get XXX #如果切换到项目目录下,看到的则是该项目的配置
scrapy runspider baidu.py
scrapy shell https://www.baidu.com
response
response.status
response.body
view(response)
scrapy view https://www.taobao.com #如果页面显示内容不全,不全的内容则是ajax请求实现的,以此快速定位问题
scrapy fetch --nolog --headers https://www.taobao.com
scrapy version #scrapy的版本
scrapy version -v #依赖库的版本
3、项目结构
"""
├── project_name/
├── scrapy.cfg/ #项目的主配置信息,用来部署scrapy时使用,爬虫相关的配置信息在settings.py文件中
├── project_name/
├── __init__.py/
├── items.py/ # 设置数据存储模板,用于结构化数据,类似Django的Model
├── settings.py # 配置文件,如:递归的层数、并发数,延迟下载等。强调:配置文件的选项必须大写否则视为无效,正确写法USER_AGENT='xxxx'
├── pipelines.py/ # 数据处理行为,如:一般结构化的数据持久化
├── spiders/ # 爬虫目录,如:创建文件,编写爬虫规则
├── __init__.py/
├── 爬虫文件1.py # 项目开发时的本地配置
├── 爬虫文件2.py # 项目开发时的本地配置
......
"""
4、数据解析
xpath解析和css解析
#1 //与/
response.xpath('//body/a/')#
response.css('div a::text')
response.xpath('//body/a') #开头的//代表从整篇文档中寻找,body之后的/代表body的儿子
>>> []
response.xpath('//body//a') #开头的//代表从整篇文档中寻找,body之后的//代表body的子子孙孙
>>> [<Selector xpath='//body//a' data='<a href="image1.html">Name: My image 1 <'>, <Selector xpath='//body//a' data='<a href="image2.html">Name: My image 2 <'>, <Selector xpath='//body//a' data='<a href="
image3.html">Name: My image 3 <'>, <Selector xpath='//body//a' data='<a href="image4.html">Name: My image 4 <'>, <Selector xpath='//body//a' data='<a href="image5.html">Name: My image 5 <'>]
#2 text 获取文本值
response.xpath('//body//a/text()')
response.css('body a::text')
'''
3、extract与extract_first:从selector对象中解出内容
extract()取出的是一个列表
extract_first()取出的是列表第一个元素
'''
response.xpath('//div/a/text()').extract()
>>> ['Name: My image 1 ', 'Name: My image 2 ', 'Name: My image 3 ', 'Name: My image 4 ', 'Name: My image 5 ']
response.css('div a::text').extract()
>>> ['Name: My image 1 ', 'Name: My image 2 ', 'Name: My image 3 ', 'Name: My image 4 ', 'Name: My image 5 ']
response.xpath('//div/a/text()').extract_first()
>>> 'Name: My image 1 '
response.css('div a::text').extract_first()
>>> 'Name: My image 1 '
#4、属性:xpath的属性加前缀@
response.xpath('//div/a/@href').extract_first()
>>> 'image1.html'
response.css('div a::attr(href)').extract_first()
>>> 'image1.html'
#4、嵌套查找
response.xpath('//div').css('a').xpath('@href').extract_first()
>>> 'image1.html'
#5、设置默认值
response.xpath('//div[@id="xxx"]').extract_first(default="not found")
>>> 'not found'
#4、按照属性查找
response.xpath('//div[@id="images"]/a[@href="image3.html"]/text()').extract()
response.css('#images a[@href="image3.html"]/text()').extract()
#5、按照属性模糊查找
response.xpath('//a[contains(@href,"image")]/@href').extract()
response.css('a[href*="image"]::attr(href)').extract()
response.xpath('//a[contains(@href,"image")]/img/@src').extract()
response.css('a[href*="imag"] img::attr(src)').extract()
response.xpath('//*[@href="image1.html"]')
response.css('*[href="image1.html"]')
#6、正则表达式
response.xpath('//a/text()').re(r'Name: (.*)')
response.xpath('//a/text()').re_first(r'Name: (.*)')
#7、xpath相对路径
res=response.xpath('//a[contains(@href,"3")]')[0]
res.xpath('img')
>>> [<Selector xpath='img' data='<img src="image3_thumb.jpg">'>]
res.xpath('./img')
>>> [<Selector xpath='./img' data='<img src="image3_thumb.jpg">'>]
res.xpath('.//img')
>>> [<Selector xpath='.//img' data='<img src="image3_thumb.jpg">'>]
res.xpath('//img') #这就是从头开始扫描
>>> [<Selector xpath='//img' data='<img src="image1_thumb.jpg">'>, <Selector xpath='//img' data='<img src="image2_thumb.jpg">'>, <Selector xpath='//img' data='<img src="image3_thumb.jpg">'>, <Selector xpa
th='//img' data='<img src="image4_thumb.jpg">'>, <Selector xpath='//img' data='<img src="image5_thumb.jpg">'>]
#8、带变量的xpath
response.xpath('//div[@id=$xxx]/a/text()',xxx='images').extract_first()
>>> 'Name: My image 1 '
response.xpath('//div[count(a)=$yyy]/@id',yyy=5).extract_first() #求有5个a标签的div的id
>>> 'images'
5、配置
# robosttxt 协议设置为false
ROBOTSTXT_OBEY = False
# 日志配置成错误级别
LOG_LEVEL='ERROR'
# 请求头配置
USER_AGENT = '浏览器标识'
'''
提升scrapy爬取数据效率的配置
#1 增加并发:
默认scrapy开启的并发线程为32个,可以适当进行增加。在settings配置文件中修改CONCURRENT_REQUESTS = 100值为100,并发设置成了为100。
#2 降低日志级别:
在运行scrapy时,会有大量日志信息的输出,为了减少CPU的使用率。可以设置log输出信息为INFO或者ERROR即可。在配置文件中编写:LOG_LEVEL = ‘INFO’
# 3 禁止cookie:
如果不是真的需要cookie,则在scrapy爬取数据时可以禁止cookie从而减少CPU的使用率,提升爬取效率。在配置文件中编写:COOKIES_ENABLED = False
# 4禁止重试:
对失败的HTTP进行重新请求(重试)会减慢爬取速度,因此可以禁止重试。在配置文件中编写:RETRY_ENABLED = False
# 5 减少下载超时:
如果对一个非常慢的链接进行爬取,减少下载超时可以能让卡住的链接快速被放弃,从而提升效率。在配置文件中进行编写:DOWNLOAD_TIMEOUT = 10 超时时间为10s
'''
'''
其他配置可以根据需要进行相应配置,也可以参考下面的其他配置
'''
#==>第一部分:基本配置<===
#1、项目名称,默认的USER_AGENT由它来构成,也作为日志记录的日志名
BOT_NAME = 'Amazon'
#2、爬虫应用路径
SPIDER_MODULES = ['Amazon.spiders']
NEWSPIDER_MODULE = 'Amazon.spiders'
#3、客户端User-Agent请求头
#USER_AGENT = 'Amazon (+http://www.yourdomain.com)'
#4、是否遵循爬虫协议
# Obey robots.txt rules
ROBOTSTXT_OBEY = False
#5、是否支持cookie,cookiejar进行操作cookie,默认开启
#COOKIES_ENABLED = False
#6、Telnet用于查看当前爬虫的信息,操作爬虫等...使用telnet ip port ,然后通过命令操作
#TELNETCONSOLE_ENABLED = False
#TELNETCONSOLE_HOST = '127.0.0.1'
#TELNETCONSOLE_PORT = [6023,]
#7、Scrapy发送HTTP请求默认使用的请求头
#DEFAULT_REQUEST_HEADERS = {
# 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
# 'Accept-Language': 'en',
#}
#===>第二部分:并发与延迟<===
#1、下载器总共最大处理的并发请求数,默认值16
#CONCURRENT_REQUESTS = 32
#2、每个域名能够被执行的最大并发请求数目,默认值8
#CONCURRENT_REQUESTS_PER_DOMAIN = 16
#3、能够被单个IP处理的并发请求数,默认值0,代表无限制,需要注意两点
#I、如果不为零,那CONCURRENT_REQUESTS_PER_DOMAIN将被忽略,即并发数的限制是按照每个IP来计算,而不是每个域名
#II、该设置也影响DOWNLOAD_DELAY,如果该值不为零,那么DOWNLOAD_DELAY下载延迟是限制每个IP而不是每个域
#CONCURRENT_REQUESTS_PER_IP = 16
#4、如果没有开启智能限速,这个值就代表一个规定死的值,代表对同一网址延迟请求的秒数
#DOWNLOAD_DELAY = 3
#===>第三部分:智能限速/自动节流:AutoThrottle extension<===
#一:介绍
from scrapy.contrib.throttle import AutoThrottle #http://scrapy.readthedocs.io/en/latest/topics/autothrottle.html#topics-autothrottle
设置目标:
1、比使用默认的下载延迟对站点更好
2、自动调整scrapy到最佳的爬取速度,所以用户无需自己调整下载延迟到最佳状态。用户只需要定义允许最大并发的请求,剩下的事情由该扩展组件自动完成
#二:如何实现?
在Scrapy中,下载延迟是通过计算建立TCP连接到接收到HTTP包头(header)之间的时间来测量的。
注意,由于Scrapy可能在忙着处理spider的回调函数或者无法下载,因此在合作的多任务环境下准确测量这些延迟是十分苦难的。 不过,这些延迟仍然是对Scrapy(甚至是服务器)繁忙程度的合理测量,而这扩展就是以此为前提进行编写的。
#三:限速算法
自动限速算法基于以下规则调整下载延迟
#1、spiders开始时的下载延迟是基于AUTOTHROTTLE_START_DELAY的值
#2、当收到一个response,对目标站点的下载延迟=收到响应的延迟时间/AUTOTHROTTLE_TARGET_CONCURRENCY
#3、下一次请求的下载延迟就被设置成:对目标站点下载延迟时间和过去的下载延迟时间的平均值
#4、没有达到200个response则不允许降低延迟
#5、下载延迟不能变的比DOWNLOAD_DELAY更低或者比AUTOTHROTTLE_MAX_DELAY更高
#四:配置使用
#开启True,默认False
AUTOTHROTTLE_ENABLED = True
#起始的延迟
AUTOTHROTTLE_START_DELAY = 5
#最小延迟
DOWNLOAD_DELAY = 3
#最大延迟
AUTOTHROTTLE_MAX_DELAY = 10
#每秒并发请求数的平均值,不能高于 CONCURRENT_REQUESTS_PER_DOMAIN或CONCURRENT_REQUESTS_PER_IP,调高了则吞吐量增大强奸目标站点,调低了则对目标站点更加”礼貌“
#每个特定的时间点,scrapy并发请求的数目都可能高于或低于该值,这是爬虫视图达到的建议值而不是硬限制
AUTOTHROTTLE_TARGET_CONCURRENCY = 16.0
#调试
AUTOTHROTTLE_DEBUG = True
CONCURRENT_REQUESTS_PER_DOMAIN = 16
CONCURRENT_REQUESTS_PER_IP = 16
#===>第四部分:爬取深度与爬取方式<===
#1、爬虫允许的最大深度,可以通过meta查看当前深度;0表示无深度
# DEPTH_LIMIT = 3
#2、爬取时,0表示深度优先Lifo(默认);1表示广度优先FiFo
# 后进先出,深度优先
# DEPTH_PRIORITY = 0
# SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleLifoDiskQueue'
# SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.LifoMemoryQueue'
# 先进先出,广度优先
# DEPTH_PRIORITY = 1
# SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleFifoDiskQueue'
# SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.FifoMemoryQueue'
#3、调度器队列
# SCHEDULER = 'scrapy.core.scheduler.Scheduler'
# from scrapy.core.scheduler import Scheduler
#4、访问URL去重
# DUPEFILTER_CLASS = 'step8_king.duplication.RepeatUrl'
#===>第五部分:中间件、Pipelines、扩展<===
#1、Enable or disable spider middlewares
# See http://scrapy.readthedocs.org/en/latest/topics/spider-middleware.html
#SPIDER_MIDDLEWARES = {
# 'Amazon.middlewares.AmazonSpiderMiddleware': 543,
#}
#2、Enable or disable downloader middlewares
# See http://scrapy.readthedocs.org/en/latest/topics/downloader-middleware.html
DOWNLOADER_MIDDLEWARES = {
# 'Amazon.middlewares.DownMiddleware1': 543,
}
#3、Enable or disable extensions
# See http://scrapy.readthedocs.org/en/latest/topics/extensions.html
#EXTENSIONS = {
# 'scrapy.extensions.telnet.TelnetConsole': None,
#}
#4、Configure item pipelines
# See http://scrapy.readthedocs.org/en/latest/topics/item-pipeline.html
ITEM_PIPELINES = {
# 'Amazon.pipelines.CustomPipeline': 200,
}
#===>第六部分:缓存<===
"""
1. 启用缓存
目的用于将已经发送的请求或相应缓存下来,以便以后使用
from scrapy.downloadermiddlewares.httpcache import HttpCacheMiddleware
from scrapy.extensions.httpcache import DummyPolicy
from scrapy.extensions.httpcache import FilesystemCacheStorage
"""
# 是否启用缓存策略
# HTTPCACHE_ENABLED = True
# 缓存策略:所有请求均缓存,下次在请求直接访问原来的缓存即可
# HTTPCACHE_POLICY = "scrapy.extensions.httpcache.DummyPolicy"
# 缓存策略:根据Http响应头:Cache-Control、Last-Modified 等进行缓存的策略
# HTTPCACHE_POLICY = "scrapy.extensions.httpcache.RFC2616Policy"
# 缓存超时时间
# HTTPCACHE_EXPIRATION_SECS = 0
# 缓存保存路径
# HTTPCACHE_DIR = 'httpcache'
# 缓存忽略的Http状态码
# HTTPCACHE_IGNORE_HTTP_CODES = []
# 缓存存储的插件
# HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
#===>第七部分:线程池<===
REACTOR_THREADPOOL_MAXSIZE = 10
#Default: 10
#scrapy基于twisted异步IO框架,downloader是多线程的,线程数是Twisted线程池的默认大小(The maximum limit for Twisted Reactor thread pool size.)
#关于twisted线程池:
http://twistedmatrix.com/documents/10.1.0/core/howto/threading.html
#线程池实现:twisted.python.threadpool.ThreadPool
twisted调整线程池大小:
from twisted.internet import reactor
reactor.suggestThreadPoolSize(30)
6、scrapy持久化存储
(1)基于终端指令
'''
要求:只可以将parse方法的返回值存储到本地的文本文件中
注意:持久化存储对应的文本文件的类型只可以为:‘json','jsonlines','jl',’csv',‘xml',’marshal','pickle‘
指令:scrapy crawl 爬虫文件名 -o filePath
优点:简介高效便捷
缺点:局限性比较强(数据只可以存储到指定后缀的文本文件中)
'''
(2)基于管道
'''
items.py: 数据结构模版文件,定义数据属性
pipelines.py:管道文件,用来接收数据(items),进行持久化操作
持久化流程:
1.爬虫文件爬取到数据后,需要将数据封装到items对象中。需要导入items.py文件里面的类
2.使用yield关键字将items对象提交给pipelines管道进行持久化操作。
3.在管道文件中的process_item方法中接收爬虫文件提交过来的item对象,然后编写持久化存储的代码将item对象中存储的数据进行持久化存储
4.settings.py配置文件中开启管道
'''
# pipline.py 管道类
class BlogPostPipeline:
# 开始
def open_spider(self,spider):
self.conn=pymysql.connect(host='127.0.0.1', user='root', password="root",database='pcdb', charset='utf8mb4',port=3306,)
# 持久化过程
def process_item(self,item,spider):
cursor=self.conn.cursor()
sql = 'insert into article (title,url,content,`desc`) values (%s,%s,%s,%s)'
cursor.execute(sql, [item['title'], item['url'], item['content'], item['desc']])
self.conn.commit()
return item
# 结束
def close_spider(self,spider):
self.conn.close()
print('爬虫结束了')
#items文件
import scrapy
class PostItem(scrapy.Item):
title=scrapy.Field()
url=scrapy.Field()
desc=scrapy.Field()
content=scrapy.Field()
图片存储 :
1、在setting文件配置:IMAGES_STORE = ‘./imgs’:表示最终图片存储的目录,并在ITEM_PIPELINES中增加管道类中新增的类
2、重写一个管道类(继承ImagesPipeline)
3、基于ImagesPipeLine类的管道类三个方法:get_media_request()、file_path()、tiem_completed()
from scrapy.pipelines.images import ImagesPipeline
# 继承ImagesPipeline
class myPipeline(ImagesPipeline):
def get_media_requests(self, item, info):
print('下载开始')
return scrapy.Request(item['img_url'],headers={'referer':item['img_referer']},meta={'item':item})
def file_path(self, request, response=None, info=None):
item=request.meta['item']
url = request.url
file_name=url.split('/')[-1]
return file_name
def item_completed(self, results, item, info):
print('下载结束')
return item
7、scrapy中间件(下载中间件)
(1)拦截请求:修改user-agent、请求头,加入代理ip(可以方法process_exception方法,或者process_request方法)等
(2)拦截响应:修改响应对象或者响应数据(例子:比如一些动态加载的页面)
def process_request(self, request, spider):
#1、更改请求头的user-agent
from fake_useragent import UserAgent
ua=UserAgent()
request.headers['User-Agent']=ua.random
# 2、设置代理ip
import requests
r = requests.get('http://127.0.0.1:5010/get/')
proxy=r.json().get('proxy')
request.meta['download_timeout'] = 5
request.meta["proxy"] = 'http://'+proxy
print(request)
return None
# 在中间件使用selenium处理ajax请求问题
def process_response(self, request, response, spider):
from scrapy.http import HtmlResponse
spider.bro.get('https://dig.chouti.com/')
response = HtmlResponse(url='https://dig.chouti.com/', body=spider.bro.page_source.encode('utf-8'),
request=request)
return response
三、去重源码解析
1、BaseDupeFilter源码
class BaseDupeFilter:
@classmethod
def from_settings(cls, settings):
return cls()
# 去重方法
def request_seen(self, request):
return False
def open(self): # can return deferred
pass
def close(self, reason): # can return a deferred
pass
def log(self, request, spider): # log that a request has been filtered
pass
2、RFPDupeFilter源码
class RFPDupeFilter(BaseDupeFilter):
"""Request Fingerprint duplicates filter"""
def __init__(self, path=None, debug=False):
self.file = None
self.fingerprints = set() #集合
self.logdupes = True
self.debug = debug
self.logger = logging.getLogger(__name__)
if path:
self.file = open(os.path.join(path, 'requests.seen'), 'a+')
self.file.seek(0)
self.fingerprints.update(x.rstrip() for x in self.file)
@classmethod
def from_settings(cls, settings):
debug = settings.getbool('DUPEFILTER_DEBUG')
return cls(job_dir(settings), debug)
# 去重的主要方法
def request_seen(self, request):
'''
from scrapy.utils.request import request_fingerprint
利用request_fingerprint对请求取指纹(md5)
判断如果在集合中,返回True,不在则添加到集合
'''
fp = self.request_fingerprint(request)
if fp in self.fingerprints:
return True
self.fingerprints.add(fp)
if self.file:
self.file.write(fp + 'n')
def request_fingerprint(self, request):
return request_fingerprint(request)
def close(self, reason):
if self.file:
self.file.close()
def log(self, request, spider):
if self.debug:
msg = "Filtered duplicate request: %(request)s (referer: %(referer)s)"
args = {'request': request, 'referer': referer_str(request)}
self.logger.debug(msg, args, extra={'spider': spider})
elif self.logdupes:
msg = ("Filtered duplicate request: %(request)s"
" - no more duplicates will be shown"
" (see DUPEFILTER_DEBUG to show all duplicates)")
self.logger.debug(msg, {'request': request}, extra={'spider': spider})
self.logdupes = False
spider.crawler.stats.inc_value('dupefilter/filtered', spider=spider)
3、通过BaseDupeFilter和RFPDupeFilter对比,我们可以看出去重规则主要在request_seen()方法中,则我们可以仿照写一个自定义去重类
(1) 新建一个去重文件dupfilter.py
from scrapy.dupefilters import BaseDupeFilter
class UrlFilter(BaseDupeFilter):
def __init__(self):
self.visited = set() #或者放到数据库
@classmethod
def from_settings(cls, settings):
return cls()
def request_seen(self, request):
if request.url in self.visited:
return True
self.visited.add(request.url)
def open(self): # can return deferred
pass
def close(self, reason): # can return a deferred
pass
def log(self, request, spider): # log that a request has been filtered
pass
(2) 在配置文件settings中
DUPEFILTER_CLASS = '项目名.dupfilter.UrlFilter'
四、scrapy-redis分布式爬虫
1、介绍
scrapy-redis是在原来的scrapy的基础上,重写Scheduler,让调度器到共享队列中取Request(该request请求时去重的)。去重主要是利用了redis的集合类型
2、使用方法
(1)安装scrapy-redis模块,pip install scrapy-redis
(2)配置settings文件
# scheduler调度器配置,参考Scheduler源码
class Scheduler(object):
"""
在scrapy中settings的配置信息
--------
SCHEDULER_PERSIST : bool (default: False)
关闭的时候,是否保留redis队列(也就是起始地址).
SCHEDULER_FLUSH_ON_START : bool (default: False)
是否在启动时刷新redis队列。
SCHEDULER_IDLE_BEFORE_CLOSE : int (default: 0)
如果没有收到任何消息,在关闭之前要等待多少秒(超时时间)。
SCHEDULER_QUEUE_KEY : str
调度器中请求存放在redis中的key
SCHEDULER_QUEUE_CLASS : str
调度程序队列类s.
SCHEDULER_DUPEFILTER_KEY : str
去重规则中,在redis中保存对应的key值.
SCHEDULER_DUPEFILTER_CLASS : str
去重规则对应的处理类
SCHEDULER_SERIALIZER : str
对保存到redis中的数据进行序列化.默认使用pickle
# SCHEDULER_SERIALIZER ="scrapy_redis.picklecompat"
"""
# Redis配置,参考connection源码
def get_redis_from_settings(settings):
# ----------------
# 在settings中的配置
# ----------------
REDIS_URL : str, optional
# 例子:REDIS_URL='redis://:12345@127.0.0.1:6379'
#redis的连接url,优先去url配置(等同的hostpost的配置),.
#注意:配置了url,就不用配置主机号跟端口了
REDIS_HOST : str, optional
#redis的主机名.
REDIS_PORT : str, optional
#redis的端口.
REDIS_ENCODING : str, optional
#redis的编码.
REDIS_PARAMS : dict, optional
#redis的其他参数,是字典类型.
#如: REDIS_PARAMS = {'password':'12345'}
# 持久化 配置,参考pipeline源码
class RedisPipeline(object):
Settings
--------
REDIS_ITEMS_KEY : str
# redis关键字
REDIS_ITEMS_SERIALIZER : str
# 序列化函数
# 参考spiders源码配置
class RedisSpider(RedisMixin, Spider):
Settings
--------
REDIS_START_URLS_KEY : str (default: "<spider.name>:start_urls")
# 起始rurl从对应的redis的key.
REDIS_START_URLS_BATCH_SIZE : int (deprecated by CONCURRENT_REQUESTS)
# 每次尝试从reids获取的url数,默认从配置中获取.
REDIS_START_URLS_AS_SET : bool (default: False)
# 获取起始URL时,如果为True,则使用self.server.spop;如果为False,则使用self.server.lpop
REDIS_ENCODING : str (default: "utf-8")
#编码
(3) 参考案例(在原来的scrapy的源码上进行修改)
'''
spdies文件
继承RedisSpider
'''
from scrapy_redis.spiders import RedisSpider
class BlogSpider(RedisSpider):
name = 'redis_blog'
redis_key = 'myspider:start_urls'
def parse(self, response):
print('---redis-blog-----')
article_list = response.xpath('//div[@id="post_list"]/article')
....
#settings文件配置
#redis的连接
REDIS_HOST='localhost'
REDIS_PORT=6379
# REDIS_PASSWD='12345'
REDIS_PARAMS = {'password':'12345'}
# REDIS_URL='redis://:12345@127.0.0.1:6379'
# from scrapy_redis.scheduler import Scheduler
DUPEFILTER_CLASS="scrapy_redis.dupefilter.RFPDupeFilter"
SCHEDULER = 'scrapy_redis.scheduler.Scheduler'
SCHEDULER_PERSIST = True
最后,在redis的数据库中插入一个起始地址:lpush myspider:start_urls https://www.cnblogs.com/ 就可以运行了
最后
以上就是优美茉莉为你收集整理的爬虫之scrapy、scrapy-redis的全部内容,希望文章能够帮你解决爬虫之scrapy、scrapy-redis所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复