2015-09-22 11 views
6

L'API deve consentire richieste di ottenere HTTP arbitrarie contenenti URL che l'utente desidera eliminare, quindi Flask deve restituire i risultati di scrap.Creazione di un'API di pallone RESTful per Scrapy

Il seguente codice funziona per la prima richiesta http, ma dopo l'interruzione del reattore torto, non si riavvia. Potrei anche non andare su questo nel modo giusto, ma voglio solo mettere una API scrapy RESTful su Heroku, e quello che ho finora è tutto ciò che riesco a pensare.

C'è un modo migliore per progettare questa soluzione? O come posso consentire a scrape_it di tornare senza fermare il reattore a spirale (che non può essere riavviato)?

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)) 
+0

Potrebbe fornire un errore di traceback o qualcosa del genere? Anche perché non basta rimuovere questa riga 'd.addBoth (lambda _: reactor.stop())' e chiamare reactor.stop dopo 'reactor.run()' Sto assumendo che errori fuori perché quando entra nel reattore di funzione potrebbe essere in uno stato avviato o uno stato di arresto. Non è garantito – AdriVelaz

+0

perché vuoi usare Scrapy? Esistono altri modi per eliminare le pagine – ahmed

+0

@ahmed il mio problema è la creazione di una coda asynch per l'estrazione di molte pagine e il successivo spidering verso i link su quelle pagine. Cosa consiglieresti per questo? –

risposta

16

Penso che non ci sia un buon modo per creare API basate su Flask per Scrapy. Flask non è uno strumento adatto per questo perché non si basa sul ciclo degli eventi. Per peggiorare le cose, il reattore contorto (che utilizza Scrapy) can't può essere avviato/arrestato più volte in una singola discussione.

Supponiamo che non vi siano problemi con il reattore ritorto e che sia possibile avviarlo e interromperlo. Non migliorerà le cose perché la tua funzione scrape_it potrebbe bloccarsi per un lungo periodo di tempo e quindi avrai bisogno di molti thread/processi.

Penso che la strada da percorrere sia creare un'API utilizzando framework asincrono come Twisted o Tornado; sarà più efficiente di una soluzione basata su Flask (o basata su Django) perché l'API sarà in grado di servire le richieste mentre Scrapy sta eseguendo uno spider.

Scrapy si basa su Twisted, quindi l'utilizzo di twisted.web o https://github.com/twisted/klein può essere più semplice. Ma anche il Tornado non è difficile perché puoi usarlo per il loop di eventi Twisted.

C'è un progetto chiamato ScrapyRT che fa qualcosa di molto simile a ciò che si desidera implementare - si tratta di un'API HTTP per Scrapy. ScrapyRT è basato su Twisted.

Come un esame del controllo dell'integrazione di Scrapy-Tornado Arachnado - here è un esempio su come integrare Scrapy's CrawlerProcess con l'applicazione di Tornado.

Se si desidera realmente l'API basata su Flask, potrebbe avere senso iniziare le ricerche per indicizzazione in processi separati e/o utilizzare una soluzione di coda come Celery. In questo modo stai perdendo la maggior parte dell'efficienza di Scrapy; se vai in questo modo puoi usare anche le richieste + BeautifulSoup.

3

Ho lavorato sul progetto analogo la scorsa settimana, è il servizio di SEO API, il mio flusso di lavoro è stato in questo modo:

  • Il client invia una richiesta al server Flask-based con un URRL per raschiare, e una funzione di callback url per notificare al client quando la demolizione viene eseguita (il client qui è un'altra app Web)
  • Eseguire Scrapy in background utilizzando python-daemon. Lo spider salverà i dati nel database.
  • Il servizio di backgound avviserà il client chiamando l'url di richiamata quando lo spider è terminato.
+0

potresti aiutarmi a capire l'idea dell'URL di callback? Ti seguo fino a quel punto, e non sono sicuro di come implementarlo ... Grazie a tutti, questa è un'idea fantastica –

+0

È come il tuo cliente saprà se il crawler ha finito. È utile solo se il tuo cliente è un sito web. se non si utilizza la richiamata, il client controllerà periodicamente se il crawler ha terminato. – ahmed