爬虫工作量由小到大的思维转变---<第二十四章 Scrapy的`统计数据`收集stats collection ---12月26日补>
作者:mmseoamin日期:2024-01-22

前言:

前两篇是讲的数据诊断分析,还有一篇深挖`解决内存泄漏`的文章,目前我还没整理汇编出来;但是,想到分析问题的时候,忽然觉得`爬虫的数据统计`好像也挺重要;于是,心血来潮准备来插一篇这个------让大家对日常scrapy爬的数据,做到心里有数!不必自己去搅破脑汁捣腾日志,敲计算器了;

正文:

在 Scrapy 中,可以使用 Stats Collection(统计信息收集)来收集和获取有关爬虫运行过程中的统计信息。Stats Collection 提供了各种默认的统计指标,例如请求数量、下载时间和爬取成功数等。

当然,也可以使用其他的,例如:

MemoryStatsCollector:默认的统计收集器,将统计数据存储在内存中。
CsvStatsCollector:将统计数据保存为 CSV 格式的文件。
JsonLinesStatsCollector:将统计数据保存为 JSON Lines 格式的文件。
XmlStatsCollector:将统计数据保存为 XML 格式的文件。
DbStatsCollector:将统计数据保存到数据库中。
LogStatsCollector:通过日志输出统计数据。
-----这些其实都大同小异,我们就拿第一个来开刀!!

使用 Stats Collection 的步骤:

1. 在 Scrapy 项目的配置文件 `settings.py` 中启用 Stats Collection:
   STATS_CLASS = 'scrapy.statscollectors.MemoryStatsCollector'

通过配置 `STATS_CLASS` 参数,可以选择不同的 Stats Collector。在示例中,我们使用了 `MemoryStatsCollector`,该 Collector 将统计信息存储在内存中。

2. 在 Scrapy 的爬虫代码中导入 `scrapy.stats`:
   from scrapy import stats
3. 在爬虫代码中,可以使用 `stats` 对象来访问和处理统计信息。以下是一些常用的方法:
  •    - `stats.get_value(key, default=None)`:获取指定键名的统计值。如果指定的键名不存在,则返回提供的 `default` 值(默认为 `None`)。
  •    - `stats.inc_value(key, count=1)`:增加指定键名的统计值。`count` 参数可以指定增加的数量,默认为 1。
  •    - `stats.set_value(key, value)`:设置指定键名的统计值为给定的 `value`。
  •    - `stats.get_stats()`:返回当前所有统计信息的字典形式。

    使用 Stats Collection:

       from scrapy import Spider
       from scrapy import stats
       
       
       class MySpider(Spider):
           name = 'my_spider'
           start_urls = ['http://example.com']
       
           def parse(self, response):
               # 增加请求数量统计值
               self.stats.inc_value('request_count')
       
               # 获取当前请求数量的统计值
               request_count = self.stats.get_value('request_count', default=0)
               self.logger.info(f"Request Count: {request_count}")
       
               # 设置自定义统计值
               self.stats.set_value('custom_stat', 10)
       
               # 获取所有统计信息
               all_stats = self.stats.get_stats()
               self.logger.info(f"All Stats: {all_stats}")
               # ...其他处理代码...
       ```

         在上述示例中,在解析函数中使用 `stats` 对象进行统计值的增加、获取和设置操作,以及获取所有统计信息。可以根据需要进行自定义的统计值操作,从而监控和分析爬虫的运行情况。

    ps:Stats Collection 默认收集的统计信息可能会消耗一定的内存,如果需要更复杂的统计需求,可以考虑使用第三方库或自定义 Stats Collector 进行更高级的统计处理。


    深入:

    当需要进行更高级的统计处理时,可以自定义 Stats Collector 来满足特定的需求。自定义 Stats Collector 可以用于收集、处理和保存统计数据,以便后续分析和可视化。

    以下是自定义 Stats Collector 的步骤:

    1. 创建一个自定义的 Stats Collector 类,继承自 `scrapy.statscollectors.StatsCollector` 类,并重写需要的方法。

    通常情况下,需要实现 `__init__()`、`open_spider()`、`close_spider()` 和 `get_value()` 方法。

     from scrapy.statscollectors import StatsCollector
       
       class CustomStatsCollector(StatsCollector):
       
           def __init__(self, crawler):
               super().__init__(crawler)
               # 初始化自定义的统计数据
               self.custom_stats = {}
       
           def open_spider(self, spider):
               super().open_spider(spider)
               # 初始化每个爬虫的自定义统计数据
               self.custom_stats[spider.name] = {}
       
           def close_spider(self, spider, reason):
               super().close_spider(spider, reason)
               # 在爬虫结束时处理自定义统计数据
               custom_stats_data = self.custom_stats[spider.name]
               # 进行进一步的处理或保存操作
       
           def get_value(self, key, default=None, spider=None):
               # 获取自定义统计数据的值
               if spider:
                   return self.custom_stats[spider.name].get(key, default)
               return default

      

    2. 在 Scrapy 项目的配置文件 `settings.py` 中配置自定义的 Stats Collector 类:
       STATS_CLASS = 'your_project_name.custom_stats.CustomStatsCollector'

      ps:`your_project_name` 需要替换为 Scrapy 项目的名称,以及其他必要的导入路径。

    3. 使用自定义的 Stats Collector

       在 Spider 类中,通过 `self.crawler.stats` 访问自定义的 Stats Collector 对象,并使用相应的方法进行统计值的获取、增加和设置。

       from scrapy import Spider
       
       class MySpider(Spider):
           name = 'my_spider'
           start_urls = ['http://example.com']
       
           def parse(self, response):
               # 增加自定义统计值
               self.crawler.stats.inc_value('custom_stat', spider=self)
       
               # 获取自定义统计值
               custom_stat_value = self.crawler.stats.get_value('custom_stat', default=0, spider=self)
               self.logger.info(f"Custom Stat Value: {custom_stat_value}")
       
               # 设置自定义统计值
               self.crawler.stats.set_value('custom_stat', 10, spider=self)
       
               # ...其他处理代码...

       创建一个自定义的 Stats Collector 类 `CustomStatsCollector`,并在 `open_spider()` 和 `close_spider()` 方法中进行自定义统计数据的初始化和处理。在 Spider 类中,使用 `self.crawler.stats` 访问自定义的 Stats Collector 对象,并用相应的方法进行自定义统计值的增加、获取和设置。

    也可以根据具体需求在自定义 Stats Collector 类中添加其他统计方法和处理逻辑,并使用自定义统计数据进行进一步的分析和处理。

    另一个案例:

    统计每个爬虫访问 URL 的数量,并在爬虫结束时将统计数据保存到文件中。

    import json
    from scrapy.statscollectors import StatsCollector
    class CustomStatsCollector(StatsCollector):
        def __init__(self, crawler):
            super().__init__(crawler)
            # 初始化自定义统计数据
            self.custom_stats = {}
        def open_spider(self, spider):
            super().open_spider(spider)
            # 初始化每个爬虫的自定义统计数据
            self.custom_stats[spider.name] = {
                'url_count': 0
            }
        def close_spider(self, spider, reason):
            super().close_spider(spider, reason)
            # 在爬虫结束时处理自定义统计数据
            custom_stats_data = self.custom_stats[spider.name]
            # 保存自定义统计数据到文件
            with open(f'{spider.name}_stats.json', 'w') as file:
                json.dump(custom_stats_data, file)
        def inc_url_count(self, spider):
            # 增加 URL 数量统计值
            self.custom_stats[spider.name]['url_count'] += 1
        def get_url_count(self, spider):
            # 获取 URL 数量统计值
            return self.custom_stats[spider.name]['url_count']

    在 Spider 类中,我们可以调用自定义 Stats Collector 的 `inc_url_count()` 方法来增加 URL 数量的统计值,并使用 `get_url_count()` 方法获取统计值。

    from scrapy import Spider
    class MySpider(Spider):
        name = 'my_spider'
        start_urls = ['http://example.com']
        def parse(self, response):
            # 增加 URL 数量统计值
            self.crawler.stats.inc_url_count(self)
            # 获取 URL 数量统计值
            url_count = self.crawler.stats.get_url_count(self)
            self.logger.info(f"URL Count: {url_count}")
            # ...其他处理代码...

    在这个案例中,我们定义了 `CustomStatsCollector` 类,用于统计每个爬虫访问的 URL 数量。使用 `inc_url_count()` 方法增加统计值,并使用 `get_url_count()` 方法获取统计结果。在爬虫结束时,自定义统计数据将被保存到以爬虫名为前缀的 JSON 文件中。

    (可自行在此框架上修改,自己需要的业务逻辑)

    12月26日补

    可以直接拿这个模版,套到自己的中间件:

    (统计那么几个数,然后输出到一个txt里面)

    class MyStatsMiddleware:
        def __init__(self, stats: StatsCollector):
            self.stats = stats
        @classmethod
        def from_crawler(cls, crawler):
            return cls(crawler.stats)
        def process_response(self, request, response, spider):
            if response.status >= 400:  # 如果响应状态码大于等于 400,表示请求失败
                self.stats.inc_value('html_failures')  # 增加 HTML 请求失败的统计计数
        def spider_closed(self, spider):
            requests_count = self.stats.get_value("downloader/request_count", 0)
            items_count = self.stats.get_value("item_scraped_count", 0)
            elapsed_time = self.stats.get_value("finish_time") - self.stats.get_value("start_time")
            html_failures = self.stats.get_value("html_failures", 0)  # 获取 HTML 请求失败总数
            data = {
                "爬取html总量": requests_count,
                "爬取html失败总量": html_failures,
                "存储的item总量": items_count,
                "一共耗时": elapsed_time  #精确到秒数
            }
            with open("任务统计.txt", "w") as f:
                json.dump(data, f)

    别忘了,在setting设置里面,添加这个中间件:

    DOWNLOADER_MIDDLEWARES = {
       "jihai_end.middlewares.DownloaderMiddleware": 543,# 正常中间件
       "jihai_end.middlewares.MyStatsMiddleware": 900,   #统计中间件
    }

    ​​​​​​​