5

スクレイピーが保存され、アイテムがスパイダーに保存されているかどうかをどのように検出できますか? サイトからアイテムを取得してから、そのアイテムに関するコメントを取得しています。最初にアイテムを保存してから、コメントを保存します。しかし、yield の後にコードを書いていると、このエラーが発生します。

save() prohibited to prevent data loss due to unsaved related object ''.

そして、これは私のコードです

def parseProductComments(self, response):

        name = response.css('h1.product-name::text').extract_first()
        price = response.css('span[id=offering-price] > span::text').extract_first()
        node = response.xpath("//script[contains(text(),'var utagData = ')]/text()")
        data = node.re('= (\{.+\})')[0]  #data = xpath.re(" = (\{.+\})")
        data = json.loads(data)

        barcode = data['product_barcode']

        objectImages = []
        for imageThumDiv in response.css('div[id=productThumbnailsCarousel]'):
            images = imageThumDiv.xpath('img/@data-src').extract()
            for image in images:
                imageQuality = image.replace('/80/', '/500/')
                objectImages.append(imageQuality)
        company = Company.objects.get(pk=3)
        comments = []
        item = ProductItem(name=name, price=price, barcode=barcode, file_urls=objectImages, product_url=response.url,product_company=company, comments = comments)
        yield item
        print item["pk"]
        for commentUl in response.css('ul.chevron-list-container'):

            url = commentUl.css('span.link-more-results::attr(href)').extract_first()
            if url is not None:
                for commentLi in commentUl.css('li.review-item'):
                    comment = commentLi.css('p::text').extract_first()
                    commentItem = CommentItem(comment=comment, product=item.instance)

                    yield commentItem
            else:

                yield scrapy.Request(response.urljoin(url), callback=self.parseCommentsPages, meta={'item': item.instance})

これが私のパイプラインです。

def comment_to_model(item):
    model_class = getattr(item, 'Comment')
    if not model_class:
        raise TypeError("Item is not a `DjangoItem` or is misconfigured")

def get_comment_or_create(model):
    model_class = type(model)
    created = False
    # Normally, we would use `get_or_create`. However, `get_or_create` would
    # match all properties of an object (i.e. create a new object
    # anytime it changed) rather than update an existing object.
    #
    # Instead, we do the two steps separately
    try:
        # We have no unique identifier at the moment; use the name for now.
        obj = model_class.objects.get(product=model.product, comment=model.comment)
    except model_class.DoesNotExist:
        created = True
        obj = model  # DjangoItem created a model for us.
        obj.save()

    return (obj, created)

def get_or_create(model):
    model_class = type(model)
    created = False
    # Normally, we would use `get_or_create`. However, `get_or_create` would
    # match all properties of an object (i.e. create a new object
    # anytime it changed) rather than update an existing object.
    #
    # Instead, we do the two steps separately
    try:
        # We have no unique identifier at the moment; use the name for now.
        obj = model_class.objects.get(product_company=model.product_company, barcode=model.barcode)
    except model_class.DoesNotExist:
        created = True
        obj = model  # DjangoItem created a model for us.
        obj.save()

    return (obj, created)


def update_model(destination, source, commit=True):
    pk = destination.pk

    source_dict = model_to_dict(source)
    for (key, value) in source_dict.items():
        setattr(destination, key, value)

    setattr(destination, 'pk', pk)

    if commit:
        destination.save()
    return destination


class ProductItemPipeline(object):
    def process_item(self, item, spider):
        if isinstance(item, ProductItem):
            item['cover_photo'] = item['files'][0]['path']
            item_model = item.instance
            model, created = get_or_create(item_model)
            #update_model(model, item_model)

            if created:
                for image in item['files']:
                    imageItem = ProductImageItem(image=image['path'], product=item.instance)
                    imageItem.save()
                # for comment in item['comments']:
                #     commentItem = CommentItem(comment=comment, product= item.instance)
                #     commentItem.save()
            return item
        if isinstance(item, CommentItem):
            comment_to_model = item.instance
            model, created = get_comment_or_create(comment_to_model)
            if created:
                print model
            else:
                print created
            return item
4

1 に答える 1

1

取得または作成

コードの大部分は、get_or_create の明らかな弱点に対処しているようです

# Normally, we would use `get_or_create`. However, `get_or_create` would
# match all properties of an object (i.e. create a new object
# anytime it changed) rather than update an existing object.

幸いなことに、この明らかな欠点は簡単に克服できます。get_or_createのデフォルト パラメータのおかげで

get_or_create() に渡されるすべてのキーワード引数 — defaults と呼ばれるオプションのものを除く — は、get() 呼び出しで使用されます。オブジェクトが見つかった場合、get_or_create() はそのオブジェクトと False のタプルを返します。複数のオブジェクトが見つかった場合、get_or_create は MultipleObjectsReturned を発生させます。オブジェクトが見つからない場合、get_or_create() は新しいオブジェクトをインスタンス化して保存し、新しいオブジェクトと True のタプルを返します。

更新または作成

get_or_create がその仕事に適しているとまだ確信していませんか? 私もそうではない。さらに良いものがあります。update_or_create !!

指定された kwargs でオブジェクトを更新し、必要に応じて新しいオブジェクトを作成するための便利なメソッド。デフォルトは、オブジェクトの更新に使用される (フィールド、値) ペアのディクショナリです。

しかし、update_or_create のユーザーについて詳しく説明するつもりはありません。モデルを更新しようとするコード内の行がコメントアウトされており、何を更新したいかを明確に述べていないからです。

新しいパイプライン

標準の API メソッドを使用すると、パイプラインを含むモジュールは ProductItemPipeline クラスに縮小されます。そして、それは変更することができます

class ProductItemPipeline(object):
    def process_item(self, item, spider):
        if isinstance(item, ProductItem):
            item['cover_photo'] = item['files'][0]['path']

            model, created = ProductItem.get_or_create(product_company=item['product_company'], barcode=item['bar_code'], 
    defaults={'Other_field1': value1, 'Other_field2': value2})

            if created:
                for image in item['files']:
                    imageItem = ProductImageItem(image=image['path'], product=item.instance)
                    imageItem.save()
            return item

        if isinstance(item, CommentItem):

            model, created = CommentItem.get_or_create(field1=value1, defaults={ other fields go in here'})

            if created:
                print model
            else:
                print created
            return item

元のコードのバグ

これがバグが存在した場所だと思います。

  obj = model_class.objects.get(product=model.product, comment=model.comment)

今はそれを使用していないので、バグは消えるはずです。それでも問題が解決しない場合は、完全なトレースバックを貼り付けてください。

于 2017-01-07T06:03:43.243 に答える