2015-09-22 5 views
6

Die API sollte beliebige HTTP-Get-Anfragen mit URLs erlauben, die der Benutzer abgekratzt haben möchte, und dann sollte Flask die Ergebnisse des Scraps zurückgeben.Erstellen einer RESTful Flask-API für Scrapy

Der folgende Code funktioniert für die erste http-Anforderung, aber nach dem verdrehten Reaktor stoppt er nicht neu. Ich mag vielleicht nicht einmal den richtigen Weg gehen, aber ich möchte einfach eine RESTful scrapy API auf Heroku setzen, und was ich bisher habe, ist alles was ich mir vorstellen kann.

Gibt es eine bessere Möglichkeit, diese Lösung zu erstellen? Oder wie kann ich scrape_it erlauben, zurückzukehren, ohne gedrehte Drossel zu stoppen (die nicht wieder gestartet werden kann)?

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

Können Sie einen Trace-Back-Fehler oder etwas anderes angeben? Auch warum nicht einfach entfernen Sie diese Zeile 'd.addBoth (Lambda _: reactor.stop())' und rufen Sie reactor.stop nach 'reactor.run()' Ich gehe davon aus, dass es aus Fehler, weil, wenn es eintritt Funktionsreaktor könnte in einem gestarteten Zustand oder einem gestoppten Zustand sein. Es ist nicht garantiert. – AdriVelaz

+0

Warum möchten Sie Scrapy verwenden? es gibt andere Möglichkeiten, Seiten zu verschrotten – ahmed

+0

@ahmed mein Problem baut eine asynch-Warteschlange für das Ziehen vieler Seiten, und spidering dann heraus zu den Verbindungen auf diesen Seiten. Was würdest du dafür empfehlen? –

Antwort

16

Ich denke, es gibt keinen guten Weg, Flask-basierte API für Scrapy zu erstellen. Flask ist dafür kein geeignetes Werkzeug, da es nicht auf einer Ereignisschleife basiert. Um die Sache noch schlimmer zu machen, Twisted Reaktor (die Scrapy verwendet) can't mehr als einmal in einem einzigen Thread gestartet/gestoppt werden.

Nehmen wir an, es gibt kein Problem mit Twisted-Reaktor und Sie können es starten und stoppen. Es wird die Dinge nicht viel besser machen, weil Ihre scrape_it Funktion für einen längeren Zeitraum blockieren kann und Sie daher viele Threads/Prozesse benötigen.

Ich denke, der Weg zu gehen ist eine API mit Async-Framework wie Twisted oder Tornado zu erstellen; Es wird effizienter sein als eine Flask-basierte (oder Django-basierte) Lösung, da die API in der Lage sein wird, Anfragen zu bedienen, während Scrapy einen Spider ausführt.

Scrapy basiert auf Twisted, also kann die Verwendung von twisted.web oder https://github.com/twisted/klein einfacher sein. Aber Tornado ist auch nicht schwer, weil Sie Twisted Event Loop verwenden können.

Es gibt ein Projekt namens ScrapyRT, das etwas sehr ähnliches zu dem was Sie implementieren möchten - es ist eine HTTP API für Scrapy. ScrapyRT basiert auf Twisted.

Als Beispiel von Scrapy-Tornado Integrationsprüfung Arachnado - here ist ein Beispiel, wie Scrapy CrawlerProcess mit Tornado-Anwendung zu integrieren.

Wenn Sie wirklich Flask-basierte API wollen, dann könnte es sinnvoll sein, Crawl in separaten Prozessen zu starten und/oder Warteschlangen-Lösung wie Sellerie zu verwenden. Auf diese Weise verlieren Sie den größten Teil der Scrapy-Effizienz. Wenn Sie diesen Weg gehen, können Sie Anfragen + BeautifulSoup auch verwenden.

3

Ich habe auf ähnliches Projekt letzte Woche arbeite, ist es SEO-Service-API, mein Workflow wie das war:

und ein Rückruf zu kratzen
  • Der Client eine Anfrage an Flask-basierten Server mit einem URRL senden URL, um den Client zu benachrichtigen, wenn die Verschrottung abgeschlossen ist (Client ist hier eine andere Webanwendung)
  • Führen Sie Scrapy im Hintergrund mit python-daemon aus. Der Spider speichert die Daten in der Datenbank.
  • Der Backgound-Dienst benachrichtigt den Client, indem er die Callback-URL aufruft, wenn der Spider beendet ist.
+0

können Sie mir helfen, die Callback-URL-Idee zu verstehen? Ich folge dir bis zu diesem Punkt, und ich bin mir nicht sicher, wie ich das umsetzen soll ... Danke übrigens, das ist eine tolle Idee –

+0

Es ist, wie dein Klient wissen wird, wenn der Crawler fertig ist. Es ist nur nützlich, wenn Ihr Client eine Website ist. Wenn Sie keinen Rückruf verwenden, überprüft Ihr Client regelmäßig, ob der Crawler fertig ist. – ahmed