Python Scrapy cómo guardar datos en diferentes archivos
Quiero guardar cada cita de http://quotes.toscrape.com/guardado en un archivo csv (campo 2: autor, cita). Otra necesidad es guardar estas citas en diferentes archivos separados por la página en la que residen. es decir: (page1.csv, page2.csv ...). Intenté lograr esto declarando exportaciones de alimento en custom_settingsatributo en mi araña como se muestra a continuación. Esto, sin embargo, ni siquiera produce un archivo llamado page-1.csv. Soy un principiante total que usa scrapy, intente explicarlo asumiendo que sé poco o nada.
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)
Cómo ejecuté el rastreador: scrapy crawl idkcomo pregunta adicional, necesito que mis archivos se sobrescriban en lugar de agregarlos como cuando se especifica la -obandera. ¿Es posible hacerlo sin tener que verificar / eliminar manualmente los archivos preexistentes de Spider?
Respuestas
Guardar sus elementos en un archivo con el nombre de la página en la que los encontró no es (afaik) compatible con la configuración. Si quisiera lograr esto, podría crear su propia funcionalidad para eso con la openfunción de python y csv.writeren su parsemétodo. Una opción alternativa sería escribir una canalización de elementos que gestione diferentes exportadores de elementos para diferentes archivos.
Sin embargo, lo que puede hacer con la configuración es limitar la cantidad de elementos en un archivo con la FEED_EXPORT_BATCH_ITEM_COUNTconfiguración, que es compatible desde la versión 2.3 de Scrapy.
También se puede sobrescribir en lugar de agregar a un archivo desde Scrapy 2.4. En FEEDSpuede establecer overwriteen True como se demuestra en breve.
Si reemplazara su custom_settingscon lo siguiente, produciría archivos con 10 elementos, cada uno con un nombre page-seguido de batch_id, que comienza con uno. Entonces, sus primeros 3 archivos se llamarían page-1.csv, page-2.csv y page-3.csv.
custom_settings = {
'FEED_EXPORT_BATCH_ITEM_COUNT': 10,
'FEEDS' : {
'page-%(batch_id)d.csv' : {
'format' : 'csv',
'store_empty' : True,
'overwrite': True
}
}
}
Implementar como canalización
Si desea implementar esto mediante una canalización de elementos, puede guardar el número de página en el que se encuentra en el diccionario que devuelve, que luego se procesa y elimina mediante la canalización de elementos.
La canalización en su pipelines.py(basada en este ejemplo ) podría verse así:
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
A su araña, deberá agregar una rutina para obtener la página en la que se encuentra, así como configurar la canalización en su custom_settings, lo que podría hacer de la siguiente manera:
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})
Sin embargo, hay un problema con esto. El último archivo (página-10.csv) permanece vacío por razones que escapan a mi comprensión. Le he preguntado por qué podría estar aquí .