ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

WorkBuddy + Flask + SQLite 日更建站实战:从数据到页面全链路

WorkBuddy + Flask + SQLite 日更建站实战:从数据到页面全链路 1. 从零建站这件事为什么我选了 WorkBuddy 加 Flask 这套组合去年年底我接手了一个小项目需求说起来简单做一个能每天更新内容的站点内容以农产品价格数据为主带可视化图表后台能自己维护数据不需要太复杂的功能但必须稳定、可控、成本低。我前后试过几种方案最后落地的组合是WorkBuddy 负责建站流程编排和日常更新调度Flask 做后端框架SQLite 存数据。这套组合跑了大半年日更没断过今天把整个过程完整记录下来。先说清楚这套方案适合谁。如果你是完全不懂代码、只想快速上线一个展示型站点的人那这套方案对你来说偏重了直接用现成的建站平台更省事。但如果你有一点 Python 基础想搞清楚站点从数据到页面的完整链路或者你手上有结构化的数据比如价格、库存、统计报表需要定期更新展示那这套组合非常合适。它的核心优势是数据完全在自己手里更新逻辑自己说了算成本几乎为零。WorkBuddy 在这套流程里扮演的角色很多人一开始会误解。它不是那种拖拽生成页面的可视化建站工具而是一个能把建站、部署、更新这些重复性动作串起来的编排工具。你可以把它理解成一个施工队长它不亲自砌砖但它知道先干什么后干什么什么时候该调用哪个脚本什么时候该把数据推到线上。Flask 和 SQLite 才是真正干活的工人和仓库。我选择 Flask 而不是 Django 或者 FastAPI理由很直接。这个站点不需要用户系统、不需要复杂的权限管理、不需要 ORM 的高级特性Flask 的轻量刚好匹配。一个app.py加几个模板文件就能跑起来调试的时候改完代码刷新页面就能看到效果这种即时反馈对日更场景特别重要。SQLite 的选择更简单单文件数据库备份就是复制一个文件日更场景下并发写入压力极小完全够用。用 DB Browser for SQLite 这类可视化工具打开就能看数据排查问题非常直观。提示如果你的站点预期日访问量超过几千或者需要多进程同时写入数据库SQLite 会成为瓶颈这时候要换成 PostgreSQL 或 MySQL。判断标准很简单看你的写入频率和并发量日更场景通常远达不到 SQLite 的上限。整套方案跑下来我最大的感受是建站最难的不是写代码而是把数据更新这件事变成不需要思考的日常动作。WorkBuddy 的价值就体现在这里它把更新流程固化下来让我不用每天手动登录服务器、手动跑脚本、手动检查页面。下面我按实际搭建顺序把每个环节拆开讲。2. 环境准备与工具选型每一步都有取舍理由2.1 Python 环境与 Flask 安装的实操细节Python 安装这一步看起来简单但踩坑的人不少。我建议直接用 Python 3.10 或 3.11不要用最新的 3.13因为部分第三方库的兼容性还没跟上。Windows 用户去官网下载安装包时务必勾选 Add Python to PATH这个选项不勾后面在命令行里敲python会提示找不到命令很多人卡在这一步。安装完成后验证一下python --version pip --version两条命令都能正常输出版本号说明环境没问题。接下来建虚拟环境这一步别省。虚拟环境的作用是把当前项目的依赖和系统全局的 Python 包隔离开避免不同项目之间互相污染。我见过太多人因为不建虚拟环境装了一堆包之后某个项目跑不起来排查半天发现是版本冲突。python -m venv venv # Windows venv\Scripts\activate # macOS / Linux source venv/bin/activate激活后命令行前面会出现(venv)标识。然后安装 Flaskpip install flask如果你还需要做数据可视化把 pandas 和 pyecharts 一起装上pip install pandas pyechartsVSCode 里配置 Python 环境时按CtrlShiftP输入 Python: Select Interpreter选中刚才创建的虚拟环境里的 python 解释器。这一步做完VSCode 的终端和调试器都会用这个环境不会出现明明装了包却提示找不到模块的问题。2.2 SQLite 与可视化工具的搭配使用SQLite 不需要单独安装服务Python 标准库自带sqlite3模块直接就能用。但为了日常查看和编辑数据方便我强烈建议装一个DB Browser for SQLite。这个工具免费、跨平台打开数据库文件就能看到表结构、数据内容还能直接执行 SQL 语句。我日常的工作流是这样的Flask 应用负责写入数据DB Browser 负责查看和临时修改。比如某天发现某条价格数据录错了直接打开 DB Browser找到对应表双击单元格改掉保存即可比写代码改快得多。这个工具还支持导出 CSV做数据备份或者迁移的时候很方便。建库的 SQL 语句我一般这样写CREATE TABLE IF NOT EXISTS price_data ( id INTEGER PRIMARY KEY AUTOINCREMENT, product_name TEXT NOT NULL, price REAL NOT NULL, unit TEXT DEFAULT 元/公斤, record_date DATE NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP );这里有几个设计细节值得说。id用自增主键方便后续更新和删除时定位记录。record_date单独存日期而不是用created_at代替因为价格数据的记录日期和入库时间是两个概念前者是业务时间后者是系统时间混在一起后面查询会很难受。unit给了默认值减少插入时的参数数量。2.3 WorkBuddy 的定位与安装配置WorkBuddy 的安装方式根据你的操作系统不同略有差异。Linux 和 Ubuntu 环境下通常通过命令行工具安装安装完成后会有一个工作台界面或者命令行入口。Windows 和 macOS 也有对应的安装包。安装完成后第一件事是配置工作目录也就是你的项目根目录后续所有的建站脚本、更新任务都在这个目录下执行。WorkBuddy 的核心概念是任务和技能。任务是你定义的一个个操作单元比如拉取最新数据更新数据库重新生成页面部署到服务器。技能则是预置的能力模块比如文件操作、命令执行、定时触发。你可以把多个任务串成一个工作流然后设定触发条件比如每天固定时间执行或者手动触发。注意WorkBuddy 和 CodeBuddy 经常被放在一起比较。简单说CodeBuddy 偏向代码生成和辅助编程WorkBuddy 偏向流程编排和任务自动化。两者定位不同不是替代关系。如果你只是想让 AI 帮你写代码用 CodeBuddy如果你想把建站和更新的重复动作自动化用 WorkBuddy。配置自定义指令的时候我建议把常用的操作封装成独立的任务而不是写一个巨大的脚本。比如拉取数据是一个任务清洗数据是另一个任务写入数据库又是一个任务。这样出问题的时候能快速定位是哪一步挂了而不是面对一个几百行的脚本无从下手。3. Flask 应用的核心结构从数据到页面的完整链路3.1 项目目录组织与路由设计我的项目目录结构是这样的project/ ├── app.py ├── data.db ├── templates/ │ ├── index.html │ └── detail.html ├── static/ │ ├── style.css │ └── chart.js ├── scripts/ │ ├── fetch_data.py │ └── update_db.py └── requirements.txtapp.py是 Flask 应用的入口templates放 HTML 模板static放静态资源scripts放数据处理的脚本。这个结构清晰后续 WorkBuddy 编排任务时每个脚本对应一个任务职责分明。路由设计上我只有三个核心路由from flask import Flask, render_template, jsonify import sqlite3 app Flask(__name__) def get_db(): conn sqlite3.connect(data.db) conn.row_factory sqlite3.Row return conn app.route(/) def index(): conn get_db() rows conn.execute( SELECT * FROM price_data ORDER BY record_date DESC LIMIT 50 ).fetchall() conn.close() return render_template(index.html, rowsrows) app.route(/api/prices) def api_prices(): conn get_db() rows conn.execute( SELECT product_name, price, record_date FROM price_data ORDER BY record_date ).fetchall() conn.close() return jsonify([dict(r) for r in rows]) app.route(/detail/int:item_id) def detail(item_id): conn get_db() row conn.execute( SELECT * FROM price_data WHERE id ?, (item_id,) ).fetchone() conn.close() return render_template(detail.html, rowrow)conn.row_factory sqlite3.Row这一行很关键它让查询结果可以像字典一样通过列名访问而不是只能用索引。比如row[product_name]比row[1]可读性好太多模板里写起来也方便。/api/prices这个接口是给前端图表用的返回 JSON 格式的数据。Flask 的jsonify会自动处理中文编码和数据类型转换比手动拼 JSON 字符串省事。这里有个细节SQLite 查出来的record_date是字符串格式price是浮点数jsonify都能正确处理不需要额外转换。3.2 模板渲染与数据绑定index.html里用 Jinja2 模板语法绑定数据!DOCTYPE html html head meta charsetUTF-8 title农产品价格看板/title link relstylesheet href{{ url_for(static, filenamestyle.css) }} /head body h1最新价格数据/h1 table thead tr th产品/th th价格/th th单位/th th日期/th /tr /thead tbody {% for row in rows %} tr tda href/detail/{{ row[id] }}{{ row[product_name] }}/a/td td{{ row[price] }}/td td{{ row[unit] }}/td td{{ row[record_date] }}/td /tr {% endfor %} /tbody /table div idchart/div script src{{ url_for(static, filenamechart.js) }}/script /body /htmlurl_for是 Flask 提供的辅助函数用来生成静态文件的 URL。它的好处是当你修改了静态文件目录结构时模板里的引用会自动跟着变不需要手动改每个链接。{% for %}和{{ }}是 Jinja2 的循环和变量输出语法写起来和 Python 很像上手很快。前端图表我用的是 ECharts通过/api/prices接口拿数据fetch(/api/prices) .then(response response.json()) .then(data { const dates data.map(item item.record_date); const prices data.map(item item.price); const chart echarts.init(document.getElementById(chart)); chart.setOption({ xAxis: { type: category, data: dates }, yAxis: { type: value }, series: [{ type: line, data: prices }] }); });这里有个常见问题Flask 返回的 JSON 里中文默认会被转义成 Unicode 编码。如果你希望返回原始中文在 Flask 应用里加一行配置app.config[JSON_AS_ASCII] FalseFlask 2.3 之后的版本这个配置项改成了app.json.ensure_ascii False具体用哪个取决于你的 Flask 版本报错的话查一下版本文档就行。3.3 数据写入与更新逻辑数据写入我单独放在scripts/update_db.py里不放在 Flask 应用里。原因是写入操作通常由 WorkBuddy 定时触发和 Web 服务的生命周期是分开的。如果写在 Flask 里每次更新都要重启服务没必要。import sqlite3 import csv from datetime import date def update_prices(csv_path): conn sqlite3.connect(data.db) cursor conn.cursor() with open(csv_path, r, encodingutf-8) as f: reader csv.DictReader(f) for row in reader: cursor.execute( INSERT INTO price_data (product_name, price, unit, record_date) VALUES (?, ?, ?, ?), (row[product_name], float(row[price]), row.get(unit, 元/公斤), row[record_date]) ) conn.commit() conn.close() if __name__ __main__: update_prices(latest_data.csv)用参数化查询?占位符而不是字符串拼接这是防止 SQL 注入的基本功。即使数据来源可信养成这个习惯也没坏处。conn.commit()不能忘不提交的话数据不会真正写入文件。更新已有数据用UPDATE语句UPDATE price_data SET price ?, record_date ? WHERE product_name ? AND record_date ?这里WHERE条件要足够精确否则会误更新其他记录。我一般用产品名加日期作为联合条件基本能唯一定位一条记录。4. WorkBuddy 编排日更流程把重复动作交给自动化4.1 日更任务的拆解与编排思路日更这件事拆开来看是四个动作获取数据、清洗数据、写入数据库、刷新页面缓存。如果每天手动做这四步坚持一周可能还行坚持一个月肯定会漏。WorkBuddy 的价值就是把这四步固化成一条流水线。我在 WorkBuddy 里建了一个工作流包含四个任务节点fetch_data执行python scripts/fetch_data.py从数据源拉取最新数据输出到latest_data.csvclean_data执行python scripts/clean_data.py处理缺失值、格式转换、去重update_db执行python scripts/update_db.py把清洗后的数据写入 SQLitenotify执行一个简单的检查脚本确认数据条数正常输出日志每个任务节点都可以单独运行方便调试。比如某天数据源格式变了fetch_data报错我可以单独跑这个任务看报错信息不用整条流水线重跑。触发条件我设的是每天早上 7 点。这个时间点的选择有讲究太早的话数据源可能还没更新太晚的话用户访问高峰已经来了。7 点是一个比较稳妥的中间值具体可以根据你的数据源更新规律调整。提示WorkBuddy 的定时触发依赖系统时间如果你部署在服务器上确认一下服务器时区设置是否正确。我遇到过服务器时区是 UTC结果任务在凌晨触发数据还没更新拉到的都是旧数据。4.2 数据拉取与清洗的实操要点fetch_data.py的逻辑取决于你的数据来源。如果是公开的数据接口用requests库请求然后解析 JSON如果是网页表格用pandas.read_html或者 BeautifulSoup 解析如果是手动维护的 Excel用pandas.read_excel读取。import requests import pandas as pd def fetch(): url https://example.com/api/prices response requests.get(url, timeout30) response.raise_for_status() data response.json() df pd.DataFrame(data) df.to_csv(latest_data.csv, indexFalse, encodingutf-8)timeout30这个参数别省不加的话网络卡住时脚本会一直挂着WorkBuddy 的任务会超时失败。raise_for_status()会在 HTTP 状态码不是 200 时抛异常让问题尽早暴露。清洗环节我主要做三件事去掉价格为空的行、把价格统一转成浮点数、按产品名和日期去重。def clean(input_path, output_path): df pd.read_csv(input_path) df df.dropna(subset[price]) df[price] pd.to_numeric(df[price], errorscoerce) df df.dropna(subset[price]) df df.drop_duplicates(subset[product_name, record_date], keeplast) df.to_csv(output_path, indexFalse, encodingutf-8)errorscoerce的作用是遇到无法转换的值时设为 NaN 而不是直接报错配合后面的dropna把脏数据过滤掉。keeplast表示重复数据保留最后一条因为通常最后一条是最新的。4.3 部署与访问验证本地开发完成后部署到服务器上。最简单的方案是用 Flask 自带的开发服务器跑起来验证flask run --host0.0.0.0 --port5000--host0.0.0.0让服务监听所有网络接口这样外部才能访问。但 Flask 自带的服务器不适合生产环境并发能力弱。正式部署我建议用 Gunicorn 加 Nginx 的组合gunicorn -w 4 -b 127.0.0.1:8000 app:app-w 4表示启动 4 个工作进程具体数量一般是 CPU 核心数的 2 倍加 1。Nginx 负责反向代理和静态文件服务配置里把/static路径直接指向静态文件目录减轻 Flask 的压力。部署完成后用 WorkBuddy 把重启服务也做成一个任务。每次数据更新后如果页面有缓存触发一次服务重启或者缓存刷新。这样用户看到的永远是最新数据。5. 常见问题与排查技巧实录5.1 数据库相关的高频问题问题一数据库文件被锁写入报 database is locked。这个问题的根源是 SQLite 的并发写入限制。同一时间只能有一个写入操作如果 Flask 正在读数据WorkBuddy 的写入任务就可能被阻塞。解决办法是给连接设置超时时间conn sqlite3.connect(data.db, timeout10)timeout10表示等待 10 秒如果还拿不到锁就报错。更彻底的方案是把读写分离写入操作集中在 WorkBuddy 任务里Flask 只负责读。问题二中文乱码。SQLite 默认用 UTF-8 编码但如果你在 Windows 上用命令行工具查看数据可能会因为终端编码问题显示乱码。用 DB Browser for SQLite 打开就不会有这个问题。写入数据时确保 Python 脚本的文件编码是 UTF-8open()时显式指定encodingutf-8。问题三数据库文件越来越大。日更场景下如果每次都是插入新记录而不清理旧数据文件会持续增长。定期执行VACUUM命令可以压缩数据库文件VACUUM;这个命令会重建数据库文件释放未使用的空间。建议每月执行一次或者在 WorkBuddy 里加一个定期清理任务。5.2 Flask 调试与部署的踩坑记录问题修改代码后页面没变化。Flask 开发模式下默认开启自动重载但如果你是用flask run启动的确认FLASK_ENVdevelopment或者FLASK_DEBUG1已设置。生产环境下不会自动重载改完代码要手动重启 Gunicorn。问题静态文件 404。检查static目录的位置是否正确Flask 默认在应用根目录下找static文件夹。如果你把静态文件放在别的地方需要在创建 Flask 应用时指定static_folder参数。问题模板找不到。同理templates目录必须在应用根目录下或者通过template_folder参数指定。我见过有人把模板放在static里面然后抱怨渲染不出来这就是目录结构没搞对。问题端口被占用。启动时报 Address already in use说明端口已经被其他程序占了。换一个端口或者用lsof -i :5000Linux/macOS或netstat -ano | findstr :5000Windows找到占用端口的进程决定是杀掉还是换端口。5.3 WorkBuddy 任务失败的排查思路WorkBuddy 任务失败时第一件事是看日志。每个任务的执行输出都会记录重点看报错信息和退出码。退出码非 0 通常意味着脚本执行出错。常见的失败原因我整理成了一张表现象可能原因排查方法任务超时网络请求卡住或脚本死循环检查脚本里的 timeout 设置加日志输出数据为空数据源未更新或接口变更手动执行拉取脚本看返回内容写入失败数据库被锁或磁盘满检查磁盘空间确认没有其他进程占用数据库页面未更新缓存未刷新或服务未重启手动访问接口确认数据检查部署脚本我的经验是给每个任务加上明确的日志输出记录开始时间、结束时间、处理的数据条数。这样出问题时一眼就能看出是哪一步、什么时间出的问题。WorkBuddy 的任务编排界面通常能看到每个节点的状态绿色通过、红色失败配合日志基本能定位到根因。提示如果某个任务频繁失败不要急着改代码先确认是不是外部依赖的问题。比如数据源网站改版、接口地址变更、服务器网络波动。这些外部因素导致的失败改代码是解决不了的要调整的是任务的重试策略或者数据源本身。6. 一些实操下来觉得值得分享的细节整套流程跑了大半年有几个细节是我在实际操作中慢慢摸索出来的文档里不会写但确实能省不少事。第一个是数据库备份。SQLite 的好处是备份简单直接复制data.db文件就行。我在 WorkBuddy 里加了一个每周备份任务把数据库文件复制到带日期的备份目录保留最近 8 周的备份。这个动作花不了几秒钟但真出问题的时候能救命。有一次我误执行了一条DELETE语句没加WHERE条件把整张表清空了就是从备份里恢复的。第二个是数据校验。日更数据最怕的是看起来更新了实际上数据是错的。我在写入数据库之前加了一个简单的校验新数据的条数不能少于历史平均值的 50%价格不能为负数日期不能是未来日期。任何一条不满足就中止写入并报警。这个校验拦住过好几次数据源异常的情况。第三个是页面缓存策略。Flask 默认不缓存页面每次请求都重新查询数据库。对于日更站点来说这其实没问题因为数据一天才变一次。但如果你发现页面加载慢可以在 Flask 里加一个简单的缓存装饰器把查询结果缓存几分钟。不过要注意数据更新后要主动清除缓存否则用户看到的是旧数据。第四个是日志分级。我在脚本里用 Python 的logging模块把日志分成 INFO、WARNING、ERROR 三个级别。正常执行记 INFO数据异常记 WARNING脚本报错记 ERROR。WorkBuddy 的任务日志里可以按级别过滤排查问题时直接看 ERROR 级别的记录效率高很多。最后说一个关于 WorkBuddy 自定义指令的心得。我一开始把所有逻辑都写在一个大脚本里后来发现调试很痛苦。改成每个任务对应一个小脚本之后不仅调试方便复用性也提高了。比如清洗数据这个脚本后来我在另一个项目里直接拿过来用只改了字段名就跑了。任务拆得越细复用性越好这是我在多个项目里验证过的规律。
返回列表