【问题标题】:Scrapy: How to load multiple items in separate function?Scrapy:如何在单独的函数中加载多个项目?
【发布时间】:2019-08-12 06:55:03
【问题描述】:

我想在与parse_product 分开的函数中处理和加载项目,在我的例子中是prepare_item_download() 函数。但是,当我运行我的蜘蛛时,我收到一条错误消息,它需要返回一个 Request、BaseItem、dict 或 None 而不是生成器。当我将它留在parse_product 函数中时它可以工作。

这是我的代码:

from scrapy import Request, Spider
from scrapy.loader import ItemLoader
from firmware.items import FirmwareItem
import re


class AvmSpider(Spider):
    name = 'avm'

    start_urls = [
        'http://download.avm.de/fritzbox/',
        'http://download.avm.de/fritzwlan/',
        'http://download.avm.de/fritzpowerline/'
    ]

    # parse top-level pages
    def parse(self, response):
        for product_url in self.link_extractor(response=response, prefix=('beta', 'tools', 'license', '..')):
            yield Request(url=product_url, callback=self.parse_product)

    # parse each product; call self as long as not in fritz.os directory
    def parse_product(self, response):
        path = response.request.url.split('/')[:-1]
        if path[-1] == 'fritz.os':
            yield self.prepare_item_download(response, path)
        else:
            for sub in self.link_extractor(response=response, prefix=('recover', '..')):
                yield Request(url=response.urljoin(sub), callback=self.parse_product)

    # get release dates, populate and load item 
    def prepare_item_download(self, response, path: str):
        release_dates = self.date_extractor(response)
        for index, file_url in enumerate(self.link_extractor(response=response, prefix='..')):
            if file_url.endswith('.image'):
                loader = ItemLoader(item=FirmwareItem(), selector=file_url)
                loader.add_value('file_urls', file_url)
                loader.add_value('vendor', 'avm')
                loader.add_value('device_name', path[-3])
                loader.add_value('device_class', path[-4])
                loader.add_value('release_date', release_dates[index])
                yield loader.load_item()
        yield None


    # return all links which do not start with a certain prefix
    @staticmethod
    def link_extractor(response, prefix) -> list:
        return [response.urljoin(p) for p in response.xpath('//a/@href').extract() if not p.startswith(prefix)]

    # return release dates of all images listed on current page
    @staticmethod
    def date_extractor(response) -> list:
        release_dates = list()
        for text in response.xpath('//pre/text()').extract():
            match = re.search(r'(\d{2}-\w{3}-\d{4} \d{2}:\d{2})', text)
            if match:
                release_dates.append(match.group(1))

        return release_dates

上面的代码不起作用。下面你可以看到工作代码:

    def parse_product(self, response):
        path = response.request.url.split('/')[:-1]
        if path[-1] == 'fritz.os':
            release_dates = self.date_extractor(response)
            for index, file_url in enumerate(self.link_extractor(response=response, prefix='..')):
                if file_url.endswith('.image'):
                    loader = ItemLoader(item=FirmwareItem(), selector=file_url)
                    loader.add_value('file_urls', file_url)
                    loader.add_value('vendor', 'avm')
                    loader.add_value('device_name', path[-3])
                    loader.add_value('device_class', path[-4])
                    loader.add_value('release_date', release_dates[index])
                    yield loader.load_item()
        else:
            for sub in self.link_extractor(response=response, prefix=('recover', '..')):
                yield Request(url=response.urljoin(sub), callback=self.parse_product)

【问题讨论】:

    标签: python scrapy items


    【解决方案1】:

    parse_product函数生成一个generator

    def parse_product(self, response):
        path = response.request.url.split('/')[:-1]
        if path[-1] == 'fritz.os':
            yield self.prepare_item_download(response, path)
            ^^^^^^^^^^^^^
    

    您应该使用yield from 语句来解压缩生成器:

    def parse_product(self, response):
    
        path = response.request.url.split('/')[:-1]
        if path[-1] == 'fritz.os':
            yield from self.prepare_item_download(response, path)
                  ^^^^
            # or for python <3.3
            for item in self.prepare_item_download(response, path):
                yield item
    

    【讨论】:

    • 非常感谢!这正是我所需要的。不知道 'yield from' 命令。
    猜你喜欢
    • 2020-01-11
    • 2014-10-03
    • 2012-08-27
    • 1970-01-01
    • 1970-01-01
    • 2016-10-08
    • 1970-01-01
    • 2022-11-12
    • 1970-01-01
    相关资源
    最近更新 更多