【问题标题】:Scrapy: scrape multiple pages and yield the results in a single arrayScrapy:抓取多个页面并在单个数组中产生结果
【发布时间】:2019-09-21 18:26:28
【问题描述】:

我要做的是抓取多个页面并在单个数组中产生结果。

我找到了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 是非阻塞的,所以我可以将结果存储在一个全局变量中,并在蜘蛛爬取所有页面后生成它。

(也就是说,我不喜欢使用全局变量,因为随着应用程序变得越来越大,维护起来可能会很困难)

我们将不胜感激。

附言

@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 是非阻塞的,所以我想可能会发生这样的情况

附言

如果你必须yield 结果,@Wim Hermans 提出的第一个解决方案可能是最好的解决方案(但要小心内存使用)

【问题讨论】:

    标签: python scrapy


    【解决方案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)。

    【讨论】:

    • 感谢您的想法!关于3,您将如何处理并发?例如,假设多个客户端正在向 Spider 发送请求,Spider 在 scrapyrt(或 klein)上作为 REST API 端点工作。为了让 Spider 服务多个 cleitns,Spider 不能将结果存储在同一个文件中,对吧? (我会更新问题以澄清这一点)
    • 当您使用 scrapyrt 时,您的方法 1 似乎是最佳解决方案。方法 2 没有在 scrapyrt 上打印结果,因为 close_spider 不会产生变量。再次感谢您的见解!
    猜你喜欢
    • 2018-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多