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
Last modification:September 16, 2022
如果觉得我的文章对你有用,请随意赞赏