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

2019-05-12 21:08发布

我有一个包含多个蜘蛛一个scrapy项目。 有没有什么办法可以定义要使用针对蜘蛛哪些管道? 不是我定义了所有的管道都适用于每一个蜘蛛。

谢谢

Answer 1:

建立在从巴勃罗·霍夫曼的解决方案 ,你可以使用下面的装饰上process_item通过管道对象的方法,以便它检查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

对于这个装饰才能正常工作,蜘蛛必须与要用来处理项目,例如管道对象的容器管道属性:

class MySpider(BaseSpider):

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

    def parse(self, response):
        # insert scrapy goodness here
        return item

然后在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

所有管道对象应犹在,ITEM_PIPELINES被定义在设置(以正确的顺序 - 将是不错的改变,这样的顺序可以在蜘蛛来指定,太)。



Answer 2:

只是删除从主设置的所有管道,并使用这里面的蜘蛛。

这将定义管道输送到每个用户的蜘蛛

class testSpider(InitSpider):
    name = 'test'
    custom_settings = {
        'ITEM_PIPELINES': {
            'app.MyPipeline': 400
        }
    }


Answer 3:

这里给出的其他解决方案都不错,但我认为他们可能是缓慢的,因为我们不是真的使用每个蜘蛛的管道,而不是我们正在检查,如果管道存在每次返回一个项目的时间(在某些情况下,这可能会达到百万)。

完全禁用(或启用)每蜘蛛功能的一个好方法是使用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

settings.py

ITEM_PIPELINES = {
   'myproject.pipelines.SomePipeline': 300,
}
SOMEPIPELINE_ENABLED = True # you could have the pipeline enabled by default

spider1.py

class Spider1(Spider):

    name = 'spider1'

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

    custom_settings = {
        'SOMEPIPELINE_ENABLED': False
    }

当你检查,我们已经指定custom_settings将覆盖在指定的事情settings.py ,我们正在禁用SOMEPIPELINE_ENABLED这个蜘蛛。

现在,当你运行这个蜘蛛,检查是这样的:

[scrapy] INFO: Enabled item pipelines: []

现在scrapy已经完全禁用管道,而不是全跑了困扰它的存在。 检查,这也适用于scrapy extensionsmiddlewares



Answer 4:

我能想到至少四种方法的:

  1. 使用不同的scrapy项目每套的蜘蛛+管道(可能是合适的,如果你的蜘蛛不同足够令在不同项目中是)
  2. 在scrapy工具命令行中,更改管道与设置scrapy settings你的蜘蛛每次调用之间
  3. 隔离您的蜘蛛到自己scrapy工具命令 ,并定义default_settings['ITEM_PIPELINES']在你的命令类与要使用该命令的管道列表。 看到本实施例的线路6 。
  4. 在管道类本身,有process_item()检查什么蜘蛛它的运行对,什么事都不做,如果它应该是蜘蛛被忽略。 请参阅使用每蜘蛛资源例子 ,让你开始。 (这似乎是一个丑陋的解决方案,因为它紧密结合蜘蛛和项目管道。你可能不应该使用这一个。)


Answer 5:

您可以使用name蜘蛛的属性,在管道

class CustomPipeline(object)

    def process_item(self, item, spider)
         if spider.name == 'spider1':
             # do something
             return item
         return item

定义所有管道这种方式可以完成你想要的。



Answer 6:

你可以这样设置蜘蛛内的项目管道设置:

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

然后我可以分割一个管道(或甚至使用多个管道)加入一个值,以标识哪些蜘蛛的一部分发送的项目通过装载器/退回的项目。 这样,我不会得到任何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


Answer 7:

我使用的两条管道,一个用于图像下载(MyImagesPipeline)和第二对保存在MongoDB中(MongoPipeline)数据。

假设我们有很多蜘蛛(spider1,spider2,...........),在我的例子spider1和spider5不能使用MyImagesPipeline

settings.py

ITEM_PIPELINES = {'scrapycrawler.pipelines.MyImagesPipeline' : 1,'scrapycrawler.pipelines.MongoPipeline' : 2}
IMAGES_STORE = '/var/www/scrapycrawler/dowload'

和波纹管的管道的完整代码

import scrapy
import string
import pymongo
from scrapy.pipelines.images import ImagesPipeline

class MyImagesPipeline(ImagesPipeline):
    def process_item(self, item, spider):
        if spider.name not in ['spider1', 'spider5']:
            return super(ImagesPipeline, self).process_item(item, spider)
        else:
           return item 

    def file_path(self, request, response=None, info=None):
        image_name = string.split(request.url, '/')[-1]
        dir1 = image_name[0]
        dir2 = image_name[1]
        return dir1 + '/' + dir2 + '/' +image_name

class MongoPipeline(object):

    collection_name = 'scrapy_items'
    collection_url='snapdeal_urls'

    def __init__(self, mongo_uri, mongo_db):
        self.mongo_uri = mongo_uri
        self.mongo_db = mongo_db

    @classmethod
    def from_crawler(cls, crawler):
        return cls(
            mongo_uri=crawler.settings.get('MONGO_URI'),
            mongo_db=crawler.settings.get('MONGO_DATABASE', 'scraping')
        )

    def open_spider(self, spider):
        self.client = pymongo.MongoClient(self.mongo_uri)
        self.db = self.client[self.mongo_db]

    def close_spider(self, spider):
        self.client.close()

    def process_item(self, item, spider):
        #self.db[self.collection_name].insert(dict(item))
        collection_name=item.get( 'collection_name', self.collection_name )
        self.db[collection_name].insert(dict(item))
        data = {}
        data['base_id'] = item['base_id']
        self.db[self.collection_url].update({
            'base_id': item['base_id']
        }, {
            '$set': {
            'image_download': 1
            }
        }, upsert=False, multi=True)
        return item


Answer 8:

我们可以使用一些条件,管道,因为这

    # -*- coding: utf-8 -*-
from scrapy_app.items import x

class SaveItemPipeline(object):
    def process_item(self, item, spider):
        if isinstance(item, x,):
            item.save()
        return item


文章来源: How can I use different pipelines for different spiders in a single Scrapy project