Scrapy: очистить несколько страниц и получить результаты в одном массиве - PullRequest
0 голосов
/ 21 сентября 2019

Я пытаюсь очистить несколько страниц и получить результат в одном массиве .

Я нашел этот пост , который описывает, как очистить несколько страниц и получить текст с каждой из очищенных страниц.

Я упомянул этот подход (и немного его изменил), и вот мой паук выглядит так ...

from scrapy import Request
from test_project.items import PriceSpiderItem

class RoomsSpider(scrapy.Spider):
    name = 'rooms'
    allowed_domains = ['sample.com']
    start_urls = ['http://sample.com/rooms']

    def parse(self, response):
        for resource in response.xpath('.//*[@class="sample"]'):
            item = PriceSpiderItem()
            item['result'] = resource.xpath("text()").extract_first()
            yield item

        nextUrl = response.xpath('//*[@label="Next"]/@href').extract_first()

        if(nextUrl is not None):
            absoluteNextUrl = response.urljoin(nextUrl)
            yield Request(url=absoluteNextUrl, callback=self.parse)

Однако при таком подходе результат будет выглядеть следующим образом ...

{
 "items" : [
  {"result": "blah blah"},
  {"result": "blah blah blah blah blah"},
  {"result": "blah blah blah blah"},
  ...
  etc.
  ...
  {"result": "blah blah blah blah blah"},
  {"result": "blah blah blah"}
 ]
}

Это не совсем то, что я стремлюсь дать.В идеале результат должен быть в одном массиве, например ...

 {
  "items" : [
    "blah blah",
    "blah blah blah blah blah",
    "blah blah blah blah",
     ...
    "blah blah blah blah blah",
    "blah blah blah"
   ]
 }

Однако я не уверен, достижим ли он.

Насколько я понимаю, Scrapyне блокирует, так что я мог бы сохранить результат в глобальной переменной и выдать его после того, как паук просканировал все страницы.

(Тем не менее, я не хотел бы использовать глобальную переменную, потому что может быть трудно поддерживать приложение по мере его увеличения)

Любой совет будет оценен.

PS

@ Вим Херманс дал мне интересные подходы (спасибо!).

Среди них можно будет сохранить результаты в файлес ItemPipeline и выдайте его после сканирования всех страниц.

Это кажется очень многообещающим, но если Spider работает на scrapyrt (или что-то подобное) для работы в качестве конечной точки REST API,Я не уверен, что делать с проблемами параллелизма.

# 1. Client A makes a request
# 2. Spider receives Client A's request
# 3. Client B makes a request
# 4. Spider receives Client B's request
# 5. Spider fulfills Client B's request, saves the result in "result.csv"
# 6. Spider fulfills Client A's request, updates "result.csv" with Client A's request
# 7. Spider responses with "result.csv" for bot Client A and B

Scrapy не блокирует, поэтому такой сценарий может случиться, я полагаю

PPS

Если вам нужно yield результат, первое решение, представленное @Wim Hermans, вероятно, является лучшим решением (но будьте осторожны с использованием памяти)

1 Ответ

1 голос
/ 22 сентября 2019

Для достижения этой цели есть несколько вариантов:

  1. Вы передаете результат в мета, пока не закончится очистка:
def parse(self, response):
    result = response.meta.get('result', [])
    for resource in response.xpath('.//*[@class="sample"]'):
        result.append(resource.xpath("text()").extract_first())

    nextUrl = response.xpath('//*[@label="Next"]/@href').extract_first()
    meta = {'result': result}
    if nextUrl:
        absoluteNextUrl = response.urljoin(nextUrl)
        yield Request(url=absoluteNextUrl, callback=self.parse, meta=meta)
    else:
        item = PriceSpiderItem()
        item['result'] = result
        yield item

В зависимости отот того, сколько данных вы получите, это может стать довольно тяжелым.

Написать пользовательский конвейер элементов:

Вы не передаете полный набор результатов в мета, но вы пишете конвейер элементов, который сохраняет результаты в списке и выдает результаты вконец.

class CombineResultsPipeline(object):
    def __init__(self):
        self.results = []

    def process_item(self, item, spider):
        self.results.append(item['result'])
        return item

    def close_spider(self, spider):
        print(f"full result set is {self.results}")

По сути, это похоже на сохранение результатов в глобальной переменной, поэтому может оказаться не совсем тем, что вам нужно.

Запись в файл / базу данных

Более эффективным способом памяти может быть запись результатов в файл (или в базу данных), а затем выполнить некоторую обработку для получениярезультаты в нужном вам формате.Вы можете сделать это в конвейере элементов ( элементов в json ) или просто использовать для этого Экспорт фидов ( экспорт фидов ).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...