如何在单个Scrapy项目中为不同的蜘蛛使用不同的管道

Cod*_*eyB 72 python web-crawler scrapy

我有一个包含多个蜘蛛的scrapy项目.有什么方法可以定义哪些管道用于哪个蜘蛛?并非我所定义的所有管道都适用于每个蜘蛛.

谢谢

Ran*_*ngh 99

只需从主设置中删除所有管道,然后在蜘蛛内部使用它.

这将为每个蜘蛛定义用户的管道

class testSpider(InitSpider):
    name = 'test'
    custom_settings = {
        'ITEM_PIPELINES': {
            'app.MyPipeline': 400
        }
    }
Run Code Online (Sandbox Code Playgroud)

  • 不知道为什么这不是公认的答案,它工作得很好,比公认的答案更干净、更简单。这正是我一直在寻找的。仍在 scrapy 1.8 中工作 (5认同)
  • 对于想知道“400”是什么的人?像我一样 - 从文档中 - “您在此设置中分配给类的整数值决定了它们运行的​​顺序:项目从低值到高值类。通常将这些数字定义在 0-1000 范围内” - https://docs.scrapy.org/en/latest/topics/item-pipeline.html (4认同)
  • 刚刚检查了 scrapy 1.6。无需删除 settings.py 中的管道设置。蜘蛛中的custom_settings会覆盖settings.py中的管道设置。 (3认同)

mst*_*ger 35

基于Pablo Hoffman的解决方案,您可以在process_itemPipeline对象的方法上使用以下装饰器,以便它检查pipeline蜘蛛的属性是否应该执行.例如:

def check_spider_pipeline(process_item_method):

    @functools.wraps(process_item_method)
    def wrapper(self, item, spider):

        # message template for debugging
        msg = '%%s %s pipeline step' % (self.__class__.__name__,)

        # if class is in the spider's pipeline, then use the
        # process_item method normally.
        if self.__class__ in spider.pipeline:
            spider.log(msg % 'executing', level=log.DEBUG)
            return process_item_method(self, item, spider)

        # otherwise, just return the untouched item (skip this step in
        # the pipeline)
        else:
            spider.log(msg % 'skipping', level=log.DEBUG)
            return item

    return wrapper
Run Code Online (Sandbox Code Playgroud)

为使此装饰器正常工作,spider必须具有一个管道属性,其中包含要用于处理项目的Pipeline对象的容器,例如:

class MySpider(BaseSpider):

    pipeline = set([
        pipelines.Save,
        pipelines.Validate,
    ])

    def parse(self, response):
        # insert scrapy goodness here
        return item
Run Code Online (Sandbox Code Playgroud)

然后在一个pipelines.py文件中:

class Save(object):

    @check_spider_pipeline
    def process_item(self, item, spider):
        # do saving here
        return item

class Validate(object):

    @check_spider_pipeline
    def process_item(self, item, spider):
        # do validating here
        return item
Run Code Online (Sandbox Code Playgroud)

所有Pipeline对象仍然应该在ITEM_PIPELINES中的设置中定义(按照正确的顺序 - 更改将很好,以便也可以在Spider上指定顺序).


eLR*_*uLL 12

这里给出的其他解决方案都很好,但我认为它们可能很慢,因为我们实际上并没有使用每个蜘蛛的管道,而是每次返回一个项目时检查是否存在管道(在某些情况下,这可能会达到百万).

完全禁用(或启用)每个蜘蛛的功能的好方法是使用custom_settingfrom_crawler所有扩展,如下所示:

pipelines.py

from scrapy.exceptions import NotConfigured

class SomePipeline(object):
    def __init__(self):
        pass

    @classmethod
    def from_crawler(cls, crawler):
        if not crawler.settings.getbool('SOMEPIPELINE_ENABLED'):
            # if this isn't specified in settings, the pipeline will be completely disabled
            raise NotConfigured
        return cls()

    def process_item(self, item, spider):
        # change my item
        return item
Run Code Online (Sandbox Code Playgroud)

settings.py

ITEM_PIPELINES = {
   'myproject.pipelines.SomePipeline': 300,
}
SOMEPIPELINE_ENABLED = True # you could have the pipeline enabled by default
Run Code Online (Sandbox Code Playgroud)

spider1.py

class Spider1(Spider):

    name = 'spider1'

    start_urls = ["http://example.com"]

    custom_settings = {
        'SOMEPIPELINE_ENABLED': False
    }
Run Code Online (Sandbox Code Playgroud)

当您检查时,我们已指定custom_settings将覆盖指定的内容settings.py,并且我们正在禁用SOMEPIPELINE_ENABLED此蜘蛛.

现在当你运行这个蜘蛛时,请检查以下内容:

[scrapy] INFO: Enabled item pipelines: []
Run Code Online (Sandbox Code Playgroud)

现在scrapy已经完全禁用了管道,并没有为整个运行而烦恼.检查这也适用于scrapy extensionsmiddlewares.


Fra*_*ila 10

我至少可以想到四种方法:

  1. 每套蜘蛛+管道使用不同的scrapy项目(如果你的蜘蛛在不同的项目中有足够的不同,可能是合适的)
  2. 在scrapy工具命令行中,scrapy settings在每次调用spider之间更改管道设置
  3. 将您的蜘蛛隔离到他们自己的scrapy工具命令中,并将default_settings['ITEM_PIPELINES']命令类定义到您希望该命令的管道列表.见本例的第6行.
  4. 在管道类本身中,process_item()检查它正在运行的蜘蛛是什么,如果该蜘蛛应该被忽略则不执行任何操作.请参阅使用每个蜘蛛资源示例来帮助您入门.(这似乎是一个丑陋的解决方案,因为它紧密地耦合了蜘蛛和物品管道.你可能不应该使用这个.)


pad*_*pad 9

您可以name在管道中使用spider 的属性

class CustomPipeline(object)

    def process_item(self, item, spider)
         if spider.name == 'spider1':
             # do something
             return item
         return item
Run Code Online (Sandbox Code Playgroud)

以这种方式定义所有管道可以实现您想要的.


def*_*ngs 9

最简单有效的解决方案是在每个蜘蛛本身中设置自定义设置。

custom_settings = {'ITEM_PIPELINES': {'project_name.pipelines.SecondPipeline': 300}}
Run Code Online (Sandbox Code Playgroud)

之后,您需要在 settings.py 文件中设置它们

ITEM_PIPELINES = {
   'project_name.pipelines.FistPipeline': 300,
   'project_name.pipelines.SecondPipeline': 400
}
Run Code Online (Sandbox Code Playgroud)

这样每个蜘蛛都会使用各自的管道。

  • 截至 2020 年,这是解决该问题的最干净的解决方案。 (3认同)

小智 6

您可以像这样在蜘蛛内部设置项目管道设置:

class CustomSpider(Spider):
    name = 'custom_spider'
    custom_settings = {
        'ITEM_PIPELINES': {
            '__main__.PagePipeline': 400,
            '__main__.ProductPipeline': 300,
        },
        'CONCURRENT_REQUESTS_PER_DOMAIN': 2
    }
Run Code Online (Sandbox Code Playgroud)

然后,我可以通过向加载器/返回的项目添加一个值来拆分管道(甚至使用多个管道),该值标识蜘蛛的哪个部分发送了项目。这样我就不会得到任何 KeyError 异常,我知道哪些项目应该可用。

    ...
    def scrape_stuff(self, response):
        pageloader = PageLoader(
                PageItem(), response=response)

        pageloader.add_xpath('entire_page', '/html//text()')
        pageloader.add_value('item_type', 'page')
        yield pageloader.load_item()

        productloader = ProductLoader(
                ProductItem(), response=response)

        productloader.add_xpath('product_name', '//span[contains(text(), "Example")]')
        productloader.add_value('item_type', 'product')
        yield productloader.load_item()

class PagePipeline:
    def process_item(self, item, spider):
        if item['item_type'] == 'product':
            # do product stuff

        if item['item_type'] == 'page':
            # do page stuff
Run Code Online (Sandbox Code Playgroud)