He creado una araña para eliminar problemas de projecteuler.net. Aquí He concluido mi respuesta a una pregunta relacionada con
Lanzo esto con el comando scrapy crawl euler -o euler.json y genera una matriz de objetos json desordenados, todos responden a un solo problema: esto está bien para mí porque lo voy a procesar con javascript, incluso si Piense que resolver el problema de pedidos a través de Scrapy puede ser muy simple.
Pero desafortunadamente, ordenar elementos para escribir en json por scrapy (necesito un orden ascendente por campo de identificación) no parece ser tan simple. He estudiado todos los componentes (middlewares, tuberías, exportadores, señales, etc.) pero nadie parece útil para este propósito. Llegué a la conclusión de que no existe una solución para resolver este problema en absoluto (excepto, tal vez, un truco muy elaborado), y se ve obligado a ordenar las cosas en una segunda fase. ¿Estás de acuerdo o tienes alguna idea? Copio aquí el código de mi raspador.
Araña:
# -*- coding: utf-8 -*-
import scrapy
from eulerscraper.items import Problem
from scrapy.loader import ItemLoader
class EulerSpider(scrapy.Spider):
name = 'euler'
allowed_domains = ['projecteuler.net']
start_urls = ["https://projecteuler.net/archives"]
def parse(self, response):
numpag = response.css("div.pagination a[href]::text").extract()
maxpag = int(numpag[len(numpag) - 1])
for href in response.css("table#problems_table a::attr(href)").extract():
next_page = "https://projecteuler.net/" + href
yield response.follow(next_page, self.parse_problems)
for i in range(2, maxpag + 1):
next_page = "https://projecteuler.net/archives;page=" + str(i)
yield response.follow(next_page, self.parse_next)
return [scrapy.Request("https://projecteuler.net/archives", self.parse)]
def parse_next(self, response):
for href in response.css("table#problems_table a::attr(href)").extract():
next_page = "https://projecteuler.net/" + href
yield response.follow(next_page, self.parse_problems)
def parse_problems(self, response):
l = ItemLoader(item=Problem(), response=response)
l.add_css("title", "h2")
l.add_css("id", "#problem_info")
l.add_css("content", ".problem_content")
yield l.load_item()
Articulo:
import re
import scrapy
from scrapy.loader.processors import MapCompose, Compose
from w3lib.html import remove_tags
def extract_first_number(text):
i = re.search('\d+', text)
return int(text[i.start():i.end()])
def array_to_value(element):
return element[0]
class Problem(scrapy.Item):
id = scrapy.Field(
input_processor=MapCompose(remove_tags, extract_first_number),
output_processor=Compose(array_to_value)
)
title = scrapy.Field(input_processor=MapCompose(remove_tags))
content = scrapy.Field()
2 respuestas
Si necesitara ordenar mi archivo de salida (supongo que tiene una razón válida para querer esto), probablemente escribiría un exportador.
Así se integra Scrapy {{X0 }} está implementado.
Con unos pocos cambios simples, puede modificarlo para agregar los elementos a una lista en export_item()
, y luego ordenar los elementos y escribir el archivo en finish_exporting()
.
Dado que solo está raspando unos pocos cientos de elementos, las desventajas de almacenar una lista de ellos y no escribir en un archivo hasta que finalice el rastreo no deberían ser un problema para usted.
Por ahora he encontrado una solución de trabajo usando pipeline:
import json
class JsonWriterPipeline(object):
def open_spider(self, spider):
self.list_items = []
self.file = open('euler.json', 'w')
def close_spider(self, spider):
ordered_list = [None for i in range(len(self.list_items))]
self.file.write("[\n")
for i in self.list_items:
ordered_list[int(i['id']-1)] = json.dumps(dict(i))
for i in ordered_list:
self.file.write(str(i)+",\n")
self.file.write("]\n")
self.file.close()
def process_item(self, item, spider):
self.list_items.append(item)
return item
Aunque puede no ser óptimo, porque la guía sugiere en otro ejemplo:
El propósito de JsonWriterPipeline es simplemente presentar cómo escribir tuberías de elementos. Si realmente desea almacenar todos los elementos raspados en un archivo JSON, debe usar las exportaciones de Feed.
Preguntas relacionadas
Preguntas vinculadas
Nuevas preguntas
python
Python es un lenguaje de programación multipropósito, de tipificación dinámica y de múltiples paradigmas. Está diseñado para ser rápido de aprender, comprender y usar, y hacer cumplir una sintaxis limpia y uniforme. Tenga en cuenta que Python 2 está oficialmente fuera de soporte a partir del 01-01-2020. Aún así, para preguntas de Python específicas de la versión, agregue la etiqueta [python-2.7] o [python-3.x]. Cuando utilice una variante de Python (por ejemplo, Jython, PyPy) o una biblioteca (por ejemplo, Pandas y NumPy), inclúyala en las etiquetas.