窗外是上海漕河泾软件园傍晚六点的天空,灰蒙蒙的,分不清是雾霾还是夜色将至。我,32岁的Flovico,刚刚关掉那个爬取币圈数据的黑屏终端。屏幕暗下去,倒映出一张疲惫但异常清醒的脸。币圈的喧嚣像潮水一样退去,接外包项目时那种被客户需求牵着鼻子走的内耗感,也暂时被锁进了抽屉。今天,2017年8月10日,我决定做一件完全不同的事:给我的那些Python脚本,一个真正的、能见光的家。一个SaaS管理后台。
不再是本地命令行里敲回车,不再是只有我自己能看懂的黑底白字日志。我要让我的客户,哪怕是对着终端会发抖的小白,也能在一个漂亮的网页上,点一个按钮,然后看到“执行成功”的绿色提示。这个念头像一颗种子,在今天这个沉闷的傍晚,破土而出,带着不容置疑的力量。我知道,这意味着我要跳进一个完全陌生的技术泥潭:前后端分离。
非科班出身的PM,对Web开发的全部认知,还停留在几年前用PHP混着HTML写页面的程度。现在,我要面对的是Flask、Vue.js、RESTful API、ORM、JWT……这些名词像一堵墙。但我没退路。我的商业逻辑很简单,也很致命:如果我的数据服务永远只能通过我手动运行脚本、然后发邮件交付,那我的时间就被彻底锁死了,规模永远是一。我必须把“执行”这个动作产品化、界面化、自动化。这是从“手艺人”到“产品人”必须跨过的深渊。
我选择了Flask,因为它是Python系的,我的爬虫和数据清洗逻辑可以相对平滑地迁移。后端,好歹算是在熟悉的地盘作战。但前端,那完全是另一个世界。我硬着头皮开始搭环境,Node.js、npm、webpack、vue-cli……光是配环境就耗掉了一整个下午,各种版本冲突,控制台里红色的报错信息像永远不会停息的警报。我一边对着教程敲命令,一边在心里骂,这他妈比写爬虫绕过反爬机制还让人烦躁。爬虫是和机器斗,好歹有逻辑可循;配环境纯粹是和莫名其妙的依赖和路径斗气。
真正的崩溃在第二天下午降临。当我的Vue前端页面,终于颤颤巍巍地在浏览器里跑起来,我怀着朝圣般的心情,点击那个“启动任务”的按钮。浏览器开发者工具的网络选项卡里,那个POST请求变成了刺眼的红色。CORS。跨域请求。一个我听过但从未真正理解的词。
“No ‘Access-Control-Allow-Origin’ header is present on the requested resource.”
就这一行字,我折腾了整整一天。我在Flask后端疯狂搜索“CORS Flask”,尝试了`flask-cors`扩展,在`app.py`里加了又删,删了又加各种装饰器和配置。我检查端口,检查请求头,甚至怀疑是不是我的Chrome浏览器有问题。时间一分一秒过去,窗外的天色从明亮到昏暗,再到完全漆黑。办公室里只剩下我屏幕的光,和我越来越粗重的呼吸。那种感觉,就像你明明已经造好了一扇门,钥匙就在手里,但门上有一个看不见的力场,你就是插不进去。挫败感像冰冷的藤蔓缠上来。我一度想砸键盘,想把所有代码都删了,回去继续跑我的黑屏终端。那多简单,多直接啊。
但我知道不能回去。
我停下来,泡了杯浓得发苦的咖啡。强迫自己离开代码,去搜索引擎上看原理,而不是零散的解决方案。我慢慢明白了,这是浏览器出于安全考虑设下的屏障,后端必须明确告诉浏览器:“来自那个端口的请求,我允许。” 不是代码逻辑错了,是规则,是协议。当我最终在Flask应用初始化那里,用`CORS(app, resources={r”/api/*”: {“origins”: “*”}})`这行看似简单的代码解决了问题时,已经是深夜。没有狂喜,只有一种虚脱般的平静。我再次点击按钮。
网络请求的状态码变成了200。
然后,我在Vue组件里,用了一个小小的`this.$message.success(‘任务启动成功!’)`。当我刷新页面,点击,看到屏幕右下角真的优雅地滑出一个绿色的、带着勾的提示框时……
我愣住了。
紧接着,一股难以言喻的热流从胸腔直冲头顶。就是它!就是这种感觉!我不再是那个躲在命令行后面的黑客,我创造了一个世界。一个按钮,一个提示,背后是我写的Python逻辑在云端某个地方默默运行。客户不需要知道Flask,不需要知道CORS,他们只需要点击,然后看到“成功”。这种将复杂封装成简单的魔力,这种创造的实感,比之前任何一次爬取到百万数据都更让我战栗。
Flovico的雏形,就在这个深夜,被这个小小的绿色Toast提示框点亮了。我知道前面还有数据库设计、用户鉴权、任务队列、状态监控……无数座大山。但今晚,我跨过了最重要的一条河。从本地脚本到云端SaaS,这条路,我算是把第一块砖,歪歪扭扭地砌上了。














