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