1. 为什么我放弃了WordPress转头用WorkBuddyFlask从零搭站去年年底我给自己定了个目标做一个能日更的农产品价格数据小站。不是那种花架子展示页而是每天能自动抓数据、入库、渲染图表、还能让我在后台手动改几条记录的真东西。一开始我走的是WordPress路线插件装了一堆采集插件、图表插件、缓存插件结果站点越跑越慢数据库里全是插件留下的垃圾表改一个字段要在后台点七八层菜单。折腾了大概两周我彻底放弃了。后来我把目光转向了自建站方案。这里先澄清一个很多人搞混的问题Shopify、WordPress、自建站到底有什么区别。Shopify是托管电商你租的是它的店铺系统数据和模板都锁在它生态里WordPress是内容管理系统灵活但依赖插件生态性能上限受PHP和MySQL配置影响而自建站是你自己写后端、自己管数据库、自己控渲染自由度最高代价是啥都得自己动手。我选的是第三条路技术栈定成Flask SQLite Python开发辅助工具用WorkBuddy。为什么是WorkBuddy因为我不是科班后端出身写Flask的时候经常卡在“这个路由该怎么绑”“模板变量传没传进去”“SQLite的update语句为什么没生效”这种具体问题上。WorkBuddy在这类场景里更像一个随时能问的结对伙伴它能帮我把零散的Python代码片段串成可运行的模块也能在我贴出报错栈的时候给出排查方向。注意它不是替你建站的一键工具而是加速你理解和落地的辅助角色。这一点想清楚后面才不会走偏。这套组合适合谁适合有一点Python基础、想拥有一个完全可控的小型数据站、并且愿意每天花二三十分钟维护的人。如果你连Python都没装过也别慌我会把安装和环境配置的坑一并写清楚。整篇记录围绕我真实的建站和日更流程展开包括Flask怎么绑定到网页元素、SQLite怎么可视化管理、WorkBuddy在哪些环节真正省了时间、以及日更机制怎么设计才不会把自己累死。2. 环境搭建Python、VSCode与SQLite这三样必须先落地2.1 Python安装与VSCode环境配置的实际顺序很多人一上来就装一堆东西结果环境变量没配好pip用不了后面全乱。我的建议是严格按顺序来先装Python再配VSCode最后装SQLite工具。Python安装教程网上满天飞但真正容易踩的坑是安装时那个“Add Python to PATH”的勾。Windows上如果你没勾后面在命令行敲python会直接提示找不到命令这时候要么重装要么手动去环境变量里加路径非常麻烦。我建议直接去官网下最新稳定版安装界面第一个勾务必打上。装完之后验证三步命令行敲python --version看版本敲pip --version看包管理器敲python -m venv testenv看能不能建虚拟环境。这三步都过了说明Python本体没问题。接下来是VSCode Python环境配置。装好VSCode后第一件事是装Python扩展第二件事是选解释器。按CtrlShiftP输入“Python: Select Interpreter”选中你刚装的那个Python路径。很多人代码跑不起来就是因为VSCode默认选了个别的解释器或者根本没选。虚拟环境这块我强烈建议每个项目单独建一个。命令是python -m venv venv # Windows激活 venv\Scripts\activate # macOS/Linux激活 source venv/bin/activate激活后命令行前面会出现(venv)前缀这时候再pip install flask包就只装在这个项目里不会污染全局。我见过太多人全局装了几十个包最后版本冲突到没法收拾。2.2 SQLite的定位为什么小站不该上MySQLSQLite数据库最容易被误解的一点是“它是不是太弱了”。实际上对于日更型小站SQLite完全够用甚至比MySQL更合适。它是文件型数据库整个库就是一个.db文件备份就是复制文件迁移就是拷走文件不需要单独起数据库服务。我的农产品价格站每天新增几百条记录查询都是按日期和品类过滤SQLite的响应在毫秒级根本感受不到瓶颈。安装方面SQLite本身通常随Python自带你import sqlite3就能用不需要额外装服务端。但可视化工具必须配一个否则你天天用命令行查数据会疯。我用的是DB Browser for SQLite免费、跨平台、界面直观。装好之后直接打开你的.db文件能看到所有表、字段、索引还能直接执行SQL。这里有个细节DB Browser修改数据后要记得点“Write Changes”否则你的改动只在内存里关掉就没了。我第一次用的时候就因为这个丢过一批测试数据。如果你用Android Studio做移动端它内置的SQLite可视化工具也能看库但那个偏开发调试日常管理还是DB Browser顺手。至于“SQLite数据库文件能否加密”原生SQLite不直接支持加密需要用到SQLCipher这类扩展对于内部小站来说没必要做好文件权限和备份就行。2.3 WorkBuddy安装与它在环境阶段的真实作用WorkBuddy安装教程的核心其实就一句话把它当成你编辑器或终端旁边的常驻助手。我是在VSCode里配合使用的遇到环境报错直接把错误信息贴给它。比如我第一次配虚拟环境时Windows PowerShell提示“无法加载文件 activate.ps1因为在此系统上禁止运行脚本”这个问题卡了我半小时。WorkBuddy给我的排查路径是先确认执行策略用Get-ExecutionPolicy查看如果是Restricted就用Set-ExecutionPolicy -Scope CurrentUser RemoteSigned放开当前用户。这个思路比我自己瞎搜快多了。需要说明的是WorkBuddy有国际版也有和CodeBuddy并列的产品线功能侧重不太一样。CodeBuddy更偏代码补全和生成WorkBuddy更偏任务编排和流程辅助。我在建站阶段主要用WorkBuddy来梳理“下一步该做什么”比如它会提醒我先建数据库schema再写路由而不是反过来。这个顺序很重要先有数据结构再有接口改起来成本低。3. 数据库设计用DB Browser把表结构一次定清楚3.1 农产品价格表到底该存哪些字段建站最容易返工的地方就是表结构。我一开始只存了品类、价格、日期三个字段结果日更两周后发现想按产地筛选、想对比不同市场的价格全都没法做。后来重新设计字段扩成这些id主键自增、category品类如白菜、土豆、market市场名、origin产地、price价格用REAL类型、unit单位如元/斤、record_date记录日期用TEXT存ISO格式、created_at入库时间戳。为什么日期用TEXT而不是DATE类型因为SQLite的日期函数对TEXT格式的ISO字符串支持最好WHERE record_date 2024-01-15这种查询直接走索引简单可靠。价格用REAL会有浮点精度问题如果你要做精确的金额计算建议用INTEGER存“分”展示时再除以100。我这个站只是展示趋势REAL够用。建表语句我直接在DB Browser的“Execute SQL”里跑CREATE TABLE price_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, category TEXT NOT NULL, market TEXT NOT NULL, origin TEXT, price REAL NOT NULL, unit TEXT DEFAULT 元/斤, record_date TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_date_category ON price_records(record_date, category);那个索引是关键。日更站最常见的查询就是“某品类最近30天价格”没有索引的话数据量上万后查询会明显变慢。加索引后同样的查询基本瞬间返回。3.2 用WorkBuddy辅助生成建表与迁移脚本表结构定下来后我让WorkBuddy帮我把建表语句整理成一个可重复执行的Python脚本这样换台机器也能一键初始化。它给我的结构是先检查表是否存在不存在才创建避免重复执行报错。import sqlite3 def init_db(db_pathprice.db): conn sqlite3.connect(db_path) cursor conn.cursor() cursor.execute( CREATE TABLE IF NOT EXISTS price_records ( id INTEGER PRIMARY KEY AUTOINCREMENT, category TEXT NOT NULL, market TEXT NOT NULL, origin TEXT, price REAL NOT NULL, unit TEXT DEFAULT 元/斤, record_date TEXT NOT NULL, created_at TEXT DEFAULT CURRENT_TIMESTAMP ) ) cursor.execute( CREATE INDEX IF NOT EXISTS idx_date_category ON price_records(record_date, category) ) conn.commit() conn.close() if __name__ __main__: init_db() print(数据库初始化完成)IF NOT EXISTS这个写法是精髓它让脚本具备幂等性跑多少次都不会出错。这个模式后来被我复用到所有初始化逻辑里。3.3 数据录入的两种方式手动与脚本批量日更站的数据来源通常有两种一种是你自己手动整理一种是脚本抓取。手动录入我直接在DB Browser里点“New Record”填适合少量修正。批量录入走Python脚本用executemany一次插入多条records [ (白菜, 城东市场, 山东, 1.85, 元/斤, 2024-01-15), (土豆, 城西市场, 甘肃, 2.10, 元/斤, 2024-01-15), ] conn.executemany( INSERT INTO price_records (category, market, origin, price, unit, record_date) VALUES (?,?,?,?,?,?), records ) conn.commit()用参数化查询那些问号而不是字符串拼接能避免引号转义问题也更安全。这一点WorkBuddy在我第一次写插入语句时就提醒过我当时我还在用f-string拼SQL遇到品类名里带单引号直接报错。4. Flask路由与模板把数据库里的数据绑到网页元素上4.1 最小可运行Flask应用的骨架Flask框架的好处是起步极简。一个能跑的应用就这几行from flask import Flask, render_template import sqlite3 app Flask(__name__) app.route(/) def index(): conn sqlite3.connect(price.db) conn.row_factory sqlite3.Row cursor conn.cursor() cursor.execute(SELECT * FROM price_records ORDER BY record_date DESC LIMIT 50) rows cursor.fetchall() conn.close() return render_template(index.html, recordsrows) if __name__ __main__: app.run(debugTrue)conn.row_factory sqlite3.Row这行很关键它让查询结果可以像字典一样用字段名访问模板里写record[category]而不是record[1]可读性天差地别。debugTrue只在开发时开它会自动重载代码并显示详细错误页但上线必须关掉。4.2 Flask如何绑定到网页元素模板变量传递的完整链路“Flask如何绑定到网页元素”这个问题本质是问数据怎么从后端传到前端。链路是这样的路由函数查询数据库得到rows通过render_template的第二个参数传给模板模板里用Jinja2语法{{ }}输出用{% %}做循环和判断。模板templates/index.html的核心部分table thead tr th品类/thth市场/thth产地/thth价格/thth日期/th /tr /thead tbody {% for record in records %} tr td{{ record[category] }}/td td{{ record[market] }}/td td{{ record[origin] }}/td td{{ record[price] }} {{ record[unit] }}/td td{{ record[record_date] }}/td /tr {% endfor %} /tbody /table这里有个新手常踩的坑模板文件必须放在项目根目录下的templates文件夹里名字不能错Flask默认只去那里找。静态文件CSS、JS放static文件夹。我第一次把html放在根目录怎么都渲染不出来排查了半天。4.3 用WorkBuddy排查“变量传了但页面不显示”的问题我遇到过最诡异的一次是路由里明明查到了数据print(rows)也有输出但页面就是空白。WorkBuddy让我按三步排查第一确认render_template的变量名和模板里用的名字完全一致大小写敏感第二在模板里临时加{{ records }}看原始输出如果显示的是空列表说明查询结果为空第三检查数据库连接是不是连到了另一个.db文件。结果我是第三种情况。项目目录下有两个db文件一个是我手动建的一个是脚本生成的Flask连的是空的那个。这个问题在有多环境时特别常见。后来我统一用绝对路径或者基于__file__的相对路径来定位数据库文件彻底避免。import os BASE_DIR os.path.dirname(os.path.abspath(__file__)) DB_PATH os.path.join(BASE_DIR, price.db)4.4 数据可视化用Chart.js把价格趋势画出来光有表格不够直观我加了趋势图。前端用Chart.js后端提供一个返回JSON的接口from flask import jsonify app.route(/api/trend/category) def trend(category): conn sqlite3.connect(DB_PATH) cursor conn.cursor() cursor.execute( SELECT record_date, AVG(price) FROM price_records WHERE category ? GROUP BY record_date ORDER BY record_date , (category,)) data cursor.fetchall() conn.close() return jsonify({ labels: [row[0] for row in data], values: [row[1] for row in data] })前端用fetch拿数据喂给Chart.js。这里注意jsonify返回的格式前端要对应解析labels和values两个数组长度必须一致否则图表会错位。我一开始因为某天数据缺失导致长度不匹配图直接画歪了后来在SQL里用GROUP BY保证每天只有一条聚合记录才解决。5. 日更机制让站点每天自动有新内容5.1 日更不是每天手动敲而是设计一条流水线“日更”听起来很勤奋但如果每天都要手动查数据、手动录入、手动发布坚持不了一周就会放弃。我的做法是把日更拆成三个可自动化的环节数据获取、数据入库、页面刷新。数据获取我用Python爬虫抓公开的农产品价格信息入库用脚本页面因为是动态渲染的数据一进库刷新就更新不需要重新生成静态页。爬虫部分要克制只抓公开的、允许访问的页面控制频率不要给对方服务器造成压力。我一般每天固定时间跑一次抓取当天的价格数据清洗后入库。清洗包括去空格、统一单位、过滤异常值比如价格突然是0或者几千明显是脏数据。5.2 用WorkBuddy编排每日任务清单WorkBuddy在日更环节的价值是帮我管理“今天该做什么”。我给它设了一套自定义指令比如“检查昨日数据完整性”“生成本周价格波动摘要”“提醒备份数据库”。它会把任务拆成步骤我照着执行就行。这种编排能力比单纯问代码问题更省心因为它管的是流程而不是片段。备份这块我要强调SQLite虽然是一个文件但备份不能简单复制正在写入的文件可能拿到不一致的状态。正确做法是用SQLite自带的备份命令import sqlite3 source sqlite3.connect(price.db) backup sqlite3.connect(backup/price_20240115.db) source.backup(backup) backup.close() source.close()这样得到的是完整一致的副本。我每天日更完成后自动跑一次备份保留最近30天。5.3 数据更新与删除SQLite update语句的正确姿势日更过程中难免要修正数据比如某天价格录错了。SQLite的update语句写法UPDATE price_records SET price 2.35, unit 元/斤 WHERE id 123;关键在WHERE条件漏写WHERE会更新全表这是灾难性的。我建议在DB Browser里执行update前先跑一遍对应的SELECT确认影响的行数对不对再改成UPDATE。另外如果开了事务但没commit改动不会落盘DB Browser里记得点Write Changes脚本里记得conn.commit()。删除同理DELETE FROM price_records WHERE id ?永远带条件。我给自己定了个规矩任何写操作先在测试库上跑一遍。6. 部署上线从本地跑通到公网可访问6.1 Flask部署的几种方式和选择逻辑本地app.run()只适合开发上线要用生产级服务器。常见方案是GunicornLinux或WaitressWindows做WSGI服务器前面挂Nginx做反向代理。我选的是GunicornNginx因为部署在Linux服务器上更稳。启动命令gunicorn -w 4 -b 127.0.0.1:8000 app:app-w 4是4个工作进程根据CPU核数调整一般设成核数×21。app:app是模块名:应用实例名。Nginx那边配置反向代理到8000端口顺便处理静态文件减轻Flask压力。6.2 上线前必须检查的清单上线前我列了个清单逐项过debug模式必须关数据库文件权限设成只有运行用户可读写备份脚本挂到定时任务日志输出到文件而不是控制台错误页面自定义别把堆栈暴露给用户。这几项里最容易忘的是debug模式开着debug上线等于把服务器内部信息公开展示非常危险。6.3 日更站在线上的稳定性观察上线后我观察了两周主要看三件事响应时间、错误日志、数据库大小增长。响应时间稳定在200毫秒以内错误日志里偶尔有爬虫超时的记录不影响主流程。数据库两周增长了约5MB按这个速度一年也就100多MBSQLite完全扛得住。如果哪天数据量真的上来了再考虑迁移到PostgreSQL但那是后话现在没必要过度设计。7. 这一路踩过的坑和WorkBuddy真正帮上忙的地方回头看这个站从零到日更稳定运行花了我大概三周业余时间。最大的坑不是技术难点而是“想太多做太少”。我一开始纠结要不要上Docker、要不要用ORM、要不要做用户系统结果WorkBuddy提醒我先让最小版本跑起来再迭代。这句话点醒了我于是我用最朴素的FlaskSQLite把核心链路打通后面所有优化都是在这个能跑的版本上做的。WorkBuddy真正帮上忙的场景有三个一是环境报错的快速定位尤其是Windows下的路径和权限问题二是代码片段的串联它能把散落的函数组织成有逻辑的模块三是流程提醒日更这种需要长期坚持的事有个东西帮你盯着任务清单比纯靠自律靠谱。至于“WorkBuddy从入门到精通”这种资料我的建议是别指望看完就会直接拿一个真实小项目练遇到问题再查效率高十倍。最后分享一个我自己的小习惯每次改完代码先在本地跑一遍完整流程——初始化库、插入测试数据、启动服务、打开页面、点一遍所有功能。这个习惯帮我拦住了至少五次“改A坏B”的低级错误。建站这事稳比快重要。
