我想将http://quotes.toscrape.com/ 中的每个报价保存到一个 csv 文件中(2 个字段:作者、报价)。另一个必要条件是将这些引用保存在由它们所在的页面分隔的不同文件中。即:(page1.csv,page2.csv ...)。我试图通过custom_settings在我的蜘蛛的属性中声明饲料出口来实现这一点,如下所示。但是,这甚至不会生成名为page-1.csv. 我是一个使用scrapy的初学者,请尝试解释,假设我知之甚少。
import scrapy
import urllib
class spidey(scrapy.Spider):
name = "idk"
start_urls = [
"http://quotes.toscrape.com/"
]
custom_settings = {
'FEEDS' : {
'file://page-1.csv' : { #edit: uri needs to be absolute path
'format' : 'csv',
'store_empty' : True
}
},
'FEED_EXPORT_ENCODING' : 'utf-8',
'FEED_EXPORT_FIELDS' : ['author', 'quote']
}
def parse(self, response):
for qts in response.xpath("//*[@class=\"quote\"]"):
author = qts.xpath("./span[2]/small/text()").get()
quote = qts.xpath("./*[@class=\"text\"]/text()").get()
yield {
'author' : author,
'quote' : quote
}
next_pg = response.xpath('//li[@class="next"]/a/@href').get()
if next_pg is not None:
next_pg = urllib.parse.urljoin(self.start_urls[0], next_pg)
yield scrapy.Request(next_pg, self.parse)
Run Code Online (Sandbox Code Playgroud)
我如何运行爬虫:scrapy crawl idk
作为一个附加问题,我需要覆盖我的文件,而不是像指定-o标志时那样附加。是否可以在不必手动检查/删除蜘蛛中预先存在的文件的情况下做到这一点?
设置中不支持将您的项目保存到以您找到它们的页面命名的文件中(据我所知)。如果你想实现这一点,你可以使用 python 的open函数并csv.writer在你的parse方法中创建你自己的功能。另一种选择是编写一个项目管道,用于管理不同文件的不同项目导出器。
然而,您可以使用设置来限制文件中的项目数量FEED_EXPORT_BATCH_ITEM_COUNT,该设置从 Scrapy 2.3 版本开始受支持。
从 Scrapy 2.4 开始,也可以覆盖而不是附加到文件。您FEEDS可以设置overwrite为 True,如稍后所示。
如果您将您替换custom_settings为以下内容,它将生成包含 10 个项目的文件,每个项目的名称page-后跟batch_id以 1 开头的 。因此,您的前 3 个文件将命名为 page-1.csv、page-2.csv 和 page-3.csv。
custom_settings = {
'FEED_EXPORT_BATCH_ITEM_COUNT': 10,
'FEEDS' : {
'page-%(batch_id)d.csv' : {
'format' : 'csv',
'store_empty' : True,
'overwrite': True
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果您想使用项目管道来实现此目的,则可以将您所在的页码保存在返回的字典中,然后由项目管道处理并删除该字典。
您的管道pipelines.py(基于此示例)可能如下所示:
from scrapy.exporters import CsvItemExporter
class PerFilenameExportPipeline:
"""Distribute items across multiple CSV files according to their 'page' field"""
def open_spider(self, spider):
self.filename_to_exporter = {}
def close_spider(self, spider):
for exporter in self.filename_to_exporter.values():
exporter.finish_exporting()
def _exporter_for_item(self, item):
filename = 'page-' + str(item['page_no'])
del item['page_no']
if filename not in self.filename_to_exporter:
f = open(f'{filename}.csv', 'wb')
exporter = CsvItemExporter(f)
exporter.start_exporting()
self.filename_to_exporter[filename] = exporter
return self.filename_to_exporter[filename]
def process_item(self, item, spider):
exporter = self._exporter_for_item(item)
exporter.export_item(item)
return item
Run Code Online (Sandbox Code Playgroud)
然后,您需要向您的蜘蛛添加一个例程来获取您所在的页面,并在您的 中设置管道custom_settings,您可以执行以下操作:
import scrapy
from ..pipelines import PerFilenameExportPipeline
class spidey(scrapy.Spider):
name = "idk"
custom_settings = {
'ITEM_PIPELINES': {
PerFilenameExportPipeline: 100
}
}
def start_requests(self):
yield scrapy.Request("http://quotes.toscrape.com/", cb_kwargs={'page_no': 1})
def parse(self, response, page_no):
for qts in response.xpath("//*[@class=\"quote\"]"):
yield {
'page_no': page_no,
'author' : qts.xpath("./span[2]/small/text()").get(),
'quote' : qts.xpath("./*[@class=\"text\"]/text()").get()
}
next_pg = response.xpath('//li[@class="next"]/a/@href').get()
if next_pg is not None:
yield response.follow(next_pg, cb_kwargs={'page_no': page_no + 1})
Run Code Online (Sandbox Code Playgroud)
然而,这有一个问题。由于我无法理解的原因,最后一个文件(page-10.csv)保持为空。我问过为什么会在这里。