【问题标题】:suppress Scrapy Item printed in logs after pipeline抑制管道后在日志中打印的 Scrapy Item
【发布时间】:2025-12-13 14:55:02
【问题描述】:

我有一个 scrapy 项目,其中最终进入我的管道的项目相对较大,并且存储了大量元数据和内容。一切都在我的蜘蛛和管道中正常工作。然而,当它离开管道时,日志会打印出整个scrapy Item(我相信):

2013-01-17 18:42:17-0600 [tutorial] DEBUG: processing Pipeline pipeline module
2013-01-17 18:42:17-0600 [tutorial] DEBUG: Scraped from <200 http://www.example.com>
    {'attr1': 'value1',
     'attr2': 'value2',
     'attr3': 'value3',
     ...
     snip
     ...
     'attrN': 'valueN'}
2013-01-17 18:42:18-0600 [tutorial] INFO: Closing spider (finished)

如果可以避免的话,我宁愿不要将所有这些数据都放入日志文件中。有关如何抑制此输出的任何建议?

【问题讨论】:

    标签: python scrapy


    【解决方案1】:

    另一种方法是覆盖Item 子类的__repr__ 方法,以选择性地选择在管道末尾打印哪些属性(如果有):

    from scrapy.item import Item, Field
    class MyItem(Item):
        attr1 = Field()
        attr2 = Field()
        # ...
        attrN = Field()
    
        def __repr__(self):
            """only print out attr1 after exiting the Pipeline"""
            return repr({"attr1": self.attr1})
    

    这样,您可以将日志级别保持在DEBUG,并仅显示您希望从管道中看到的属性(例如,检查attr1)。

    【讨论】:

    • 为我工作,使用return repr({"attr1": self["attr1"]})
    • 对我来说应该是return repr({'attr1':self['attr1']})
    【解决方案2】:

    通读文档并(简要)搜索源代码后,我看不出实现这一目标的直接方法。

    锤子的做法是将settings中的日志级别设置为INFO(即在settings.py中加入以下行):

    LOG_LEVEL='INFO'

    这将去除有关正在抓取的 URL/页面的许多其他信息,但它肯定会抑制有关已处理项目的数据。

    【讨论】:

      【解决方案3】:

      我尝试了@dino提到的repre方式,效果不好。但从他的想法演变而来,我尝试了 str 方法,并且成功了。

      这是我的做法,非常简单:

          def __str__(self):
              return ""
      

      【讨论】:

        【解决方案4】:

        如果只想排除输出的某些属性,可以扩展@dino给出的答案

        from scrapy.item import Item, Field
        import json
        
        class MyItem(Item):
            attr1 = Field()
            attr2 = Field()
            attr1ToExclude = Field()
            attr2ToExclude = Field()
            # ...
            attrN = Field()
        
            def __repr__(self):
                r = {}
                for attr, value in self.__dict__['_values'].iteritems():
                    if attr not in ['attr1ToExclude', 'attr2ToExclude']:
                        r[attr] = value
                return json.dumps(r, sort_keys=True, indent=4, separators=(',', ': '))
        

        【讨论】:

          【解决方案5】:

          如果您因为多年后遇到同样的问题而找到了自己的方式,那么最简单的方法是使用LogFormatter

          class QuietLogFormatter(scrapy.logformatter.LogFormatter):
              def scraped(self, item, response, spider):
                  return (
                      super().scraped(item, response, spider)
                      if spider.settings.getbool("LOG_SCRAPED_ITEMS")
                      else None
                  )
          

          只需将LOG_FORMATTER = "path.to.QuietLogFormatter" 添加到您的settings.py,您就会看到除已抓取的项目之外的所有DEBUG 消息。使用LOG_SCRAPED_ITEMS = True,您可以恢复以前的行为,而无需更改您的LOG_FORMATTER

          同样,您可以自定义已抓取页面和丢弃项目的日志记录行为。

          编辑:我总结了 this formatter 和其他一些 Scrapy 的东西 in this library

          【讨论】:

            【解决方案6】:

            或者如果您知道蜘蛛正常工作,那么您可以禁用整个日志记录

            LOG_ENABLED = False

            当我的爬虫运行良好时我禁用它

            【讨论】:

              【解决方案7】:

              我认为最干净的方法是向scrapy.core.scraper 记录器添加一个过滤器,以更改相关消息。这使您可以保持您的项目的__repr__ 完整,并且不必更改scrapy 的日志记录级别:

              import re
              
              class ItemMessageFilter(logging.Filter):
                  def filter(self, record):
                      # The message that logs the item actually has raw % operators in it,
                      # which Scrapy presumably formats later on
                      match = re.search(r'(Scraped from %\(src\)s)\n%\(item\)s', record.msg)
                      if match:
                          # Make the message everything but the item itself
                          record.msg = match.group(1)
                      # Don't actually want to filter out this record, so always return 1
                      return 1
              
              logging.getLogger('scrapy.core.scraper').addFilter(ItemMessageFilter())
              

              【讨论】:

              • match = re.search(r'(Scraped from %\(src\)s)\r\n%\(item\)s', record.msg) 为我工作
              【解决方案8】:

              我们在生产中使用以下示例:

              import logging
              
              logging.getLogger('scrapy.core.scraper').addFilter(
                  lambda x: not x.getMessage().startswith('Scraped from'))
              

              这是一个非常简单且有效的代码。我们将此代码添加到带有蜘蛛的模块中的__init__.py 中。在这种情况下,此代码会自动使用 scrapy crawl &lt;spider_name&gt; 之类的命令为所有蜘蛛运行。

              【讨论】:

              • 谢谢我试过这个,它抑制了报废的结果,所以日志文件的大小不是我想要的那么大。