自定义scrapy xml + rss导出到S3

时间:2016-07-09 07:19:46

标签: scrapy

我正在尝试创建一个自定义xml Feed,它将包含蜘蛛抓取的项目,以及存储在spider定义中的一些其他高级信息。输出应存储在S3上。

所需的输出如下所示:

<xml>
  <title>my title defined in the spider</title>
  <description>The description from the spider</description>
  <items>
    <item>...</item>
  </items>
</xml>

为此,我定义了一个自定义导出器,它能够在本地导出所需的输出文件。

spider.py:

class DmozSpider(scrapy.Spider):
    name = 'dmoz'
    allowed_domains = ['dmoz.org']
    start_urls = ['http://www.dmoz.org/Computers/']
    title = 'The DMOZ super feed'

    def parse(self, response):
        ...
        yield item

exporters.py:

from scrapy.conf import settings

class CustomItemExporter(XmlItemExporter):

    def __init__(self, *args, **kwargs):
        self.title = kwargs.pop('title', 'no title found')
        self.link = settings.get('FEED_URI', 'localhost')
        super(CustomItemExporter, self).__init__(*args, **kwargs)

    def start_exporting(self):
       ...
       self._export_xml_field('title', self.title)
       ...

settings.py:

FEED_URI = 's3://bucket-name/%(name)s.xml'
FEED_EXPORTERS = {
    'custom': 'my.exporters.CustomItemExporter',
}

我能够通过运行以下命令来运行整个事件并在s3上获得输出:

scrapy crawl dmoz -t custom

或者,如果我想在本地导出json:scrapy crawl -o dmoz.json dmoz

但是在这一点上,我无法检索蜘蛛标题以将其放入输出文件中。

我尝试实现一个自定义管道,它在本地输出数据(以下是众多例子):

pipelines.py:

class CustomExportPipeline(object):

    def __init__(self):
        self.files = {}

    @classmethod
    def from_crawler(cls, crawler):
         pipeline = cls()
         crawler.signals.connect(pipeline.spider_opened, signals.spider_opened)
         crawler.signals.connect(pipeline.spider_closed, signals.spider_closed)
         return pipeline

    def spider_opened(self, spider):
        file = open('%s_feed.xml' % spider.name, 'w+b') 
        self.files[spider] = file
        self.exporter = CustomItemExporter(
            file,
            title = spider.title),
        )
        self.exporter.start_exporting()

问题是,文件存储在本地,这会使Feedexport.py中定义的FeedExporter逻辑短路,处理所有不同的存储。 管道中没有来自FeedExporter的信息,我想重用所有逻辑而不重复代码。我错过了什么吗?谢谢你的帮助。

1 个答案:

答案 0 :(得分:1)

这是我的解决方案:

  1. 摆脱管道。
  2. 覆盖scrapy的FeedExporter

    <强>的myproject / feedexport.py:

    from scrapy.extensions.feedexport import FeedExporter as _FeedExporter
    from scrapy.extensions.feedexport import SpiderSlot
    
    class FeedExporter(_FeedExporter):
    
        def open_spider(self, spider):
            uri = self.urifmt % self._get_uri_params(spider)
            storage = self._get_storage(uri)
            file = storage.open(spider)
            extra = { 
                # my extra settings
            }
            exporter = self._get_exporter(file, fields_to_export=self.export_fields, extra=extra)
            exporter.start_exporting()
            self.slot = SpiderSlot(file, exporter, storage, uri)
    

    我想要做的只是将这些额外的设置传递给导出器,但是它的构建方式,除了覆盖之外别无选择。 为了同时支持其他scrapy导出格式,我必须考虑在某些scrapy导出器中将dont_fail设置覆盖为True以防止它们失败

  3. 用新的替换scrapy的Feed导出器

    <强>的myproject / feedexport.py:

    EXTENSIONS = {
        'scrapy.extensions.feedexport.FeedExporter': None,
        'myproject.feedexport.FeedExporter': 0,
    }
    

    ...或者2个Feed导出器会同时运行