我想做的是抓取多个页面并在单个数组中产生结果。
我找到了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 是非阻塞的,所以我想这样的场景可能会发生
P.P.S.
如果您必须产生
结果,@Wim Hermans 提出的第一个解决方案可能是最好的解决方案(但要小心内存使用)
最佳答案
我可以想到几种不同的选择来实现这一目标:
- 您将结果传递到元中,直到抓取完成:
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}")
这基本上就像将结果存储在全局变量中一样,因此可能也不是您所需要的。
- 写入文件/数据库
更节省内存的选项可能是将结果写入文件(或数据库),然后对其进行一些处理以获得所需格式的结果。您可以在项目管道 ( items to json ) 中执行此操作,或者仅使用 Feed Exports ( feed exports )。
关于python - Scrapy:抓取多个页面并在单个数组中生成结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58043009/