用 Heroku 托管 Flask App 时,每一个连接最多保持 30 秒,而且这个限制是没法取消的。30 秒的时间对于一些爬虫类 API 可能就不太够了,经常会遇到超时被强制结束进程的情况。为了让这类耗时较长的 API 正常运行,我们可能需要用一些奇技淫巧。

思路

经测试,Heroku 虽然会直接中止连接进程,但通过 subprocess 运行的后台脚本是不会被中止的。因此我们可以:

API 被访问 → 启动前台后台双爬虫 → 后台爬虫将数据写入数据库(MongoDB、LeanCloud,等等)→ 若前台没超时,那皆大欢喜 → 若前台超时了,下一次再访问 API 就直接从数据库里面取,不需要再爬一次了。

之前失败的尝试

之前以为 Heroku 只是超时断开连接,但程序还是接着跑,于是就在 app.py 开一个全局字典变量存数据,结果发现数据存不进去,估计是断开连接的同时也把进程终止了。

涉及的库(Libraries)

用到的库有:

  • subprocess:启动后台脚本
  • click:命令行支持
  • 数据库支持

目录结构

本例的,目录结构如下:

1
2
3
4
5
./
...
app.py # Flask 主程序
background_job.py # 后台任务(爬虫)
...

代码修改

app.py 中返回数据的 API:

1
2
3
4
if 数据库内有对应数据:
return DATA
else:
subprocess.Popen(['python', './background_job.py', ARGUMENT_1, ARGUMENT_2, ...])

background_job.py:

1
2
3
4
5
6
7
8
9
10
@click.command()
@click.argument('ARGUMENT_1')
@click.argument('ARGUMENT_2')
...
def crawl(ARGUMENT_1, ARGUMENT_2, ...):
# 爬取数据并放入数据库
pass

if __name__ == "__main__":
crawl()

一些可以做的改进

  • API 中添加强制刷新数据库数据的接口
  • 数据库数据用多少次之后/超过多少时间后自动作废