Как заполнить scrapy.Field как словарь

Я строю скребок для http://www.apkmirror.com/, используя Scrapy (с пауком SitemapSpider). Пока что работают следующие:

DEBUG = True

from scrapy.spiders import SitemapSpider
from apkmirror_scraper.items import ApkmirrorScraperItem


class ApkmirrorSitemapSpider(SitemapSpider):
    name = 'apkmirror-spider'
    sitemap_urls = ['http://www.apkmirror.com/sitemap_index.xml']
    sitemap_rules = [(r'.*-android-apk-download/$', 'parse')]

    if DEBUG:
        custom_settings = {'CLOSESPIDER_PAGECOUNT': 20}

    def parse(self, response):
        item = ApkmirrorScraperItem()
        item['url'] = response.url
        item['title'] = response.xpath('//h1[@title]/text()').extract_first()
        item['developer'] = response.xpath('//h3[@title]/a/text()').extract_first()
        return item

где ApkMirrorScraperItem определяется в items.py следующее:

class ApkmirrorScraperItem(scrapy.Item):
    url = scrapy.Field()
    title = scrapy.Field()
    developer = scrapy.Field()

Результирующий вывод JSON, если я запускаю его из каталога проекта с помощью команды

scrapy crawl apkmirror-spider -o data.json

массив JSON-словарей с ключами url, title, а также developer и соответствующие строки в качестве значений. Я хотел бы изменить это, однако, чтобы значение developer сам по себе словарь с name поле, так что я могу заполнить его так:

item['developer']['name'] = response.xpath('//h3[@title]/a/text()').extract_first()

Однако, если я попробую это, я получу KeyError с, также если я инициализирую developer "s Field (который является dict Согласно https://doc.scrapy.org/en/latest/topics/items.html) как developer = scrapy.Field(name=None), Как я могу пойти по этому поводу?

1 ответ

Решение

Scrapy реализует поля внутренне как диктовки, но это не означает, что к ним следует обращаться как к диктовкам. Когда вы звоните item['developer']что вы действительно делаете, так это получаете значение поля, а не само поле. Таким образом, если значение еще не было установлено, это вызовет KeyError.

Учитывая это, есть два способа решения вашей проблемы.

Во-первых, просто установите для поля разработчика значение dict:

def parse(self, response):
    item = ApkmirrorScraperItem()
    item['url'] = response.url
    item['title'] = response.xpath('//h1[@title]/text()').extract_first()
    item['developer'] = {'name': response.xpath('//h3[@title]/a/text()').extract_first()}
    return item

Во-вторых, создайте новый класс Developer и установите значение для разработчика в качестве экземпляра этого класса:

# this can go to items.py
class Developer(scrapy.Item):
    name = scrapy.Field()

def parse(self, response):
    item = ApkmirrorScraperItem()
    item['url'] = response.url
    item['title'] = response.xpath('//h1[@title]/text()').extract_first()

    dev = Developer()        
    dev['name'] = response.xpath('//h3[@title]/a/text()').extract_first()       
    item['developer'] = dev

    return item

Надеюсь, поможет:)

Другие вопросы по тегам