Scrapy:刮擦多页并在单个数组中产生结果

时间:2019-09-21 18:26:28

标签: python scrapy

我想做的是刮刮多页并在 单个数组 中产生结果。

我发现this post,它描述了如何抓取多个页面并从每个被抓取的页面中产生文本。

我提到了这种方法(并对其做了一些修改),这就是我的蜘蛛的样子……

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是非阻塞的,因此我可能能够将结果存储在全局变量中,并在Spider抓取所有页面之后产生它。

(也就是说,我不想使用全局变量,因为随着应用程序的增大,可能难以维护它)

任何建议将不胜感激。

PS

@Wim Hermans给了我有趣的方法(谢谢!)。

其中,可以将结果存储在具有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是非阻塞的,所以我想这样的情况可能发生

P.P.S。

如果必须yield,@ Wim Hermans提出的第一个解决方案可能是最好的解决方案(但请注意内存使用情况)

1 个答案:

答案 0 :(得分:1)

我可以想到几种不同的选择:

  1. 您将结果传递到meta中,直到抓取完成为止:
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

根据要获取的数据量,这可能会变得很繁重。

  1. 编写自定义项目管道:

您不会在meta中传递完整的结果集,而是编写了一个项目管道,该结果管道将结果保存在列表中并在最后给出结果。

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}")

这基本上就像将结果存储在全局变量中一样,因此可能也不完全是您所需要的。

  1. 写入文件/数据库

一种更节省内存的方法是将结果写入文件(或数据库),然后对其进行一些处理以获取所需格式的结果。您可以在项目管道(items to json)中执行此操作,也可以仅使用Feed导出(feed exports)。

相关问题