Làm cách nào để sử dụng các đường ống dẫn khác nhau cho các loại nhện khác nhau trong một dự án Trị liệu


84

Tôi có một dự án trị liệu chứa nhiều nhện. Có cách nào tôi có thể xác định đường ống dẫn để sử dụng cho con nhện nào không? Không phải tất cả các đường ống mà tôi đã xác định đều có thể áp dụng cho mọi mạng nhện.

Cảm ơn


2
Cảm ơn bạn vì câu hỏi rất hay của bạn. Vui lòng chọn một câu trả lời cho tất cả các googlers trong tương lai. Câu trả lời được cung cấp bởi mstringer rất hiệu quả đối với tôi.
symbiotech

Câu trả lời:


35

Dựa trên giải pháp từ Pablo Hoffman , bạn có thể sử dụng trình trang trí sau trên process_itemphương thức của một đối tượng Pipeline để nó kiểm tra pipelinethuộc tính của spider của bạn xem nó có nên được thực thi hay không. Ví dụ:

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

Để trình trang trí này hoạt động chính xác, con nhện phải có thuộc tính đường ống với một vùng chứa các đối tượng Đường ống mà bạn muốn sử dụng để xử lý mặt hàng, ví dụ:

class MySpider(BaseSpider):

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

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

Và sau đó trong một pipelines.pytệp:

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

Tất cả các đối tượng Pipeline vẫn phải được xác định trong ITEM_PIPELINES trong cài đặt (theo đúng thứ tự - sẽ rất tốt nếu thay đổi để thứ tự cũng có thể được chỉ định trên Spider).


Tôi đang cố gắng thực hiện cách chuyển đổi giữa các đường ống của bạn, tuy nhiên, tôi nhận được NameError! Tôi nhận được đường ống không được xác định. bạn đã tự mình kiểm tra mã này chưa? bạn sẽ giúp tôi?
mehdix_

. @ mehdix_ vâng, nó phù hợp với tôi. Bạn lấy NameError ở đâu?
mstringer

Lỗi xuất hiện ngay sau scrapy crawl <spider name>lệnh. python không nhận ra tên tôi đặt trong lớp spider để các đường ống chạy. Tôi sẽ cung cấp cho bạn liên kết đến tôi spider.pypipeline.py để bạn có thể có một cái nhìn. Cảm ơn
mehdix_

1
Cảm ơn đã làm rõ. đoạn mã đầu tiên đi đâu? ở đâu đó ở cuối spider.pybên phải?
mehdix_

1
Tôi đã chỉnh sửa điều kiện không thành công trên các trình thu thập thông tin đã được xác định chưa đặt đường ống, điều này cũng sẽ làm cho nó thực thi tất cả các đường ống theo mặc định trừ khi được yêu cầu khác. if not hasattr(spider, 'pipeline') or self.__class__ in spider.pipeline:
Nour Wolf,

138

Chỉ cần xóa tất cả các đường ống dẫn khỏi cài đặt chính và sử dụng spider bên trong này.

Điều này sẽ xác định đường dẫn đến người dùng trên mỗi con nhện

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

3
cho những ai đang tự hỏi '400' là gì? như tôi - TỪ TÀI LIỆU - "Các giá trị số nguyên mà bạn gán cho các lớp trong cài đặt này xác định thứ tự mà chúng chạy: các mục đi qua từ các lớp có giá trị thấp hơn đến các lớp có giá trị cao hơn. Thông thường, xác định các số này trong phạm vi 0-1000" - docs.scrapy.org/en/latest/topics/item-pipeline.html
brainLoop

2
Không chắc tại sao đây không phải là câu trả lời được chấp nhận, hoạt động hoàn hảo, sạch sẽ và đơn giản hơn nhiều so với câu trả lời được chấp nhận. Điều này thật đúng với gì mà tôi đã tìm kiếm. Vẫn làm việc trong scrapy 1.8
Eric F

1
Chỉ cần kiểm tra trong liệu pháp 1.6. Không cần thiết phải xóa cài đặt đường dẫn trong settings.py. custom_settings trong cài đặt đường ống ghi đè nhện trong settings.py.
Graham Monkman

Hoạt động hoàn hảo cho kịch bản của tôi!
Mark Kamyszek

cho 'app.MyPipeline' thay thế tên đầy đủ của lớp đường ống. Ví dụ: project.pipelines.MyPipeline trong đó dự án là tên của dự án, đường ống là tệp pipelines.py và MyPipeline là lớp Pipeline
Nava Bogatee

13

Các giải pháp khác được đưa ra ở đây là tốt, nhưng tôi nghĩ chúng có thể chậm, bởi vì chúng tôi không thực sự không sử dụng đường ống cho mỗi con nhện, thay vào đó chúng tôi đang kiểm tra xem đường ống có tồn tại mỗi khi một mặt hàng được trả lại hay không (và trong một số trường hợp, điều này có thể đạt được hàng triệu).

Một cách tốt để tắt hoàn toàn (hoặc bật) một tính năng mà mỗi con nhện đang sử dụng custom_settingfrom_crawlercho tất cả các tiện ích mở rộng như sau:

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
    }

Khi bạn kiểm tra, chúng tôi đã chỉ định custom_settingsđiều đó sẽ ghi đè những thứ được chỉ định trong settings.pyvà chúng tôi đang vô hiệu hóa trình thu thập dữ liệu SOMEPIPELINE_ENABLEDnày.

Bây giờ khi bạn chạy con nhện này, hãy kiểm tra một cái gì đó như:

[scrapy] INFO: Enabled item pipelines: []

Bây giờ liệu pháp đã hoàn toàn vô hiệu hóa đường ống, không bận tâm đến sự tồn tại của nó trong suốt quá trình. Kiểm tra xem điều này cũng có tác dụng đối với liệu pháp extensionsmiddlewares.


11

Tôi có thể nghĩ ra ít nhất bốn cách tiếp cận:

  1. Sử dụng một dự án trị liệu khác cho mỗi bộ nhện + đường ống (có thể phù hợp nếu các loài nhện của bạn đủ khác nhau để đảm bảo cho các dự án khác nhau)
  2. Trên dòng lệnh công cụ trị liệu, thay đổi cài đặt đường ống dẫn scrapy settingsgiữa mỗi lần gọi nhện của bạn
  3. Cô lập các trình thu thập thông tin của bạn thành các lệnh công cụ xử lý của riêng chúng và xác định default_settings['ITEM_PIPELINES']trên lớp lệnh của bạn thành danh sách đường ống mà bạn muốn cho lệnh đó. Xem dòng 6 của ví dụ này .
  4. Trong chính các lớp đường ống, process_item()hãy kiểm tra xem nó đang chống lại con nhện nào và không làm gì nếu nên bỏ qua con nhện đó. Xem ví dụ về cách sử dụng tài nguyên trên mỗi con nhện để giúp bạn bắt đầu. (Đây có vẻ là một giải pháp xấu vì nó kết hợp chặt chẽ giữa nhện và đường ống dẫn vật phẩm. Bạn có thể không nên sử dụng giải pháp này.)

Cám ơn phản hồi của bạn. Tôi đang sử dụng phương pháp 1 nhưng tôi cảm thấy có một dự án sạch hơn và cho phép tôi sử dụng lại mã. bạn có thể vui lòng giải thích thêm về phương pháp 3. Làm cách nào để tách các spider thành các lệnh công cụ của riêng chúng?
CodeMonkeyB

Theo liên kết được đăng trên một câu trả lời khác, bạn không thể ghi đè đường ống dẫn, vì vậy tôi đoán số 3 sẽ không hoạt động.
Daniel Bang

bạn có thể giúp tôi ở đây lời cầu xin? stackoverflow.com/questions/25353650/…
Marco Dinatsoli,

11

Bạn có thể sử dụng namethuộc tính của con nhện trong đường dẫn của mình

class CustomPipeline(object)

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

Xác định tất cả các đường ống theo cách này có thể đạt được những gì bạn muốn.


4

Bạn chỉ có thể đặt cài đặt đường ống mục bên trong spider như sau:

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

Sau đó, tôi có thể chia nhỏ một đường ống (hoặc thậm chí sử dụng nhiều đường ống) bằng cách thêm một giá trị vào trình tải / mục trả về để xác định phần nào của trình thu thập dữ liệu đã gửi các mục qua. Bằng cách này, tôi sẽ không nhận được bất kỳ ngoại lệ KeyError nào và tôi biết những mục nào sẽ có sẵn.

    ...
    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

1
Đây phải là câu trả lời được chấp nhận. Linh hoạt hơn và ít cồng kềnh hơn
Ben Wilson

1

Giải pháp đơn giản nhưng vẫn hữu ích.

Mã nhện

    def parse(self, response):
        item = {}
        ... do parse stuff
        item['info'] = {'spider': 'Spider2'}

mã đường ống

    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')

Hy vọng điều này tiết kiệm thời gian cho ai đó!


0

Tôi đang sử dụng hai đường dẫn, một để tải xuống hình ảnh (MyImagesPipeline) và thứ hai để lưu dữ liệu trong mongodb (MongoPipeline).

giả sử chúng ta có nhiều nhện (spider1, spider2, ...........), trong ví dụ của tôi, spider1 và spider5 không thể sử dụng MyImagesPipeline

settings.py

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

Và mã hoàn chỉnh dưới đây của đường ống

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

0

chúng ta có thể sử dụng một số điều kiện trong đường dẫn như sau

    # -*- 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

0

Giải pháp đơn giản và hiệu quả nhất là đặt cài đặt tùy chỉnh trong chính mỗi con nhện.

custom_settings = {'ITEM_PIPELINES': {'project_name.pipelines.SecondPipeline': 300}}

Sau đó, bạn cần đặt chúng trong tệp settings.py

ITEM_PIPELINES = {
   'project_name.pipelines.FistPipeline': 300,
   'project_name.pipelines.SecondPipeline': 300
}

theo cách đó, mỗi con nhện sẽ sử dụng đường ống tương ứng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.