Scrapy框架进阶

Scrapy框架进阶

Scrapy的启动和debug

  • 命令行

    scrapy crawl jd_search
    
  • 启动脚本

    # 新建run.py
    
    from scrapy import cmdline
    
    command = "scrapy crawl jd_search".split()
    cmdline.execute(command)
    

Scrapy Item

只是对解析的结构化结果进行一个约束, 在到达pipeline前就可以检查出数据错误.

Scrapy的设置

  • *ROBOTTEXT_OBEY

    ROBOTTEXT_OBEY=False
    

    获取对方网站是否允许爬虫获取数据的信息.

  • 设置中间件

    数字越小, 离ENGINE越近

    DOWNLOADER_MIDDLEWARES = {
       # 'jd_crawler_scrapy.middlewares.JdCrawlerScrapyDownloaderMiddleware': 543,
       'jd_crawler_scrapy.middlewares.UAMiddleware': 100,
    }
    
  • 设置PIPELINE

    ITEM_PIPELINES = {
       'jd_crawler_scrapy.pipelines.JdCrawlerScrapyPipeline': 300,
    }
    
  • 请求限制

    • *CONCURRENT_REQUESTS

      请求并发数, 通过控制请求并发数达到避免或者延缓IP被封禁

    CONCURRENT_REQUESTS = 1

    
    - CONCURRENT_REQUESTS_PER_DOMAIN
    
    控制每个`域名`请求的并发数
    
    - CONCURRENT_REQUESTS_IP
    
    控制每个`IP`请求的次数. 通过这样的方式可以过掉一些对IP封禁严格的网站.
    
    - CONCURRENT_ITEMS
    
    默认为100, 控制处理`item`的并发数. 如果我存入的数据库性能比较差, 通过这样的方式解决防止数据库崩溃的情况.
    
    - ***DOWNLOAD_DELAY**
    
    默认为0, 控制请求的频率. 在调度完一个请求后, 休息若干秒. 
    
    > Scrapy会自动帮我们进行随机休息    (DOWNLOAD_DELAY - 0.5, DOWNLOAD_DELAY + 0.5)
    
    

    DOWNLOAD_DELAY = 2

    
    - ***DOWNLOAD_TIMEOUT**
    
    控制每个请求的超时时间. 通过这样的方式解决IP代理池质量差的问题.
    
    

    根据自己的IP代理池质量自定决定

    DOWNLOAD_TIMEOUT = 6

    
    - ***REDIRECT_ENABLE**
    
    默认为`True`, 建议修改为`False`, 因为大部分情况下, 重定向都是识别出你当前身份有问题, 重定向到`log in`页面
    
    
  • 重试机制

    • *RETRY_ENABLE

    RETYR_ENABLE = False

    
    默认为`True`, 建议改成`False`, 然后自己重写重试中间件
    
    - RETRY_TIMES
    
    控制重新次数,  RETRY_TIMES其实是当前项目的兜底配置
    
    > 如果当前请求失败后永远会重试, 正好你请求的接口是收费的, 万一有一天报错, 那么产生的费用是巨大的.
    
    

    RETRY_TIMES = 3

    
    - RETRY_HTTP_CODES
    
    

    RETRY_HTTP_CODES = [500, 502, 503, 504, 408, 429]

    
    
  • 过滤器

    • 设置中指定过滤器

DUPEFILTER_CLASS = “jd_crawler_scrapy.middlewares.MyRFPDupeFilter”
```

  • Spider中打开过滤器

                  yield scrapy.FormRequest(
                        dont_filter=False,
                        url=url,
                        method='GET',
                        # formdata=data,           
                        callback=self.parse_search  
                    )
    
  • 过滤器

    from scrapy.dupefilters import RFPDupeFilter
    from w3lib.url import canonicalize_url
    from scrapy.utils.python import to_bytes
    import hashlib
    import weakref
    
    class MyRFPDupeFilter(RFPDupeFilter):
        """
        过滤器是在到达下载器之前就生成了过滤指纹, 如果我们的下载器中间件报错了, 那么过滤指纹仍然生效, 但是没有实际请求.
        所以我们可以通过一些特殊参数来进行自定义过滤规则
        """
        def request_fingerprint(self, request, include_headers=None, keep_fragments=False):
            cache = _fingerprint_cache.setdefault(request, {})
            cache_key = (include_headers, keep_fragments)
            if cache_key not in cache:
                fp = hashlib.sha1()
                fp.update(to_bytes(request.method))
                fp.update(to_bytes(canonicalize_url(request.url, keep_fragments=keep_fragments)))
                fp.update(request.body or b'')
                fp.update(request.meta.get("batch_no", "").encode("utf-8"))
                cache[cache_key] = fp.hexdigest()
            return cache[cache_key]
    
  • LOG

    • LOG_ENABLE

      默认为True, 是否使用log

    • LOG_FILE

      设置保存的log文件目录

    • LOG_LEVEL(按严重程序排序)

      • CRITICAL
      • ERROR
      • WARNING
      • INFO
      • DEBUG

Scrapy的中间件

  • 请求头中间件

    class UAMiddleware:
        def process_request(self, request, spider):
            request.headers["user-agent"] = "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.141 Safari/537.36"
    
    
  • 重试中间件

    from scrapy.downloadermiddlewares.retry import RetryMiddleware
    from scrapy.utils.response import response_status_message
    
    class MyRetryMiddleware(RetryMiddleware):
        """
        解决对方服务器返回正常状态码200, 但是根据IP需要进行验证码验证的情况.
        我们可以通过换IP可以解决验证码, 那么就应该重试.
        """
        def process_response(self, request, response, spider):
            if request.meta.get('dont_retry', False):
                return response
            if "验证码" in response.text:
                reason = response_status_message(response.status)
                return self._retry(request, reason, spider) or response
            return response
    

Meta

  • 信息传递

    • 在创建REQEUSTS对象时设置meta

      yield scrapy.FormRequest(meta={"keyword": keyword, "sta_date": sta_date})
      
    • REQUESTS对象流转中修改meta

      • download_slot
      • download_latency
    • RESPONSE对象中获取meta

      #等同于response.request.meta
      response.meta
      
  • 自定义单个请求的配置

    https://docs.scrapy.org/en/latest/topics/request-response.html?highlight=meta#topics-request-meta
    
    • dont_redirect

      如果设置为True, 当前请求则不会重定向.

    • dont_retry

      如果设置为True, 当前请求则不会重试.

    • max_retry_times

      设置最大重试次数.

    • dont_merge_cookiescookiejar

      操作cookie的meta参数, 但是不建议这么使用, 一般来说我们直接设置

      request.headers["cookie"] = "......."
      
    • proxy

      设置请求代理

      request.meta['proxy'] = '127.0.0.1:8989'
      
    • 设置优先级

      如果你设置了优先级队列, 那么可以只是priority参数决定请求的顺序

      # 数字越小, 优先级越高
      request.meta['priority'] = 10
      

异常处理

异常处理时scrapy最大的痛点, 因为你一定要熟悉事件的流向.

  • Spiders组件

    在异常处理中, Spider组件其实是处理RESPONSE对象或者请求之后产生的异常, 一般作为一次请求异常处理的终点, 也就是指定的回调函数errorback.

    • errorback

      处理不可控的异常

          def start_request(self):
          	yield scrapy.FormRequest(errorback=self.process_error)
          
          def process_error(self, failure):
              print(failure)
              # 记录异常
              # 发送通知
              # 重做任务
              ...
      
      • failure.request

        当前异常请求对象

      • failure.value

        当前的异常对象

    • CloseSpider

      遇到像cookie过期, 账号警告, 代理池空了这样严重的错, 需要关闭实例, 可抛出CloseSpider异常, 该异常最终会流向ENGINE后关闭爬虫实例.

      from scrapy.exceptions import CloseSpider
      
  • 中间件

    处理可控的异常

    def process_exception(self, request, exception, spider):
    	pass
    
    • 返回None

      由下一层中间件继续处理, 如果你指定了errback, 最终会到达errback

    • 返回REPONSE对象

      中断异常链, 返回的RESPONSE对象会到达Spiders组件

    • 返回Request

      中断异常链, 返回的Request对象将到达ENGINESCHEDULER重新调度.

          def process_exception(self, request, exception, spider):
              # 如果异常是cookie池空了, 可以在这里完成cookie池的补充
              # 补充cookie池
              if isinstance(exception, IndexError):
                  # 我已经知道异常产生原因, 所以免除当前重试次数的计数
                  retry_times = request.meta.get('retry_times', 1)
                  request.meta['retry_times'] = retry_times - 1
                  return request
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值