为 Scrapy 构建一个 RESTful Flask API
Building a RESTful Flask API for Scrapy
API 应该允许包含用户想要抓取的 URL 的任意 HTTP get 请求,然后 Flask 应该 return 抓取的结果。
以下代码适用于第一个 http 请求,但在 twisted reactor 停止后,它不会重新启动。我什至可能没有以正确的方式解决这个问题,但我只想在 Heroku 上放一个 RESTful scrapy API,到目前为止我能想到的就是这些。
是否有更好的方法来构建此解决方案?或者我如何允许 scrape_it
到 return 而不停止扭曲的反应堆(无法再次启动)?
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))
我认为没有什么好的方法可以为 Scrapy 创建基于 Flask 的API。 Flask 不是一个合适的工具,因为它不是基于事件循环的。更糟糕的是,Twisted reactor(Scrapy 使用的)can't 在单个线程中 started/stopped 不止一次。
假设 Twisted reactor 没有问题,您可以启动和停止它。它不会使事情变得更好,因为您的 scrape_it
函数可能会阻塞很长时间,因此您将需要许多 threads/processes.
我认为要走的路是使用像 Twisted 或 Tornado 这样的异步框架来创建一个 API;它将比基于 Flask(或基于 Django)的解决方案更有效,因为 API 将能够为请求提供服务,而 Scrapy 是 运行 蜘蛛。
Scrapy 基于 Twisted,因此使用 twisted.web 或 https://github.com/twisted/klein 可以更直接。但是 Tornado 也不难,因为你可以使用 Twisted 事件循环。
有一个名为 ScrapyRT 的项目,它做的事情与您想要实现的非常相似 - 它是 Scrapy 的 HTTP API。 ScrapyRT 基于 Twisted.
作为 Scrapy-Tornado 集成检查的示例 Arachnado - here 是一个关于如何将 Scrapy 的 CrawlerProcess 与 Tornado 的应用程序集成的示例。
如果你真的想要基于 Flask 的 API 那么在单独的进程中开始爬网可能是有意义的 and/or 使用像 Celery 这样的队列解决方案。这样你就失去了大部分的 Scrapy 效率;如果你这样做,你也可以使用 requests + BeautifulSoup。
我上周一直在做类似的项目,它是 SEO 服务 API,我的工作流程是这样的:
- 客户端向基于 Flask 的服务器发送一个请求,其中包含一个要抓取的 URL,以及一个回调 url 以在抓取完成时通知客户端(这里的客户端是另一个网络应用程序)
- 运行 使用 Celery 在后台进行 Scrapy。蜘蛛会将数据保存到数据库中。
- 后台服务将在蜘蛛完成时通过调用回调url通知客户端。
API 应该允许包含用户想要抓取的 URL 的任意 HTTP get 请求,然后 Flask 应该 return 抓取的结果。
以下代码适用于第一个 http 请求,但在 twisted reactor 停止后,它不会重新启动。我什至可能没有以正确的方式解决这个问题,但我只想在 Heroku 上放一个 RESTful scrapy API,到目前为止我能想到的就是这些。
是否有更好的方法来构建此解决方案?或者我如何允许 scrape_it
到 return 而不停止扭曲的反应堆(无法再次启动)?
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))
我认为没有什么好的方法可以为 Scrapy 创建基于 Flask 的API。 Flask 不是一个合适的工具,因为它不是基于事件循环的。更糟糕的是,Twisted reactor(Scrapy 使用的)can't 在单个线程中 started/stopped 不止一次。
假设 Twisted reactor 没有问题,您可以启动和停止它。它不会使事情变得更好,因为您的 scrape_it
函数可能会阻塞很长时间,因此您将需要许多 threads/processes.
我认为要走的路是使用像 Twisted 或 Tornado 这样的异步框架来创建一个 API;它将比基于 Flask(或基于 Django)的解决方案更有效,因为 API 将能够为请求提供服务,而 Scrapy 是 运行 蜘蛛。
Scrapy 基于 Twisted,因此使用 twisted.web 或 https://github.com/twisted/klein 可以更直接。但是 Tornado 也不难,因为你可以使用 Twisted 事件循环。
有一个名为 ScrapyRT 的项目,它做的事情与您想要实现的非常相似 - 它是 Scrapy 的 HTTP API。 ScrapyRT 基于 Twisted.
作为 Scrapy-Tornado 集成检查的示例 Arachnado - here 是一个关于如何将 Scrapy 的 CrawlerProcess 与 Tornado 的应用程序集成的示例。
如果你真的想要基于 Flask 的 API 那么在单独的进程中开始爬网可能是有意义的 and/or 使用像 Celery 这样的队列解决方案。这样你就失去了大部分的 Scrapy 效率;如果你这样做,你也可以使用 requests + BeautifulSoup。
我上周一直在做类似的项目,它是 SEO 服务 API,我的工作流程是这样的:
- 客户端向基于 Flask 的服务器发送一个请求,其中包含一个要抓取的 URL,以及一个回调 url 以在抓取完成时通知客户端(这里的客户端是另一个网络应用程序)
- 运行 使用 Celery 在后台进行 Scrapy。蜘蛛会将数据保存到数据库中。
- 后台服务将在蜘蛛完成时通过调用回调url通知客户端。