python - tutorial - Construyendo una API RESTful Flask para Scrapy
python rest api example (2)
La API debería permitir solicitudes de obtención de HTTP arbitrarias que contengan las URL que el usuario desea eliminar, y luego Flask debería devolver los resultados del raspado.
El siguiente código funciona para la primera solicitud http, pero después de paradas del reactor retorcido, no se reiniciará. Puede que ni siquiera esté haciendo esto de la manera correcta, pero solo quiero poner una API RESTful para el tratamiento de heridos en Heroku, y lo que tengo hasta ahora es todo lo que puedo pensar.
¿Hay una mejor manera de diseñar esta solución? ¿O cómo puedo permitir que scrape_it
regrese sin detener el reactor retorcido (que no se puede volver a arrancar)?
from flask import Flask
import os
import sys
import json
from n_grams.spiders.n_gram_spider import NGramsSpider
# scrapy api
from twisted.internet import reactor
import scrapy
from scrapy.crawler import CrawlerRunner
from scrapy.xlib.pydispatch import dispatcher
from scrapy import signals
app = Flask(__name__)
def scrape_it(url):
items = []
def add_item(item):
items.append(item)
runner = CrawlerRunner()
d = runner.crawl(NGramsSpider, [url])
d.addBoth(lambda _: reactor.stop()) # <<< TROUBLES HERE ???
dispatcher.connect(add_item, signal=signals.item_passed)
reactor.run(installSignalHandlers=0) # the script will block here until the crawling is finished
return items
@app.route(''/scrape/<path:url>'')
def scrape(url):
ret = scrape_it(url)
return json.dumps(ret, ensure_ascii=False, encoding=''utf8'')
if __name__ == ''__main__'':
PORT = os.environ[''PORT''] if ''PORT'' in os.environ else 8080
app.run(debug=True, host=''0.0.0.0'', port=int(PORT))
Creo que no hay una buena manera de crear una API basada en Flask para Scrapy. Flask no es una herramienta adecuada para eso porque no se basa en el ciclo de eventos. Para empeorar las cosas, el reactor Twisted (que utiliza Scrapy) no se puede iniciar / detener más de una vez en un solo hilo.
Supongamos que no hay problema con el reactor torcido y puede iniciarlo y detenerlo. No mejorará mucho las cosas porque su función scrape_it
puede bloquearse durante un período de tiempo prolongado, por lo que necesitará muchos subprocesos / procesos.
Creo que el camino a seguir es crear una API utilizando un marco asíncrono como Twisted o Tornado; será más eficiente que una solución basada en Flask (o basada en Django) porque la API podrá atender solicitudes mientras Scrapy ejecuta una araña.
Scrapy se basa en Twisted, por lo que utilizar twisted.web o https://github.com/twisted/klein puede ser más sencillo. Pero Tornado tampoco es difícil porque puedes hacer que use el ciclo de eventos Twisted.
Existe un proyecto llamado ScrapyRT que hace algo muy similar a lo que desea implementar: es una API HTTP para Scrapy. ScrapyRT se basa en Twisted.
Como un ejemplo de la integración de Scrapy-Tornado, consulte a Arachnado : este es un ejemplo de cómo integrar el proceso CrawlerProcess de Scrapy con la aplicación Tornado.
Si realmente desea la API basada en Flask, entonces podría tener sentido comenzar los rastreos en procesos separados y / o usar la solución de cola, como Apio. De esta manera estás perdiendo la mayor parte de la eficiencia de Scrapy; si vas por aquí también puedes usar solicitudes + BeautifulSoup.
He estado trabajando en un proyecto similar la semana pasada, es la API de servicio de SEO, mi flujo de trabajo era así:
- El cliente envía una solicitud al servidor basado en Flask con un URRL para raspar, y una url de devolución de llamada para notificar al cliente cuando se realiza el desguace (el cliente aquí es otra aplicación web)
- Ejecute Scrapy en segundo plano utilizando python-daemon . La araña guardará los datos en la base de datos.
- El servicio de fondo notificará al cliente llamando a la url de devolución de llamada cuando termine la araña.