Python Scrapy-无声地删除一个项目

Python Scrapy-无声地删除一个项目,python,scrapy,Python,Scrapy,我正在使用Scrapy抓取几个网站,这些网站可能共享冗余信息 对于我抓取的每个页面,我将页面的url、标题和html代码存储到mongoDB中。 我希望避免数据库中的重复,因此,我实现了一个管道,以检查是否已经存储了类似的项。在这种情况下,我提出了一个DropItem异常 我的问题是,每当我在DropItem异常上按原因删除一个项目时,Scrapy都会将该项目的全部内容显示在日志(stdout或文件)中。 当我提取每个刮取的页面的整个HTML代码时,如果出现下降,整个HTML代码将显示在日志中

我正在使用Scrapy抓取几个网站,这些网站可能共享冗余信息

对于我抓取的每个页面,我将页面的url、标题和html代码存储到mongoDB中。 我希望避免数据库中的重复,因此,我实现了一个管道,以检查是否已经存储了类似的项。在这种情况下,我提出了一个
DropItem
异常

我的问题是,每当我在
DropItem
异常上按原因删除一个项目时,Scrapy都会将该项目的全部内容显示在日志(stdout或文件)中。 当我提取每个刮取的页面的整个HTML代码时,如果出现下降,整个HTML代码将显示在日志中

我怎么能在不显示内容的情况下默默地删除项目

谢谢你抽出时间

class DatabaseStorage(object):
    """ Pipeline in charge of database storage.

    The 'whole' item (with HTML and text) will be stored in mongoDB.
    """

    def __init__(self):
        self.mongo = MongoConnector().collection

    def process_item(self, item, spider):
        """ Method in charge of item valdation and processing. """
        if item['html'] and item['title'] and item['url']:
            # insert item in mongo if not already present
            if self.mongo.find_one({'title': item['title']}):
                raise DropItem('Item already in db')
            else:
                self.mongo.insert(dict(item))
                log.msg("Item %s scraped" % item['title'],
                    level=log.INFO, spider=spider)
        else:
            raise DropItem('Missing information on item %s' % (
                'scraped from ' + item.get('url')
                or item.get('title')))
        return item

好的,我在发布问题之前就找到了答案。 我仍然认为这个答案对任何有同样问题的人来说都是有价值的

您只需返回一个None值,而不是使用
DropItem
异常删除对象:

def process_item(self, item, spider):
    """ Method in charge of item valdation and processing. """
    if item['html'] and item['title'] and item['url']:
        # insert item in mongo if not already present
        if self.mongo.find_one({'url': item['url']}):
            return
        else:
            self.mongo.insert(dict(item))
            log.msg("Item %s scraped" % item['title'],
                level=log.INFO, spider=spider)
    else:
        raise DropItem('Missing information on item %s' % (
           'scraped from ' + item.get('url')
            or item.get('title')))
    return item

正确的方法是为您的项目实现一个定制的LogFormatter,并更改已删除项的日志记录级别

例如:

from scrapy import log
from scrapy import logformatter

class PoliteLogFormatter(logformatter.LogFormatter):
    def dropped(self, item, exception, response, spider):
        return {
            'level': log.DEBUG,
            'format': logformatter.DROPPEDFMT,
            'exception': exception,
            'item': item,
        }
然后在您的设置文件中,类似于:

LOG_FORMATTER = 'apps.crawler.spiders.PoliteLogFormatter'

我运气不好,只是返回了“None”,这在将来的管道中导致了异常。

在最近的Scrapy版本中,这一点有了一些改变。我从@jimmythelaf复制了代码,并将其修复为使用最近的Scrapy:

import logging
from scrapy import logformatter


class PoliteLogFormatter(logformatter.LogFormatter):
    def dropped(self, item, exception, response, spider):
        return {
            'level': logging.INFO,
            'msg': logformatter.DROPPEDMSG,
            'args': {
                'exception': exception,
                'item': item,
            }
        }

这个问题的另一个解决方案是调整
scrapy.Item
子类中的
repr
方法

class SomeItem(scrapy.Item):
    scrape_date = scrapy.Field()
    spider_name = scrapy.Field()
    ...

    def __repr__(self):
        return ""

这样,该项就不会出现在日志中。

正如Levon在其先前的评论中所指出的,也可能会使您正在处理的项的_repr_____函数过载

通过这种方式,消息将显示在Scrapy日志中,但您无法控制要在日志中显示的代码长度,例如,网页的前150个字符。 假设您有这样一个表示HTML页面的项,_repr_uuu的重载可能如下所示:

class MyHTMLItem(Scrapy.Item):
    url = scrapy.Field()
    htmlcode = scrapy.Field()
    [...]
    def __repr__(self):
        s = ""
        s += "URL: %s\n" % self.get('URL')
        s += "Code (chunk): %s\n" % ((self.get('htmlcode'))[0:100])
        return s

执行此操作将输出一个包含字符串“None”的调试级日志项,而不是一个包含已删除项的警告级日志项。在
--loglevel=INFO
或更高级别,这是一个公平的解决方案。理想情况下,
scrapy.core.scraper.scraper
应该允许轻松访问
\u itemproc\u finished
@jah中的输出配置是正确的。“jimmytheleaf”的解决方案在本例中是正确的。返回
None
对象的问题是,它可能无法与某些中间件扩展一起工作,即使是
FeedExporter
类这样的核心扩展,在使用
-o file.csv
选项时负责将结果导出到文件中,您将在日志中看到大量错误,
None
对象无法序列化。这是一个坏主意,因为返回
None
不会停止管道进程,并且仍会进行进一步处理。i、 e.如果您在pipelineA中返回
None
,pipelineB仍将在
process\u item
中处理
None
,并且很可能会中断,因为它预期的是
item
dict
而不是
None
。这是怎么回事?中间商?管道?@Xodarap777,我认为
middleware.py
文件更合适。或者您可以创建新文件,例如
logformatter.py
。这个答案中的代码提供了使用spider将代码放入文件中的功能。注意:这段代码不推荐使用,但是下面的@mirosval的答案已经更新了工作版本。它对我来说非常有用!我建议将
'level':logging.INFO,
更改为
'level':logging.DEBUG,
,并在settings.py文件中提到
LOG\u FORMATTER='…PoliteLogFormatter'
,它将在拖放时显示几乎为空的行。不是吗?