四.配置文件(settings.py)
参见:https://www.cnblogs.com/fengf233/p/11400262.html
https://www.cnblogs.com/longyunfeigu/p/9494408.html
################################基本配置#################################
#爬虫的名字:会被放在USER_AGENT(当前客户端)中提交出去,网站可以据此筛选
BOT_NAME = 'scrapy1'
#查找爬虫文件的路径:
SPIDER_MODULES = ['scrapy1.spiders']#默认值
#创建爬虫文件时的路径:
NEWSPIDER_MODULE = 'scrapy1.spiders'#默认值
#当前客户端(即请求头中的USER-AGENT字段):
USER_AGENT = 'scrapy1 (+http://www.yourdomain.com)'#默认值
#实际使用时,会伪装成浏览器发出的请求
#是否遵守爬虫协议:
ROBOTSTXT_OBEY = True#默认值
#为True时,只在网站允许时爬虫;为False,则忽略网站的规则
#是否自动爬取Cookie:
COOKIES_ENABLED = True#默认值
#为True时才能使用CookieJar进行解析
#是否对Cookie使用调试模式(即日志中是否包含Cookie):
COOKIES_DEBUG = False#默认值
#默认的请求头:
DEFAULT_REQUEST_HEADERS = {#默认值
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en',
}
##############################查看/操作爬虫##############################
#是否允许查看爬虫当前的状态:
TELNETCONSOLE_ENABLED = False
#在终端中使用telnet <IP> <port>进入,然后使用命令查看
#使用扩展scrapy.extensions.telnet.TelnetConsole查看
TELNETCONSOLE_HOST = '127.0.0.1'
TELNETCONSOLE_PORT = [6023,]
###############################并发与延迟################################
#最大并发请求数(0为无限制):
CONCURRENT_REQUESTS = 16#默认值
#下载延迟(即对1个域名的2次请求间的间隔;单位为s):
DOWNLOAD_DELAY = 3
#因为有些网站在访问过于频繁时会禁止访问
#针对单个域名/IP的最大并发数(0为无限制):
CONCURRENT_REQUESTS_PER_DOMAIN = 8#默认值
CONCURRENT_REQUESTS_PER_IP = 0#默认值
##################################插件##################################
#注册爬虫中间件:
SPIDER_MIDDLEWARES = {#默认值
'scrapy1.middlewares.Scrapy1SpiderMiddleware': 543,
}
#注册下载器中间件:
DOWNLOADER_MIDDLEWARES = {
'scrapy1.middlewares.Scrapy1DownloaderMiddleware': 543,
}
#注册扩展:
EXTENSIONS = {#默认值
'scrapy.extensions.telnet.TelnetConsole': None,
#这是用于查看爬虫当前状态的扩展
}
#注册管道:
ITEM_PIPELINES = {
'scrapy1.pipelines.Scrapy1Pipeline': 300,
'scrapy.pipelines.Scrapy1Pipeline2':200,
}
###############关于AutoThrottle extension(用于实现智能限速)################
#是否启用"
AUTOTHROTTLE_ENABLED = True
#首次请求的下载延迟:
AUTOTHROTTLE_START_DELAY = 5
#最大下载延迟:
AUTOTHROTTLE_MAX_DELAY = 10#会使用DOWNLOAD_DELAY作为最小下载延迟
#每秒并发请求数的平均值(只是目标值,并非必须达到):
AUTOTHROTTLE_TARGET_CONCURRENCY = 1.0
#是否对其使用调试模式:
AUTOTHROTTLE_DEBUG = False
#算法:
from scrapy.contrib.throttle import AutoThrottle#这是默认的算法
①设置最小延迟(DOWNLOAD_DELAY)
②设置最大延迟(AUTOTHROTTLE_MAX_DELAY)
③设置初始下载延迟(AUTOTHROTTLE_START_DELAY)
④获取上次请求的"连接时间"(latency)
#即从请求连接到收到响应头所用的时间
#每次请求的下载延迟都是利用上次请求的连接时间计算得到的
⑤进行计算的... AUTOTHROTTLE_TARGET_CONCURRENCY
target_delay = latency / self.target_concurrency
#self.target_concurrency就是AUTOTHROTTLE_TARGET_CONCURRENCY
new_delay = (slot.delay + target_delay) / 2.0
#注:这里slot.delay表示上次的延迟时间
new_delay = max(target_delay, new_delay)
new_delay = min(max(self.mindelay, new_delay), self.maxdelay)
#self.mindelay就是DOWNLOAD_DELAY;self.maxdelay就是AUTOTHROTTLE_MAX_DELAY
slot.delay = new_delay
#将(当前)请求的延迟设为new_delay
##################################缓存##################################
#是否启用缓存策略:
HTTPCACHE_ENABLED = True
#缓存策略:
①所有请求均缓存,下次再请求直接访问缓存即可:
HTTPCACHE_POLICY = "scrapy.extensions.httpcache.DummyPolicy"
②根据Http响应头中的Cache-Control/Last-Modified等字段进行缓存:
HTTPCACHE_POLICY = "scrapy.extensions.httpcache.RFC2616Policy"
#缓存超时时间(单位为s):
HTTPCACHE_EXPIRATION_SECS = 0
#缓存路径:
HTTPCACHE_DIR = 'httpcache'
#即缓存会被保存到该路径处
#不进行缓存的Http状态码:
HTTPCACHE_IGNORE_HTTP_CODES = []
#即如果请求的状态属于该列表,就不进行缓存
#用于进行缓存的插件:
HTTPCACHE_STORAGE = 'scrapy.extensions.httpcache.FilesystemCacheStorage'
##################################深度##################################
#指定爬取的深度:
DEPTH_LIMIT=4
#如指定为1,则访问初始URL页面中的URL(记为URL1),但不访问URL1页面中的URL
#0表示无深度
#广度优先还是深度优先:
DEPTH_PRIORITY=0#默认值
#只能为0(深度优先)或1(广度优先)
SCHEDULER_DISK_QUEUE = 'scrapy.squeue.PickleFifoDiskQueue'
SCHEDULER_MEMORY_QUEUE = 'scrapy.squeue.FifoMemoryQueue'
#调度器队列:
SCHEDULER = 'scrapy.core.scheduler.Scheduler'#默认值
#指定用于对URL去重的类:
DUPEFILTER_CLASS='scrapy.dupefilter.RFPDupeFilter'#默认值
##################################代理##################################
#实现代理的类:
from scrapy.contrib.downloadermiddleware.httpproxy import HttpProxyMiddleware
#代理的实质:
在request.meta中设置proxy属性;如果代理需要用户名和密码,再在request.headers中设
置Proxy-Authorization属性
①方式1(默认):默认方法需要将代理加到Python的当前环境变量(os.environ)中
#如果需要用户名和密码:
os.environ["http_proxy"]="http://root:woshiniba@192.168.11.11:9999/"
#如果不需要用户名和密码:
https_proxy:http://192.168.11.11:9999/
#key必须以_proxy结尾
#value的格式为http://[<username>:<pwd>@]<IP>:<port>/
#[...]中的部分在不需要用户名和密码时不需要加
#注意:value结尾的'/'不能没有
②方式2:使用自定义下载中间件
#middlewares.py中:
def to_bytes(text, encoding=None, errors='strict'):#转换成字节
if isinstance(text, bytes):
return text
if not isinstance(text, six.string_types):
raise TypeError('to_bytes must receive a unicode, str or bytes object, got %s' % type(text).__name__)
if encoding is None:
encoding = 'utf-8'
return text.encode(encoding, errors)
#middlewares.py中:
class ProxyMiddleware(object):
def process_request(self, request, spider):
PROXIES = [
{'ip_port': '111.11.228.75:80', 'user_pass': ''},
{'ip_port': '120.198.243.22:80', 'user_pass': ''},
{'ip_port': '111.8.60.9:8123', 'user_pass': ''},
{'ip_port': '101.71.27.120:80', 'user_pass': ''},
{'ip_port': '122.96.59.104:80', 'user_pass': ''},
{'ip_port': '122.224.249.122:8088', 'user_pass': ''},
]
proxy = random.choice(PROXIES)
if proxy['user_pass'] is not None:#代理有用户名和密码
request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])
encoded_user_pass = base64.encodestring(to_bytes(proxy['user_pass']))
#必须使用base64算法进行加密;
request.headers['Proxy-Authorization'] = to_bytes('Basic ' + encoded_user_pass)
#Proxy-Authorization属性的值必须以"Basic"开头
else:#代理没有用户名和密码
request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])
#settings.py中:
DOWNLOADER_MIDDLEWARES = {
'scrapy1.middlewares.ProxyMiddleware': 500,
}
##################################证书##################################
1.爬取网站使用的可信任证书(即第3方提供的证书;默认支持):
#以下是settings.py中的默认配置,但没有明示写出(不可见)
DOWNLOADER_HTTPCLIENTFACTORY="scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
DOWNLOADER_CLIENTCONTEXTFACTORY="scrapy.core.downloader.contextfactory.ScrapyClientContextFactory"
#在第2个类中读取了系统中已经植入了的可信任证书
2.要爬取网站使用的自定义证书(需要自定制):
DOWNLOADER_HTTPCLIENTFACTORY="scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
DOWNLOADER_CLIENTCONTEXTFACTORY="scrapy1.https.MySSLFactory"
#/scrapy1/https.py中:
from scrapy.core.downloader.contextfactory import ScrapyClientContextFactory
from twisted.internet.ssl import (optionsForClientTLS,CertificateOptions,PrivateCertificate)
class MySSLFactory(ScrapyClientContextFactory):#继承默认使用的类
def getCertificateOptions(self):#重写读取证书的方法
from OpenSSL import crypto
v1=crypto.load_privatekey(crypto.FILETYPE_PEM,open('/Users/1234/client.key.unsecure',mode='r').read())
v2=crypto.load_certificate(crypto.FILETYPE_PEM,open('/Users/1234/client.pem',mode='r').read())
return CertificateOptions(
privateKey=v1,#PKey对象,即私钥
certificate=v2,#X509对象,即证书
verify=False,
method=getattr(self,'method',getattr(self,'_ssl_method',None))
)
五.中间件
参见:https://www.cnblogs.com/xieqiankun/p/know_middleware_of_scrapy_1.html
使用中间件可在请求发起前或返回后对数据进行定制化修改,从而开发出适应不同情况的爬虫
中间件是开发者主动添加的组件,可在中途劫持数据,做一些修改后再把数据传递出去
Scrapy有2类中间件:下载中间件和爬虫中间件;每类都可包含多个中间件,每个中间件都是1个类
所有类中同名的方法都会被放入1个列表,在事件触发时依次执行该列表中的所有方法(见下图)
1.下载中间件(Downloader Middleware)
#说明:
①下载器中间件是用于处理引擎和下载器之间的request/response对象的钩子框架,大致位于调
度器和爬虫之间
②主要用于更换代理IP/更换Cookies/更换User-Agent/自动重试
③如果进行到某个中间件时就完成了下载,则之后的中间件不再继续执行
(1)内置的下载中间件:
#默认下载中间件(settings.py中,但没有明示写出):
DOWNLOADER_MIDDLEWARES={
'scrapy.contrib.downloadermiddleware.robotstxt.RobotsTxtMiddleware':100,
'scrapy.contrib.downloadermiddleware.httpauth.HttpAuthMiddleware':300,
'scrapy.contrib.downloadermiddleware.downloadtimeout.DownloadTimeoutMiddleware':350,
'scrapy.contrib.downloadermiddleware.useragent.UserAgentMiddleware':400,
'scrapy.contrib.downloadermiddleware.retry.RetryMiddleware':500,
'scrapy.contrib.downloadermiddleware.defaultheaders.DefaultHeadersMiddleware':550,
'scrapy.contrib.downloadermiddleware.redirect.MetaRefreshMiddleware':580,
'scrapy.contrib.downloadermiddleware.httpcompression.HttpCompressionMiddleware':590,
'scrapy.contrib.downloadermiddleware.redirect.RedirectMiddleware':600,
'scrapy.contrib.downloadermiddleware.cookies.CookiesMiddleware':700,
'scrapy.contrib.downloadermiddleware.httpproxy.HttpProxyMiddleware':750,
'scrapy.contrib.downloadermiddleware.chunked.ChunkedTransferMiddleware':830,
'scrapy.contrib.downloadermiddleware.stats.DownloaderStats':850,
'scrapy.contrib.downloadermiddleware.httpcache.HttpCacheMiddleware':900,
}
作为value的int值表示权重,越小越先执行其process_request()方法,而process_response()
的执行顺序与此相反
(2)自定义下载中间件:
#middlewares.py中:
class DownMiddleware1(object):
#注意:中间件中的方法只能属于这3个
def process_request(self,request,spider):
'''
请求需要被下载时,经过所有下载器中间件的process_request()调用
:param request:Request对象
:param spider:爬虫对象
:return:
None:交给下1个下载中间件的process_request()
Response对象:停止process_request()的执行,开始执行process_response()
Request对象:停止中间件的执行,将Request放回调度器
raise IgnoreRequest异常:调用process_exception()
'''
pass
def process_response(self,request,response,spider):
'''
下载完成,返回时调用
:param request:Request对象
:param response:Response对象
:param spider:爬虫对象
:return:
Response对象:交给下1个下载中间件的process_response()
Request对象:停止中间件的执行,request将被重新调度下载
raise IgnoreRequest异常:调用Request.errback()
'''
print('response1')
return response
def process_exception(self,request,exception,spider):
'''
当下载处理器(download handler)或process_request()抛出异常时执行
:param response:Response对象
:param exception:错误对象
:param spider:爬虫对象
:return:
None:交给下1个下载中间件的process_exception()
如果所有process_exception()都返回None,则报错
Response对象:停止后续process_exception方法
Request对象:停止中间件,request将会被重新调度下载
'''
return None
如果自定义的下载中间件都未下载,则由内置下载中间件下载
(3)执行顺序:
2.爬虫中间件(Spider Middleware)
①下载器中间件是用于处理引擎和爬虫之间的request/response对象的钩子框架,大致位于下载
器和管道之间
(1)内置的爬虫中间件:
#默认爬虫中间件(settings.py中,但没有明示写出):
SPIDER_MIDDLEWARES={
'scrapy.contrib.spidermiddleware.httperror.HttpErrorMiddleware':50,
'scrapy.contrib.spidermiddleware.offsite.OffsiteMiddleware':500,
'scrapy.contrib.spidermiddleware.referer.RefererMiddleware':700,
'scrapy.contrib.spidermiddleware.urllength.UrlLengthMiddleware':800,
'scrapy.contrib.spidermiddleware.depth.DepthMiddleware':900,
}
作为value的int值表示权重,越小越先执行
(2)自定义爬虫中间件:
#middlewares.py中:
class SpiderMiddleware(object):
def process_spider_input(self,response,spider):
'''
下载完成时执行,然后交给parse()处理
:param response:下载器移交的Response对象
:param spider:爬虫对象
:return:
'''
pass
def process_spider_output(self,response,result,spider):
'''
spider处理完成,返回时调用,之后再交给调度器或管道
:param response:Response对象
:param result:parse()返回的Request或Item对象
:param spider:爬虫对象
:return:必须返回包含Request或Item对象的可迭代对象
'''
return result
def process_spider_exception(self,response, exception, spider):
'''
异常调用
:param response:
:param exception:
:param spider:
:return:
None:继续交给后续中间件处理异常
含Response或Item对象的可迭代对象:交给调度器或管道
'''
return None
def process_start_requests(self,start_requests,spider):
'''
爬虫启动时调用,在之后迭代深度时不执行
:param start_requests:初始URL的Request对象构成的可迭代对象
:param spider:爬虫对象
:return:包含Request对象的可迭代对象
'''
return start_requests
(3)执行顺序:
六.自定义Scrapy命令
在spiders同级创建任意目录,如commands
在其中创建任意文件,如crawlall.py(此处文件名就是自定义的命令)
#/scrapy1/scrapy1/commands/crawlall.py中:
from scrapy.commands import ScrapyCommand
from scrapy.utils.project import get_project_settings
class Command(ScrapyCommand):
requires_project=True
def syntax(self):
return '[options]'
def short_desc(self):#使用scrapy --help时关于该命令的提示
return 'Runs all of the spiders'
def run(self,args,opts):#opts是传入命令的参数
#找到所有爬虫的名称:
spider_list=self.crawler_process.spiders.list()
#开始爬取(内置的crawl命令也是通过类似的方法执行的):
for name in spider_list:
self.crawler_process.crawl(name,**opts.__dict__)
self.crawler_process.start()
#settings.py中:
COMMANDS_MODULE='scrapy1.commands'
scrapy crawlall#执行所有爬虫