我有一个令人毛骨悚然的项目,其中包含多个蜘蛛。我有什么方法可以定义为哪个蜘蛛使用哪个管道?并非我定义的所有管道都适用于每个蜘蛛。
谢谢
我有一个令人毛骨悚然的项目,其中包含多个蜘蛛。我有什么方法可以定义为哪个蜘蛛使用哪个管道?并非我定义的所有管道都适用于每个蜘蛛。
谢谢
Answers:
基于Pablo Hoffman的解决方案,您可以在process_item
Pipeline对象的方法上使用以下装饰器,以便它检查pipeline
Spider的属性是否应执行。例如:
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
所有Pipeline对象仍应在ITEM_PIPELINES中的设置中定义(以正确的顺序进行更改-这样很好,以便可以在Spider上指定顺序)。
scrapy crawl <spider name>
命令后出现。python无法识别我在Spider类中设置的名称,以便运行管道。我将为您提供指向我的spider.py和pipeline.py的链接,供您查看。谢谢
spider.py
右端的某个地方?
if not hasattr(spider, 'pipeline') or self.__class__ in spider.pipeline:
只需从主要设置中删除所有管道,然后在Spider中使用它即可。
这将定义每个蜘蛛到用户的管道
class testSpider(InitSpider):
name = 'test'
custom_settings = {
'ITEM_PIPELINES': {
'app.MyPipeline': 400
}
}
此处给出的其他解决方案很好,但我认为它们可能会很慢,因为我们并不是真正不使用每个蜘蛛的流水线,而是我们每次检查一个项目时是否检查流水线是否存在(在某些情况下可能达到百万)。
一个完全禁用(或启用)每个Spider功能的好方法是对所有扩展使用custom_setting
,from_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
蜘蛛1
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完全禁用了该管道,因此在整个运行过程中都不会打扰它的存在。检查这是否也适用于刮擦extensions
和刮擦middlewares
。
我至少可以想到四种方法:
scrapy settings
在每次调用Spider之间更改管道设置default_settings['ITEM_PIPELINES']
在命令类中将定义到该命令所需的管道列表。请参见本示例的第6行。process_item()
检查它所针对的蜘蛛,如果对该蜘蛛应该忽略它,则不执行任何操作。请参阅使用每个蜘蛛资源的示例来开始使用。(这似乎是一个丑陋的解决方案,因为它紧密结合了蜘蛛程序和项目管道。您可能不应该使用此程序。)您可以像这样在Spider内部设置项目管道设置:
class CustomSpider(Spider):
name = 'custom_spider'
custom_settings = {
'ITEM_PIPELINES': {
'__main__.PagePipeline': 400,
'__main__.ProductPipeline': 300,
},
'CONCURRENT_REQUESTS_PER_DOMAIN': 2
}
然后,我可以通过在加载器/返回的项目中添加一个值来拆分流水线(甚至使用多个流水线),该值可以标识Spider的哪一部分发送了数据。这样,我将不会收到任何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
简单但仍然有用的解决方案。
蜘蛛码
def parse(self, response):
item = {}
... do parse stuff
item['info'] = {'spider': 'Spider2'}
流水线代码
def process_item(self, item, spider):
if item['info']['spider'] == 'Spider1':
logging.error('Spider1 pipeline works')
elif item['info']['spider'] == 'Spider2':
logging.error('Spider2 pipeline works')
elif item['info']['spider'] == 'Spider3':
logging.error('Spider3 pipeline works')
希望这可以节省一些时间!
我正在使用两个管道,一个用于图像下载(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