Scrapy Тот же элемент, но несколько URL - PullRequest
0 голосов
/ 19 марта 2019
class MySiteSpider(scrapy.Spider):
    name = 'MySite'
    allowed_domains = ['example.com']
    start_urls = ['http://example.com/']

    def parse(self, response):
        links = LinkExtractor(unique=True).extract_links(response)
        for link in links:
            yield response.follow(link, callback=self.parse)
            pass

        if response.css('.product-page-content'):
            id_ = response.css('#id::text').extract_first()
            item = MyItem()
            item['id'] = id
            item['urls'] = [response.url]
            # Sorting some data
            yield item

Проблема в том, что иногда я получаю страницу с другим url, но с тем же id_, и в такой ситуации мне нужно добавить этот новый URL к старому элементу со старым id_ / Что-то вроде:

if response.css('.product-page-content'):
    id_ = response.css('#id::text').extract_first()
    if this_id_already_processed:
        old_item['url'].append(response.url)
    else
        item = MyItem()
        item['id'] = id
        item['urls'] = [response.url]
        # Sorting some data
        yield item

Ответы [ 2 ]

3 голосов
/ 19 марта 2019

Интересный случай.Есть несколько способов, которыми вы можете сделать это, но основным барьером в этом случае будет память.

Когда scrapy возвращает элемент, он экспортирует его в любой вывод, который у вас есть (stdout, json, csv и т. Д.), И тогда все готово.Если вы хотите объединить свои элементы таким образом, вам нужно либо постобработать вывод, либо сохранить все в памяти.

  1. Вывод постобработки
    Если у вас есть файлы в output.csv Вы можете просто запустить скрипт над результатами, чтобы присоединиться к вашим элементам.См. Сигнал spider_closed - когда паук закрывается, откройте ouput.json и настройте содержимое

  2. Конвейеры
    При таком подходе вы можете хранить все элементы в памяти и обрабатывать ихпо мере того, как вы сканируете:

    import json
    class MyPipeline:
        items = {}
    
        def process_item(self, item):
            if item['id'] in self.items:
                self.items['id']['urls'].append(item['url'])
            else:
                self.items[item['id']] = item
            return item
    
        close_spider(self, spider):
            with open('output.json') as f:
                f.write(json.dumps(self.items))
    
  3. Экспортеры каналов
    Это будет почти идентично подходу конвейера - сохраняйте элементы в памяти перед экспортом

Если ваш паук маленький, используйте # 2, иначе # 1 намного эффективнее и эффективнее использует память.

0 голосов
/ 19 марта 2019

Вы не можете сделать это простым способом из-за того, как работает Scrapy. Он обрабатывает запросы асинхронно, выдавая элементы один за другим, сам по себе не ведет никакой истории. Что вы можете сделать, это использовать буфер некоторых элементов в пауке и использовать сигналы , чтобы сбросить все элементы в конце сканирования.

См. Этот фиктивный пример:

import json
import scrapy
from scrapy import signals


class QuotesSpider(scrapy.Spider):
    name = 'quotes'
    start_urls = ['http://quotes.toscrape.com/page/1/']
    items = []

    @classmethod
    def from_crawler(cls, crawler, *args, **kwargs):
        spider = super(QuotesSpider, cls).from_crawler(crawler, *args, **kwargs)
        crawler.signals.connect(spider.spider_closed, signal=signals.spider_closed)
        return spider

    def parse(self, response):
        for quote in response.xpath('//div[@class="quote"]'):
            item = {
                'text': quote.xpath('normalize-space(./span[@class="text"])').extract_first()
            }
            self.items.append(item)

    def spider_closed(self, spider):
        with open('items.json', 'wt') as f:
            json.dump(self.items, f)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...