Scrapy框架2

一、进程、线程、协成

  1、进程、线程、协成之间的关系
           1、  线程是计算机中最小的工作单元。
              2、 进程是提供资源供n个线程使用,即进程是最小的管理单元。

      3、协程是人为控制的线程。

      4、总结:1、python中由于有 GIL锁的存在,所以一个进程中同一时刻只有一个线程被CPU调度,所以在计算密集型,使用多进程而在io密集型使用多线程。

         2、使用协成可以实现单线程下的并发,线程中cpu在遇到io操作时就会切到下一个线程中,这样大大的影响的效率于是人为的控制cpu在线程中的切换,当cpu在线程中遇到io会切换到该线程的下一个程序中这就是协成

  2、异步非阻塞模块

   1、gevent模块,基于协程的异步非阻塞模块。
           2、Twisted,基于事件驱动(while循环检测)异步非阻塞模块(框架)。
           3、非阻塞是指不等待 ,异步是指回调,执行某个任务完成之后,自动执行的函数。回调函数

二、scarpy框架

  1、重写scrapy请求头方法

# -*- coding: utf-8 -*-
import scrapy
from scrapy.http import Request
from scrapy.selector import HtmlXPathSelector             #response= HtmlXPathSelector(response)主要是在parse函数中使用,
from urllib.parse import urlencode
from ..items import MyscrapyItem class BaiduSpider(scrapy.Spider): name = 'baidu' #爬虫应用的名称,通过此名称启动爬虫命令 allowed_domains = ['www.amazon.cn'] # 允许爬取数据的域名 def __init__(self,keywords=None,*args,**kwargs): super(BaiduSpider,self).__init__(*args,**kwargs) self.keywords=keywords #主要是接受一些外部传进来的参数 def start_requests(self): #重写scrapy请求头url必须写这个名字的函数 url='https://www.amazon.cn/s/ref=nb_sb_noss_1?' #新url parmas={ 'field-keywords':self.keywords #主要是定义一些外部传进来的参数 } url=url+urlencode(parmas,encoding='utf-8') #路径拼接,形成一个完整的url路径 yield Request(url,callback=self.parse_index) #yield返回数据给回调函数,也可以是return+[]的方式返回数据给回调函数,yield返回的是生成器,return+[]返回的可迭代对象 # return [Request(url='www.baidu.com',callback=self.parse),] def parse_index(self, response): urls=response.xpath('//*[contains(@id,"result_"]/div/div[3]/div[1]/a/@href').extract() for url in urls: print(url) yield Request(url,callback=self.parse) #返回详情页的信息 next_url=response.urljoin(response.xpath('//*[@id="pagnNextLink"]/@href').extract_first()) yield Request(next_url,callback=self.parse_index) #返回下一页的url路径,返回的是个Request对象 def parse(self, response): price=response.xpath('//*[@id="priceblock_ourprice"]/text()').extract_first().strip() colour=response.xpath('//*[@id="variation_color_name"]/div/span/text()').extract_first().strip() item=MyscrapyItem() item['price']=price item['colour']=colour return item #返回的是个item对象
     或者是这种格式,只要返回的是个item对象及格
     obj = XiaoHuarItem(price=price, colour=colour, )
     yield obj

   2、items文件写法

import scrapy
class MyscrapyItem(scrapy.Item):
    price=scrapy.Field() 
    colour=scrapy.Field()

   3、自定义pipelines文件

from scrapy.exceptions import DropItem    #主动触发异常用的
class CustomPipeline(object):
    def __init__(self,val):
        self.vale = val              #构造一个对象特有的方法
    def process_item(self, item, spider):       #用于存储爬取的数据,可以存储在文件也可以存储在数据库。
      
       f=opne('item.log','a+')
     f.write(item.price,item.colour)
     f.close() return item # return表示会被后续的pipeline继续处理,因为有可以一个爬虫项目会有多个爬虫程序一起执行,但是都有写入同一个文件中,那就让第一个文件写入,其余后面的文件不用重复写入。 # raise DropItem() # 表示将item丢弃,不会被后续pipeline处理     # if spider.name='baidu': #这就表示该文件只是存储这个名字的爬虫爬取的数据
      #f=opne('item.log','a+')
      #f.write(item.price,item.colour)
      #f.close()
    #return item
@classmethod def from_crawler(cls, crawler):#初始化时候,用于创建pipeline对象,crawler这个类可以得到settings配置文件内的数据,如果需要给pipeline对象创建某个额外的参数就可以用这中方法 val = crawler.settings.getint('MMMM') #val = crawler.settings.get('MMMM') 读取配置文件的某个信息 return cls(val) def open_spider(self,spider): #在爬虫程序开始执行之前调用该方法 print('000000') def close_spider(self,spider): #在爬虫程序关闭后调用该方法 print('111111')

总结:这五个方法的执行顺序: 先执行from_crawler方法,用于初始化数据的时候读取配置文件,然后再执行爬虫程序的__init__方法,然后程序启动前执行open_spider方法,程序结束前保存数据时执行process_item方法,程序结果后
执行close_spider方法。

   4、自定义url去重功能

scrapy默认使用 scrapy.dupefilter.RFPDupeFilter 进行去重,settings相关配置有: 
DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter' #去重文件,可以自定义 DUPEFILTER_DEBUG = False #默认去重功能是关闭的可以在settings中打开去重功能也可以在爬虫程序中手动指定去重 yield Request(url,callback=self.parse,dont_filter=True) JOBDIR = "保存范文记录的日志路径,如:/root/" # 最终路径为 /root/requests.seen 默认url是存放在内存中的,只要添加上jobdir就可以吧url存放在指定文件中

   自己写一个去重规则

class RepeatUrl:
    def __init__(self):
        self.visited_url = set() #默认是一个集合,也可以是自定义链接一个数据库,文件等

    @classmethod
    def from_settings(cls, settings):
        """
        初始化时,调用
        """
        return cls()

    def request_seen(self, request):
        """
        检测当前请求是否已经被访问过
        :param request: 
        :return: True表示已经访问过;False表示未访问过
        """
        if request.url in self.visited_url:
            return True
        self.visited_url.add(request.url)
        return False

    def open(self):
        """
        开始爬去请求时,调用
        :return: 
        """
        print('open replication')

    def close(self, reason):
        """
        结束爬虫爬取时,调用
        :param reason: 
        :return: 
        """
        print('close replication')

    def log(self, request, spider):
        """
        记录日志
        :param request: 
        :param spider: 
        :return: 
        """
        print('repeat', request.url)
注意:写好自定义的去重规则后要将该文件设置到settings中该规程才能生效 比如:DUPEFILTER_CLASS = 'scrapy.dupefilter.RFPDupeFilter'

   5、自定义命令

   #自定制一键启动所有爬虫命令 
  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): return 'Runs all of the spiders' def run(self, args, opts): #这是源码的入口 spider_list = self.crawler_process.spiders.list() for name in spider_list: #循环所有的爬虫程序名称 self.crawler_process.crawl(name, **opts.__dict__) #准备所有的爬虫程序 self.crawler_process.start() #开始爬取任务
注意:1、在spiders同级创建任意目录,如:commands
   2、在其中创建 crawlall.py 文件 (此处文件名就是自定义的命令)
   3、在settings.py 中添加配置 COMMANDS_MODULE = '项目名称.目录名称'
   4、在项目目录执行命令:scrapy crawlall
  
  #自定制在py文件中启动某个爬虫命令  
  from scrapy.cmdline import execute  
  execute(['scrapy', '命令', '爬虫程序名称','-a','传入的参数'])
  eg:execute(['scrapy', 'crawl', 'baidu','-a','keywords=iphone7'])
注意:自定制的py文件名称必须是entrypoint.py

     6、自定义信号

from scrapy import signals
class MyExtension(object):
    def __init__(self, value):
        self.value = value    #定义自己想要的方法

    @classmethod
    def from_crawler(cls, crawler): #扩展功能,在扩展功能里注册信号
        val = crawler.settings.getint('MMMM')#读取配置文件信息
        ext = cls(val)
        crawler.signals.connect(ext.spider_opened, signal=signals.spider_opened) #注册信号
        crawler.signals.connect(ext.spider_closed, signal=signals.spider_closed) #注册信号,可以注册很多信号,看源码
        return ext   #如果不需要扩展功能那么就直接注册信号即可

    def spider_opened(self, spider): #爬虫程序启动时触发的信号
        print('open')

    def spider_closed(self, spider): #爬虫程序关闭时触发的信号
        print('close')
注释:1、在项目目录下创建一个py文件,将自定义的信号写入该文件,建议py文件名为extensions
   2、在settings中注册自定义信号的文件
    eg:EXTENSIONS = {'scrapy.extensions.telnet.TelnetConsole': 300,}

    7、中间件

#下载中间件
class DownMiddleware1(object): def process_request(self, request, spider): #请求过去的中间件带的时request """ 请求需要被下载时,经过所有下载器中间件的process_request调用 return: None,继续后续中间件去下载,默认就是什么都不返回; Response对象,停止process_request的执行,开始执行process_response,先导入Response(from scrapy.http import Response)
       eg:return Response(url='http://www.baidu.com',request=request) #只要返回Response对象就不会执行后续的Request,而是直接执行Response Request对象,停止中间件的执行,将Request重新调度器 #如果返回的是Request对象那就不会执行后续的Request和Reques而是直接到引擎,重新调度 raise IgnoreRequest异常,停止process_request的执行,开始执行process_exception """ pass def process_response(self, request, response, spider): #返回数据的中间件带的时response """ spider处理完成,返回时调用,默认返回的就是Response return: Response 对象:转交给其他中间件process_response Request 对象:停止中间件,request会被重新调度下载 raise IgnoreRequest 异常:调用Request.errback """ return response def process_exception(self, request, exception, spider): """ 当下载处理器(download handler)或 process_request() (下载中间件)抛出异常的时候调用 :return: None:继续交给后续中间件处理异常; Response对象:停止后续process_exception方法,正确执行后续方法 Request对象:停止中间件后续方法,request将会被重新调用下载 """ return None

注释:1、在项目目录下创建.py文件,将中间件代码写在该文件中,建议文件名称为DownMiddleware
   2、在settings中注册中间件,DOWNLOADER_MIDDLEWARES = {'chouti.middlewares.MyCustomDownloaderMiddleware': 600,},写了几个中间件就注册几个中间件
   3、可以在中间件的process_request中将所有的Request请求添加上请求头,添加cookies等操作,也可以添加代理,判断那个Request需要代理。

 

#爬虫中间件
class SpiderMiddleware(object):
    def process_spider_input(self,response, spider):
        """下载完成,执行,然后交给parse处理,默认返回none""" 
        pass
    def process_spider_output(self,response, result, spider):
        """spider处理完成,返回时调用,return: 必须返回包含 Request 或 Item 对象的可迭代对象(iterable)"""
        return result
def process_spider_exception(self,response, exception, spider): """异常调用,return: None,继续交给后续中间件处理异常;含 Response 或 Item 的可迭代对象(iterable),交给调度器或pipeline""" return None
def process_start_requests(self,start_requests, spider): """爬虫启动时调用,return: 包含 Request 对象的可迭代对象,由于爬虫只是启动一次所以process_start_requests也只是调用一次 """ return start_requests
注释:1、在项目目录下创建.py文件,将中间件代码写在该文件中,建议文件名称为SpiderMiddleware
   2、在settings中注册中间件,
SPIDER_MIDDLEWARES = { 'chouti.middlewares.ChoutiSpiderMiddleware': 600,},写了几个中间件就注册几个中间件

   8、自定义代理

代理,需要在环境变量中设置
    from scrapy.contrib.downloadermiddleware.httpproxy import HttpProxyMiddleware
    
    方式一:使用默认
     import os os.environ {
      http_proxy:http://root:woshiniba@192.168.11.11:9999/ #有的代理链接是需要用户名和密码的,这个就是root为用户名,woshiniba为密码 https_proxy:http://192.168.11.11:9999/
      } #该方法只需要在爬虫程序文件的类的__init__中添加上就可以了,程序会判断只要是_prxoy结尾的就会吧这个代理拿出来
方式二:使用自定义下载中间件 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) 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']))#代理加密,固定加密方法 request.headers['Proxy-Authorization'] = to_bytes('Basic ' + encoded_user_pass) #代理授权 #print "**************ProxyMiddleware have pass************" + proxy['ip_port'] else: #print "**************ProxyMiddleware no pass************" + proxy['ip_port'] request.meta['proxy'] = to_bytes("http://%s" % proxy['ip_port'])#添加代理 注释:1、在爬虫项目路径中创建代理文件
     2、在settings中注册上代理 DOWNLOADER_MIDDLEWARES = { 'step8_king.middlewares.ProxyMiddleware': 500,}

   9、settings配置文件详解

1. 爬虫名称
BOT_NAME = 'step8_king'

2. 爬虫应用路径
SPIDER_MODULES = ['step8_king.spiders'] #其实就是创建的爬虫程序名称+spiders
NEWSPIDER_MODULE = 'step8_king.spiders'
3. 客户端 user-agent请求头 # USER_AGENT = 'step8_king (+http://www.yourdomain.com)' 4. 禁止爬虫配置(机器人协议) # ROBOTSTXT_OBEY = True
5. 并发请求数 # CONCURRENT_REQUESTS = 4 #一个线程最大能并发多少个请求,力度粗 6. 延迟下载秒数 # DOWNLOAD_DELAY = 2 默认是秒,每多少秒一次下载 7. 单域名访问并发数,并且延迟下次秒数也应用在每个域名 力度细 # CONCURRENT_REQUESTS_PER_DOMAIN = 2 # 单IP访问并发数,如果有值则忽略:CONCURRENT_REQUESTS_PER_DOMAIN,并且延迟下次秒数也应用在每个IP 力度细 # CONCURRENT_REQUESTS_PER_IP = 3 8. 是否支持cookie,cookiejar进行操作cookie # COOKIES_ENABLED = True # COOKIES_DEBUG = True
9. Telnet用于查看当前爬虫的信息,操作爬虫等... # 使用telnet ip port ,然后通过命令操作 # TELNETCONSOLE_ENABLED = True # TELNETCONSOLE_HOST = '127.0.0.1' # TELNETCONSOLE_PORT = [6023,] 10. 默认请求头,死板 # Override the default request headers: # DEFAULT_REQUEST_HEADERS = { # 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', # 'Accept-Language': 'en', # } 11. 定义pipeline处理请求 # ITEM_PIPELINES = { # 'step8_king.pipelines.JsonPipeline': 700, # 'step8_king.pipelines.FilePipeline': 500, # } 12. 自定义扩展,基于信号进行调用 # Enable or disable extensions # See http://scrapy.readthedocs.org/en/latest/topics/extensions.html # EXTENSIONS = { # # 'step8_king.extensions.MyExtension': 500, # } 13. 爬虫允许的最大深度,可以通过meta查看当前深度;0表示无深度 # DEPTH_LIMIT = 3 14. 爬取时,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' 15. 调度器队列 # SCHEDULER = 'scrapy.core.scheduler.Scheduler' # from scrapy.core.scheduler import Scheduler 16. 访问URL去重 # DUPEFILTER_CLASS = 'step8_king.duplication.RepeatUrl' """ 17. 自动限速算法 from scrapy.contrib.throttle import AutoThrottle 自动限速设置 1. 获取最小延迟 DOWNLOAD_DELAY 2. 获取最大延迟 AUTOTHROTTLE_MAX_DELAY 3. 设置初始下载延迟 AUTOTHROTTLE_START_DELAY 4. 当请求下载完成后,获取其"连接"时间 latency,即:请求连接到接受到响应头之间的时间 5. 用于计算的... AUTOTHROTTLE_TARGET_CONCURRENCY target_delay = latency / self.target_concurrency new_delay = (slot.delay + target_delay) / 2.0 # 表示上一次的延迟时间 new_delay = max(target_delay, new_delay) new_delay = min(max(self.mindelay, new_delay), self.maxdelay) slot.delay = new_delay """

   10、https证书设置

    Https访问时有两种情况:
    1. 要爬取网站使用的可信任证书(默认支持)
        DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
        DOWNLOADER_CLIENTCONTEXTFACTORY = "scrapy.core.downloader.contextfactory.ScrapyClientContextFactory"
        
    2. 要爬取网站使用的自定义证书
        DOWNLOADER_HTTPCLIENTFACTORY = "scrapy.core.downloader.webclient.ScrapyHTTPClientFactory"
        DOWNLOADER_CLIENTCONTEXTFACTORY = "step8_king.https.MySSLFactory"
        
        # 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/wupeiqi/client.key.unsecure', mode='r').read()) #打开本地保存的证书文件
                v2 = crypto.load_certificate(crypto.FILETYPE_PEM, open('/Users/wupeiqi/client.pem', mode='r').read())
                return CertificateOptions(
                    privateKey=v1,  # pKey对象
                    certificate=v2,  # X509对象
                    verify=False,
                    method=getattr(self, 'method', getattr(self, '_ssl_method', None))
                )
    其他:
        相关类
            scrapy.core.downloader.handlers.http.HttpDownloadHandler
            scrapy.core.downloader.webclient.ScrapyHTTPClientFactory
            scrapy.core.downloader.contextfactory.ScrapyClientContextFactory
        相关配置
            DOWNLOADER_HTTPCLIENTFACTORY
            DOWNLOADER_CLIENTCONTEXTFACTORY

 

posted @ 2017-11-13 20:24  mx轩  阅读(266)  评论(0编辑  收藏  举报