报表一多、Excel 一乱、同事一句“能不能顺手做个小工具”,Python 基本就躲不过去。
这种内部工具,最怕两件事:一是写得太重,上来就整 Django、整前后端分离,最后没人维护;二是写得太轻,几十个脚本散在服务器上,谁改过、跑没跑、结果存哪了,全靠猜。
我自己做这类东西,通常先不急着搭大架子。先看需求是不是逃不过这几件事:读 Excel、连数据库、做个页面、导个文件、发个通知、定时跑。能落在这几个坑里,库就好选了。
1. pandas:脏数据先别嘴硬,先洗干净
公司内部工具,十有八九都得碰表格。销售台账、财务对账、渠道名单、工单导出,格式一个比一个野。你要是还手搓 for 循环一行一行改,后面肯定返工。
import pandas as pd
df = pd.read_excel("客户导出.xlsx")
df["手机号"] = df["手机号"].astype(str).str.replace(r"\.0$", "", regex=True).str.strip()
df["注册时间"] = pd.to_datetime(df["注册时间"], errors="coerce")
df = df.dropna(subset=["客户ID"])
df = df[df["状态"].isin(["有效", "试用"])]
summary = (
df.groupby("销售")
.agg(客户数=("客户ID", "nunique"), 成交额=("合同金额", "sum"))
.reset_index()
)
summary.to_excel("清洗后结果.xlsx", index=False)
这类代码不花哨,但真管用。尤其是字段清洗、汇总、透视,pandas 干这个比你在 Excel 里点半天稳多了。
2. openpyxl:不是所有 Excel 都能只看数据
有些工具不是“算完就行”,还得让人能直接交付。带颜色、带公式、带批注、带表头样式,这时候只用 pandas 就不够了,得上 openpyxl。
from openpyxl import load_workbook
from openpyxl.styles import Font, PatternFill
wb = load_workbook("日报模板.xlsx")
ws = wb["汇总"]
ws["A1"] = "昨日异常订单汇总"
ws["A1"].font = Font(bold=True, size=14)
ws["A1"].fill = PatternFill("solid", fgColor="FFF2CC")
for row in range(2, ws.max_row + 1):
if ws[f"D{row}"].value and ws[f"D{row}"].value > 10000:
ws[f"D{row}"].fill = PatternFill("solid", fgColor="F4CCCC")
wb.save("日报结果.xlsx")
内部工具最后一公里,经常不是算法,是格式。领导不看你处理过程,只看导出的表顺不顺眼。
3. SQLAlchemy:一开始就把 SQL 写死,后面改起来很烦
很多人做内部工具,喜欢直接拼 SQL。刚开始是快,后来条件一多,分页、排序、筛选一起上,字符串拼接能把人看烦。
from sqlalchemy import create_engine, text
engine = create_engine("mysql+pymysql://user:pwd@10.10.1.23:3306/ops")
sql = text("""
select order_id, user_id, amount, status, created_at
from t_order
where created_at >= :start_time
and status = :status
order by created_at desc
limit 100
""")
with engine.connect() as conn:
rows = conn.execute(sql, {
"start_time": "2026-03-01 00:00:00",
"status": "FAILED"
}).mappings().all()
for row in rows[:3]:
print(row["order_id"], row["amount"])
我对内部工具连库这事有个习惯:查询和更新先分开,危险 SQL 单独收口,不让页面参数直接碰数据库。不是讲究,是被误删坑过。
4. Streamlit:后台同学做页面,别上来就折腾前端
很多内部系统,需求真的不复杂:上传个文件,选个日期,点个按钮,下载结果。这个场景下,Streamlit 很省事。
import streamlit as st
import pandas as pd
st.title("渠道数据校验工具")
uploaded = st.file_uploader("上传Excel", type=["xlsx"])
if uploaded:
df = pd.read_excel(uploaded)
st.write("原始数据", df.head())
bad_rows = df[df["佣金比例"] > 1]
st.write("异常数据", bad_rows)
if st.button("导出异常结果"):
bad_rows.to_excel("/tmp/bad_rows.xlsx", index=False)
with open("/tmp/bad_rows.xlsx", "rb") as f:
st.download_button("下载文件", f, file_name="异常结果.xlsx")
这个库最大的好处,不是炫,是省沟通。以前“你给我个脚本我不会跑”,现在给个页面地址,业务同学自己点。
5. requests:公司里一半工具,本质都是调接口
别把内部工具想得太神秘。很多时候就是把多个系统串起来:查用户、补数据、重试任务、批量通知。核心动作就是 HTTP 请求。
import requests
session = requests.Session()
session.headers.update({"Authorization": "Bearer internal-token"})
resp = session.post(
"http://internal-api/user/query",
json={"user_ids": [1001, 1002, 1003]},
timeout=5
)
resp.raise_for_status()
data = resp.json()
for item in data["data"]:
print(item["user_id"], item["nickname"], item["status"])
这里我一般都会带 timeout,不然你以为是脚本卡了,实际是接口在装死。内部接口尤其容易这样,日志还不一定全。
6. schedule:定时脚本别靠人记
每天 9 点发日报、每小时扫异常单、每晚同步一次名册,这种活交给人手动点,迟早漏。
import time
import schedule
def sync_blacklist():
print("开始同步黑名单...")
# 这里调用数据库或接口
print("同步完成")
schedule.every().day.at("09:00").do(sync_blacklist)
schedule.every(30).minutes.do(sync_blacklist)
while True:
schedule.run_pending()
time.sleep(1)
小工具阶段,schedule 足够用。真到多机器部署、失败重试、任务编排很复杂,再考虑 Celery、Airflow。别一开始就把路走重。
7. loguru:没有日志的内部工具,出了问题基本靠猜
内部工具最尴尬的场景,不是报错,是“有人说昨天还能用,今天不行了”。你问日志呢?没有。那就只能对着空气排查。
from loguru import logger
logger.add("tool.log", rotation="10 MB", retention="7 days")
def import_users(file_name):
logger.info("开始导入文件: {}", file_name)
try:
# 模拟业务处理
total = 128
success = 126
logger.info("导入完成,总数={}, 成功={}", total, success)
except Exception as e:
logger.exception("导入失败: {}", e)
import_users("user_list.xlsx")
日志这事我一直觉得别省。很多脚本不是写出来难,是三个月后你自己都不知道它当时怎么跑的。
这 7 个库,基本能把公司里大部分内部工具先搭起来:pandas 处理数据,openpyxl 修 Excel,SQLAlchemy 连库,Streamlit 出页面,requests 调接口,schedule 跑定时,loguru 留痕。
真做这类工具,别先想着“搞个完整平台”。先把最烦、最重复、最容易出错的那一步替掉。能让运营少复制一次 Excel,能让财务少改一列格式,能让你少半夜上服务器跑脚本,这工具就值了。如果你经常开发这类 Python 工具,不妨把这几件趁手的兵器先备齐。