Scrapy CrawlSpider了解
scrapy通用爬虫
CrawlSpider它是Spider的派生类,Spider类的设计原则是只爬取start_url列表中的网页,而CrawlSpider类定义了一些规则Rule来提供跟进链接的方便的机制,从爬取的网页结果中获取链接并继续爬取的工作.
源码:
class CrawlSpider(Spider):
rules = ()
def __init__(self, *a, **kw):
super(CrawlSpider, self).__init__(*a, **kw)
self._compile_rules()
#首先调用parse()来处理start_urls中返回的response对象
#parse()则将这些response对象传递给了_parse_response()函数处理,并设置回调函数为parse_start_url()
#设置了跟进标志位True
#parse将返回item和跟进了的Request对象
def parse(self, response):
return self._parse_response(response, self.parse_start_url, cb_kwargs={}, follow=True)
#处理start_url中返回的response,需要重写
def parse_start_url(self, response):
return []
def process_results(self, response, results):
return results
#从response中抽取符合任一用户定义'规则'的链接,并构造成Resquest对象返回
def _requests_to_follow(self, response):
if not isinstance(response, HtmlResponse):
return
seen = set()
#抽取之内的所有链接,只要通过任意一个'规则',即表示合法
for n, rule in enumerate(self._rules):
links = [l for l in rule.link_extractor.extract_links(response) if l not in seen]
#使用用户指定的process_links处理每个连接
if links and rule.process_links:
links = rule.process_links(links)
#将链接加入seen集合,为每个链接生成Request对象,并设置回调函数为_repsonse_downloaded()
for link in links:
seen.add(link)
#构造Request对象,并将Rule规则中定义的回调函数作为这个Request对象的回调函数
r = Request(url=link.url, callback=self._response_downloaded)
r.meta.update(rule=n, link_text=link.text)
#对每个Request调用process_request()函数。该函数默认为indentify,即不做任何处理,直接返回该Request.
yield rule.process_request(r)
#处理通过rule提取出的连接,并返回item以及request
def _response_downloaded(self, response):
rule = self._rules[response.meta['rule']]
return self._parse_response(response, rule.callback, rule.cb_kwargs, rule.follow)
#解析response对象,会用callback解析处理他,并返回request或Item对象
def _parse_response(self, response, callback, cb_kwargs, follow=True):
#首先判断是否设置了回调函数。(该回调函数可能是rule中的解析函数,也可能是 parse_start_url函数)
#如果设置了回调函数(parse_start_url()),那么首先用parse_start_url()处理response对象,
#然后再交给process_results处理。返回cb_res的一个列表
if callback:
#如果是parse调用的,则会解析成Request对象
#如果是rule callback,则会解析成Item
cb_res = callback(response, **cb_kwargs) or ()
cb_res = self.process_results(response, cb_res)
for requests_or_item in iterate_spider_output(cb_res):
yield requests_or_item
#如果需要跟进,那么使用定义的Rule规则提取并返回这些Request对象
if follow and self._follow_links:
#返回每个Request对象
for request_or_item in self._requests_to_follow(response):
yield request_or_item
def _compile_rules(self):
def get_method(method):
if callable(method):
return method
elif isinstance(method, basestring):
return getattr(self, method, None)
self._rules = [copy.copy(r) for r in self.rules]
for rule in self._rules:
rule.callback = get_method(rule.callback)
rule.process_links = get_method(rule.process_links)
rule.process_request = get_method(rule.process_request)
def set_crawler(self, crawler):
super(CrawlSpider, self).set_crawler(crawler)
self._follow_links = crawler.settings.getbool('CRAWLSPIDER_FOLLOW_LINKS', True)
通过下面的命令可以快速创建 CrawlSpider模板 的代码:
scrapy genspider -t crawl 爬虫文件 域名
rules
CrawlSpider使用rules属性来决定爬虫的爬取规则,并将匹配后的url请求提交给引擎,完成后续的爬取工作。
在rules中包含一个或多个Rule对象,每个Rule对爬取网站的动作定义了某种特定操作,比如提取当前相应内容里的特定链接,是否对提取的链接跟进爬取,对提交的请求设置回调函数等。
class scrapy.spiders.Rule(
link_extractor,
callback = None,
cb_kwargs = None,
follow = None,
process_links = None,
process_request = None
)
link_extractor:是一个Link Extractor对象,用于定义需要提取的链接。
callback: 从link_extractor中每获取到链接得到Responses时,会调用参数所指定的值作为回调函数,该回调函数接收一个response作为其一个参数。
follow:是一个布尔(boolean)值,指定了根据该规则从response提取的链接是否需要跟进。如果callback为None,follow 默认设置为True ,否则默认为False。
process_links:指定spider中哪个的函数将会被调用,从link_extractor中获取到链接列表时将会调用该函数。该方法主要用来过滤。
process_request:指定处理函数,根据该Rule提取到的每个Request时,该函数将会被调用,可以对Request进行处理,该函数必须返回Request或者None
class scrapy.linkextractors.LinkExtractor(
allow = (),
deny = (),
allow_domains = (),
deny_domains = (),
deny_extensions = None,
restrict_xpaths = (),
tags = ('a','area'),
attrs = ('href'),
canonicalize = True,
unique = True,
process_value = None
)
allow:满足括号中“正则表达式”的URL会被提取,如果为空,则全部匹配。
deny:满足括号中“正则表达式”的URL一定不提取(优先级高于allow)。
allow_domains:会提取的链接的domains。
deny_domains:一定不会被提取链接的domains。
restrict_xpaths:使用xpath表达式,和allow共同作用过滤链接。
下载中间件
下载中间件:处于引擎与下载器之间
反爬措施
- 基于请求头的反爬(合理构建请求头)
- 基于cookie的反爬(cookie池,文件存储,数据库存储)(如何获取cookies,如何验证cookie,怎么进行模拟登陆)
- 基于ip的反爬(代理,代理的原理,代理怎么获取?代理怎么检测?代理池?)
- 基于动态加载的网页?(ajax,js,jq)(selenium?无头.有头浏览器)
- 关于数据加密?(js,aPP,web网页,)
@classmethod
def from_crawler(cls, crawler):
# This method is used by Scrapy to create your spiders.
s = cls()
crawler.signals.connect(s.spider_opened, signal=signals.spider_opened)
return s
def process_request(self, request, spider):
# 所有的request请求在交给下载器之前都会交给进入方法
# Called for each request that goes through the downloader middleware.
# Must either:
# - return None: continue processing this request
# - or return a Response object
# - or return a Request object
# - or raise IgnoreRequest: process_exception() methods of
# installed downloader middleware will be called
return None
def process_response(self, request, response, spider):
# 响应结果都会经过这个方法
# Called with the response returned from the downloader.
# Must either;
# - return a Response object
# - return a Request object
# - or raise IgnoreRequest
return response
def process_exception(self, request, exception, spider):
# 处理错误
# Called when a download handler or a process_request()
# (from other downloader middleware) raises an exception.
# Must either:
# - return None: continue processing this exception
# - return a Response object: stops process_exception() chain
# - return a Request object: stops process_exception() chain
pass
def spider_opened(self, spider):
spider.logger.info('Spider opened: %s' % spider.name)
scrapy.settings.py文件中的配置:
# 项目名称
BOT_NAME = 'downloadmiddler'
# 爬虫存储的文件路径
SPIDER_MODULES = ['downloadmiddler.spiders']
# 创建爬虫文件的模板,创建好的文件存放在这个目录下
NEWSPIDER_MODULE = 'downloadmiddler.spiders'
# 模拟浏览器请求
USER_AGENT = 'downloadmiddler (+http://www.yourdomain.com)'
# 设置是否遵守robots协议,默认TRUE遵守
ROBOTSTXT_OBEY = False
# 设置请求最大的并发数量(下载器处理的最大数量),默认16个
CONCURRENT_REQUESTS = 32
# 设置请求下载延时,默认为0.
DOWNLOAD_DELAY = 3
# 设置网站的最大并发请求数量,默认8个
CONCURRENT_REQUESTS_PER_DOMAIN = 16
# 设置某个IP下的最大并发请求数量,默认0个,如果非0,要注意网站并发就无效,请求的并发数量将只针对于IP,如果非零,下载延时则针对IP,而不是网站
CONCURRENT_REQUESTS_PER_IP = 16
# 是否携带cookie,默认为TRUE
COOKIES_ENABLED = False
# 是一个终端的扩展插件
TELNETCONSOLE_ENABLED = False
# 设置默认的请求头(cookie不要放在这里)
DEFAULT_REQUEST_HEADERS = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
}
# 设置和激活爬虫中间键
SPIDER_MIDDLEWARES = {
'downloadmiddler.middlewares.DownloadmiddlerSpiderMiddleware': 543,
}
# 设置和激活下载中间键
DOWNLOADER_MIDDLEWARES = {
'downloadmiddler.middlewares.SeleniumDownloadMiddlerware': 543
}
# 设置和激活管道文件,数字表示优先级,越小越高
ITEM_PIPELINES = {
'downloadmiddler.pipelines.DownloadmiddlerPipeline': 300,
}
# 设置扩展
EXTENSIONS = {
'scrapy.extensions.telnet.TelnetConsole': None,
}
# 自动限速的扩展,上一个请求和下一个请求之间的时间是不固定的,默认情况下,自动限速扩是没有打开的False
AUTOTHROTTLE_ENABLED = True
# 初始的下载延时,默认为5秒
AUTOTHROTTLE_START_DELAY = 5
# 最大下载延时
AUTOTHROTTLE_MAX_DELAY = 60
# 针对于网站的最大的并行请求数量
AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
# debug调试模式,默认为False
AUTOTHROTTLE_DEBUG = False
# 设置数据缓存,默认不开启
HTTPCACHE_ENABLED = True
# 设置缓存的超时时间,为0代表永久有效
HTTPCACHE_EXPIRATION_SECS = 0
# 设置缓存数据的存储路径
HTTPCACHE_DIR = 'httpcache'
# 忽略某些状态码的请求结果
HTTPCACHE_IGNORE_HTTP_CODES = []
# 开启缓存的一个扩展插件
HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
# cookie debug模式,默认为Fales
COOKIES_DEBUG = True
# 关于日志信息设置
Scrapy提供5层logging级别:
CRITICAL - 严重错误(critical)
ERROR - 一般错误(regular errors)
WARNING - 警告信息(warning messages)
INFO - 一般信息(informational messages)
DEBUG - 调试信息(debugging messages)
LOG_ENABLED 默认: True,启用logging
LOG_ENCODING 默认: 'utf-8',logging使用的编码
LOG_FILE 默认: None,在当前目录里创建logging输出文件的文件名
LOG_LEVEL 默认: 'DEBUG',log的最低级别
LOG_STDOUT 默认: False 如果为 True,进程所有的标准输出(及错误)将会被重定向到log中。例如,执行 print "hello" ,其将会在Scrapy log中显示
scrapy-redis
pip3 install scrapy-redis
修改设置文件
-
设置去重组件,使用的是scrapy_redis的去重组件,而不再使用scrapy框架自己的去重组件
DUPEFILTER_CLASS = "scrapy_redis.dupefilter.RFPDupeFilter" -
设置调度器,使用的是scrapy_redis重写的调度器,而不再使用scrapy框架自己的调度器
SCHEDULER = "scrapy_redis.scheduler.Scheduler" -
可以实现断点爬取(请求的记录不会丢失,会存储在redis数据库中,不会清除redis的任务队列)
SCHEDULER_PERSIST = True -
设置任务队列的模式(三选一)
SpiderPriorityQueue是scrapy_redis默认使用的队列模式(有自己的优点)
SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderPriorityQueue"
使用了队列的形式,任务先进先出
SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderQueue"
采用了核的形式,任务先进后出
SCHEDULER_QUEUE_CLASS = "scrapy_redis.queue.SpiderStack"
- 实现这个管道,可以将爬虫端获取的item数据,统一保存在redis数据库中
'scrapy_redis.pipelines.RedisPipeline': 400,
- 数据库的相关信息
指定要存储的redis数据库的主机ip
REDIS_HOST = '127.0.0.1'
指定redis数据库主机的端口
REDIS_PORT = 6379
"xcfCrawlSpider:requests":存储的是请求的request对象
"xcfCrawlSpider:items":存储的爬虫端获取的items数据
"xcfCrawlSpider:dupefilter":存储的指纹(为了实现去重)
127.0.0.1:6379> type xcfCrawlSpider:requests
zset
127.0.0.1:6379> type xcfCrawlSpider:items
list
127.0.0.1:6379> type xcfCrawlSpider:dupefilter
set
第一中情况:只设置settings.py文件,并没有实现分布式,知识使用了sctapy_redis的数据存储和去重功能
第二中情况:实现通用爬虫的分布式爬虫
from scrapy_redis.spiders import RedisCrawlSpider
#继承制:RedisCrawlSpider
class MyCrawler(RedisCrawlSpider):
"""Spider that reads urls from redis queue (myspider:start_urls)."""
name = 'mycrawler_redis'
allowed_domains = ['dmoz.org']
#缺少了start_url,多了redis_key:根据redis_key从redis
#数据库中获取任务
redis_key = 'mycrawler:start_urls'
启动爬虫:scrapy crawl 爬虫名称
现象:爬虫处于等待状态
需要设置起始任务:
lpush mycrawler:start_urls 目标url
第三中情况:实现scrpy.spider爬虫的分布式爬虫
from scrapy_redis.spiders import RedisSpider
#继承制:RedisSpider
class MyCrawler(RedisSpider):
"""Spider that reads urls from redis queue (myspider:start_urls)."""
name = 'mycrawler_redis'
allowed_domains = ['dmoz.org']
#缺少了start_url,多了redis_key:根据redis_key从redis
#数据库中获取任务
redis_key = 'mycrawler:start_urls'
启动爬虫:scrapy crawl 爬虫名称
现象:爬虫处于等待状态
需要设置起始任务:
lpush mycrawler:start_urls 目标url
网友评论