/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */
#content_views .toc,
/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */
#content_views.markdown_views > p:empty:has(+ .toc),
#content_views.markdown_views > .toc + p:empty,
/* 富文本旧版目录标记 */
#content_views.htmledit_views #main-toc,
#content_views.htmledit_views #hr-toc,
#content_views.htmledit_views p[id*=\”-toc\”] {
display: none !important;
}
/* 目录去掉后,紧跟的首个标题不再多出一块上边距 */
#content_views.markdown_views > .toc + h1,
#content_views.markdown_views > .toc + h2,
#content_views.markdown_views > .toc + h3,
#content_views.markdown_views > .toc + h4,
#content_views.markdown_views > .toc + p:empty + h1,
#content_views.markdown_views > .toc + p:empty + h2,
#content_views.markdown_views > .toc + p:empty + h3,
#content_views.markdown_views > .toc + p:empty + h4 {
margin-top: 0 !important;
}
1. 采集脚本跑通了,为什么一换机器就报鉴权错
写 Python 数据采集脚本的人,大概率都经历过这个阶段:本地跑得好好的,换台机器、换个同事、或者过两天再跑,突然就 401 了。代码没动,数据库也没动,动的只是那个藏在脚本里的 API Key。
这篇要解决的就是这个链路问题。从请求发出、到数据解析、再到落库,整条链路上最容易出问题的其实不是 SQL 语句,而是采集端凭证的管理方式。我见过太多项目把 Key 硬编码在 spider.py 第 8 行,然后靠 git 提交记录去追溯谁改过。一旦要接多个数据源、多个模型接口,Key 就散落在十几个文件里,排查一次鉴权报错要翻半小时。
所以这篇的定位很明确:给正在写 Python 采集入库脚本、并且需要调用外部 API 通道的开发者,提供一套可复制的配置骨架。核心思路是把采集端的凭证从代码里抽出来,统一走 TaoToken 的 Key 管理,让 config.toml 成为唯一的凭证入口。适合谁?适合已经会写 requests 或 selenium 采集、会连 MySQL、但被 Key 管理折腾过的人。
下面按「问题场景 → 前置准备 → 配置骨架 → 端到端验证 → 报错排查」的顺序走一遍,每一步都能直接抄。
2. 把采集端凭证收口到 TaoToken
先说清楚 TaoToken 在这个链路里扮演什么角色。你的采集脚本要调用模型接口做数据清洗、字段抽取、或者内容分类,这些调用都需要一个 API 通道。TaoToken 提供的就是这个统一通道,官网在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 入口是 https://taotoken.net/api 。
关键点是:你不需要在每个采集脚本里各写一份 Key。统一在 TaoToken 控制台生成 Key,然后所有采集端、清洗端、入库端都读同一个配置文件。这样换机器只需要改配置文件,不用动代码。
具体操作路径是这样的:先到控制台创建 Key,地址是 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,然后在 API Keys 页面生成密钥,地址是 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 。生成后先别急着写进代码,我们下一步用配置文件接住它。
如果你只是想先验证模型能不能通,可以直接用模型对话页面测一下,地址 https://taotoken.net/models?utm_source=taotoken_aicg_blog_end&utm_content=models&utm_campaign=rewrite 。确认通道没问题,再往下做采集集成。
注意:Key 只生成一次可见,复制后立刻存进配置文件或环境变量,别留在聊天记录里。
3. 可复制的 config.toml 与数据库连接串骨架
这一步是全文的核心。我们把配置拆成三块:TaoToken 通道配置、数据库连接配置、采集任务参数。用 TOML 是因为它比 JSON 好写注释,比 YAML 不容易缩进踩坑。
先建目录结构:
project/
├── config.toml
├── spider.py
├── db.py
└── requirements.txt
config.toml 骨架如下,直接复制改值:
# TaoToken 统一通道配置
[taotoken]
base_url = "https://taotoken.net/api"
api_key = "sk-你的Key粘贴在这里"
model = "claude-3-5-sonnet"
timeout = 30
max_retries = 3
# 数据库连接配置
[database]
host = "127.0.0.1"
port = 3306
user = "root"
password = "your_password"
db = "spider_data"
charset = "utf8mb4"
# 采集任务参数
[spider]
start_url = "https://example.com/list"
page_count = 10
sleep_interval = 2
table_name = "house_info"
数据库连接串模板单独放一份,方便复用:
# db.py
import pymysql
import tomllib
def load_config(path="config.toml"):
with open(path, "rb") as f:
return tomllib.load(f)
def get_conn(cfg):
db = cfg["database"]
return pymysql.connect(
host=db["host"],
port=db["port"],
user=db["user"],
password=db["password"],
database=db["db"],
charset=db["charset"],
cursorclass=pymysql.cursors.DictCursor,
)
注意 tomllib 是 Python 3.11 起内置的,如果你用 3.10 及以下,装 tomli 然后 import tomli as tomllib 即可。
采集脚本里读配置的方式:
# spider.py
import requests
from db import load_config, get_conn
cfg = load_config()
tt = cfg["taotoken"]
headers = {
"Authorization": f"Bearer {tt['api_key']}",
"Content-Type": "application/json",
}
def call_model(prompt):
payload = {
"model": tt["model"],
"messages": [{"role": "user", "content": prompt}],
}
resp = requests.post(
f"{tt['base_url']}/v1/chat/completions",
headers=headers,
json=payload,
timeout=tt["timeout"],
)
resp.raise_for_status()
return resp.json()["choices"][0]["message"]["content"]
建表语句沿用你熟悉的写法,字段按采集目标调整:
def ensure_table(conn, table):
sql = f"""
CREATE TABLE IF NOT EXISTS {table} (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
sale VARCHAR(255),
house_type VARCHAR(255),
big_add VARCHAR(255),
small_add VARCHAR(255),
shape VARCHAR(255),
square VARCHAR(255),
avg_price VARCHAR(255),
sum_price VARCHAR(255),
subway VARCHAR(255),
shopping VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
"""
with conn.cursor() as cur:
cur.execute(sql)
conn.commit()
批量入库用 executemany,参数集是 tuple 套 tuple:
def save_rows(conn, table, rows):
sql = f"""INSERT INTO {table}
(name, sale, house_type, big_add, small_add, shape,
square, avg_price, sum_price, subway, shopping)
VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)"""
with conn.cursor() as cur:
n = cur.executemany(sql, rows)
conn.commit()
return n
到这里,凭证、连接、入库三块都收口了。换机器只改 config.toml,代码一行不动。
4. 一次端到端验证:从请求到落库
配置写完必须验证,不然你不知道是通道问题还是 SQL 问题。写一个最小验证脚本:
# verify.py
from db import load_config, get_conn
from spider import call_model, ensure_table, save_rows
cfg = load_config()
conn = get_conn(cfg)
table = cfg["spider"]["table_name"]
ensure_table(conn, table)
# 1. 验证 TaoToken 通道
answer = call_model("只回复两个字:通了")
print("模型返回:", answer)
# 2. 验证入库
rows = [
("测试小区A", "在售", "住宅", "朝阳", "某路1号",
"两室一厅", "89", "50000", "445", "无地铁", "购物方便"),
("测试小区B", "待售", "别墅", "海淀", "某路2号",
"四室两厅", "200", "80000", "1600", "地铁沿线", "无商场"),
]
n = save_rows(conn, table, rows)
print(f"入库 {n} 行")
# 3. 回查确认
with conn.cursor() as cur:
cur.execute(f"SELECT COUNT(*) AS c FROM {table}")
print("表内总数:", cur.fetchone()["c"])
conn.close()
跑 python verify.py,预期输出类似:
模型返回: 通了
入库 2 行
表内总数: 2
三个信号都对了,说明通道通、SQL 对、提交生效。如果模型那步报 401,往下看排查章节;如果入库报错,先检查表字段数量和 VALUES 占位符是否一致。
采集主循环里,把解析出来的字段拼成 tuple 列表,攒够一批再 save_rows,比逐条 insert 快很多。翻页逻辑按目标站点调整,注意每次翻页后 sleep 一下,别把对方打挂。
5. 本篇常见报错排查
401 Unauthorized:九成是 Key 问题。先确认 config.toml 里 api_key 没有多余空格,再确认请求头是 Bearer 加 Key,中间一个空格。如果 Key 是从控制台复制的,检查有没有把换行符带进去。还不行就去 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 重新生成一个。
404 Not Found:多半是 base_url 拼错了。正确写法是 https://taotoken.net/api,然后路径接 /v1/chat/completions。别把 /api 漏掉,也别重复拼。
pymysql 连接超时:检查 host 是 127.0.0.1 还是 localhost,有些环境两者解析不同。端口默认 3306,改过的话在配置里同步。
字段数量不匹配:INSERT 里 11 个 %s,参数 tuple 就必须 11 个元素。少一个就报 not enough arguments。建议把字段名和参数顺序写在一起对照。
中文乱码:连接串里 charset 必须是 utf8mb4,建表也要 DEFAULT CHARSET=utf8mb4。两个地方都要改,只改一个不够。
数据没落库:executemany 之后必须 conn.commit(),忘了提交数据只在事务里,关连接就没了。这是最常见的坑。
Key 泄露风险:别把 config.toml 提交到 git。在 .gitignore 里加一行 config.toml,仓库里放一份 config.example.toml 做模板。
6. 长期跑采集任务,把通道和编码都收口
如果你只是偶尔跑一次采集,上面这套配置够用了。但如果是要长期跑的定时任务,或者要接多个采集源、多个模型做清洗,建议把通道管理再往上提一层。TaoToken 的 Coding Plan 就是为这种持续调用场景准备的,地址 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,适合把采集、清洗、入库整条链路的 API 调用统一到一个计划里管理。
接入细节和参数说明看文档:https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite 。如果你用的是 Claude Code 这类编码工具做采集脚本开发,Anthropic 兼容配置参考 https://taotoken.net/claude-code-anthropic?utm_source=taotoken_aicg_blog_end&utm_content=claude-code-anthropic&utm_campaign=rewrite 。
最后留一个实用习惯:每次改完 config.toml,先跑一遍 verify.py,三个信号都绿了再跑正式采集。这个动作花不到十秒,能省掉后面半小时的排查。




