
1. 从一张表格说起为什么图片总是插歪做 Python 办公自动化时把本地图片按行写进 Excel 是个高频需求。比如商品清单要配主图、员工档案要放证件照、巡检记录要贴现场照片。很多人第一次用 xlsxwriter 的insert_image代码跑通了打开 xlsx 却发现图片要么盖住文字要么大小不一要么只插进去一张。问题通常不在insert_image本身而在三个地方单元格的行高列宽没有提前设定、图片缩放比例没算、插入锚点选错了。xlsxwriter 的insert_image(row, col, filename)默认把图片左上角对齐到单元格左上角图片按原始像素尺寸渲染而 Excel 默认行高约 20 像素、列宽约 64 像素一张 800×600 的图直接压上去必然溢出。这篇内容聚焦 Python 办公自动化场景用 xlsxwriter 的insert_image把本地图片按行批量写入 Excel并控制单元格尺寸与偏移。你会拿到可复制的安装命令与脚本配置、图片路径与行列映射参数以及运行后打开 xlsx 核对图片数量与位置的验证动作。适合已经会写基础 Python、想把图片批量塞进表格的办公自动化同学。核心检索词先明确xlsxwriter 是一个纯 Python 的 Excel 写入库insert_image是它往工作表指定位置插入图片的方法支持按行列坐标或 A1 样式定位也支持通过image_data传入内存中的图片流。它不能读取已有 xlsx只能新建并写入这一点和 openpyxl 不同选型时要留意。我试过把 200 行商品数据配 200 张缩略图写进一个 sheet第一次跑完图片全叠在 A 列排查后发现是循环里行列变量写反了。下面把完整流程拆开讲每一步都能直接跟做。2. 环境准备与 TaoToken 前置配置2.1 安装 xlsxwriter 与 Pillowxlsxwriter 负责写 ExcelPillow 负责在需要时读取图片尺寸、做等比缩放。两个都装pip install xlsxwriter Pillow验证安装python -c import xlsxwriter; print(xlsxwriter.__version__)能打印出版本号比如 3.2.0就说明装好了。如果你用的是虚拟环境记得先激活再装。2.2 为什么这里要提 TaoToken批量插图脚本本身不依赖大模型但实际办公自动化项目里图片路径、列名映射、异常处理这些逻辑很多人会用 AI 辅助生成和调试。TaoToken 是一个大模型 API 聚合平台提供统一的 Base URL 和 Key兼容 OpenAI 风格的接口也支持 Claude Code、Cline 这类编码工具接入。它的 API 地址是https://taotoken.net/api官网在https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content。如果你打算让 AI 帮你写这个插图脚本或者后续把「读数据库→生成 Excel→插图」整条链路做成 Agent可以先把接入配置准备好。下面给一份可复制的配置片段路径和字段名按实际工具保持一致。2.3 可复制的接入配置片段以常见的 OpenAI 兼容客户端为例配置文件config.json{ base_url: https://taotoken.net/api, api_key: sk-你的Key, model: claude-sonnet-4-20250514, timeout: 60 }如果你用 Claude Code配置通常写在~/.claude/settings.json或项目级.claude/settings.json{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的Key, ANTHROPIC_MODEL: claude-sonnet-4-20250514 } }用 Cline 的 MCP 配置时mcp_settings.json里写{ mcpServers: { taotoken: { command: npx, args: [-y, taotoken/mcp-server], env: { TAOTOKEN_BASE_URL: https://taotoken.net/api, TAOTOKEN_API_KEY: sk-你的Key } } } }Codex 的auth.json则关注这几个字段{ base_url: https://taotoken.net/api, api_key: sk-你的Key, model: claude-sonnet-4-20250514 }三件套记牢Base URL 用https://taotoken.net/apiKey 在控制台生成Model ID 按你实际要用的填。Key 的获取入口在 API Keys 页面接入细节可以查接入文档。这些配置和插图脚本是两条线插图脚本不配也能跑配了是为了后续用 AI 辅助排障更顺。3. 可复制的批量 insert_image 脚本配置3.1 最小可运行版本先看单张图插入理解坐标体系import xlsxwriter book xlsxwriter.Workbook(demo_single.xlsx) sheet book.add_worksheet(demo) sheet.insert_image(D4, images/001.jpg) book.close()D4是 A1 样式等价于insert_image(3, 3, images/001.jpg)行列都从 0 开始。跑完打开 xlsx图片左上角贴在 D4 单元格左上角。3.2 按行批量插入的完整脚本下面这份脚本把images/目录下的图片按文件名排序逐行写入同时控制行高列宽和缩放import os import xlsxwriter from PIL import Image IMG_DIR images OUT_FILE batch_images.xlsx ROW_HEIGHT 90 # 行高单位磅 COL_WIDTH 18 # 列宽单位字符 IMG_MAX_W 120 # 图片最大显示宽度像素 IMG_MAX_H 80 # 图片最大显示高度像素 book xlsxwriter.Workbook(OUT_FILE) sheet book.add_worksheet(图片清单) # 设定列宽和行高 sheet.set_column(A:A, 8) sheet.set_column(B:B, COL_WIDTH) sheet.set_row(0, 24) # 表头 sheet.write(0, 0, 序号) sheet.write(0, 1, 图片) files sorted( f for f in os.listdir(IMG_DIR) if f.lower().endswith((.jpg, .jpeg, .png)) ) for idx, fname in enumerate(files, start1): path os.path.join(IMG_DIR, fname) with Image.open(path) as im: w, h im.size scale min(IMG_MAX_W / w, IMG_MAX_H / h, 1.0) x_scale round(scale, 4) y_scale round(scale, 4) sheet.set_row(idx, ROW_HEIGHT) sheet.write(idx, 0, idx) sheet.insert_image( idx, 1, path, { x_scale: x_scale, y_scale: y_scale, x_offset: 4, y_offset: 4, object_position: 1, } ) book.close() print(f完成共写入 {len(files)} 张图片)关键参数逐个说x_scale/y_scale是缩放比例1.0 表示原始尺寸。上面用min(最大宽/原宽, 最大高/原高, 1.0)算出等比缩放保证图片不超出设定框同时不放大小图。x_offset/y_offset是相对单元格左上角的像素偏移给 4 像素让图片不贴边视觉上更舒服。object_position控制图片随单元格移动和缩放的行为取值 1 表示「移动但不缩放」2 表示「移动并缩放」3 表示「不移动不缩放」。批量清单场景用 1 比较稳。set_row(idx, ROW_HEIGHT)必须每行都设因为 Excel 默认行高装不下缩放后的图。ROW_HEIGHT 单位是磅90 磅约等于 120 像素配合 IMG_MAX_H80 像素留出余量。3.3 用 image_data 插入内存图片如果图片来自数据库 blob 或网络流不想落盘用image_dataimport io import base64 import xlsxwriter from PIL import Image raw base64.b64decode(b64_string) im Image.open(io.BytesIO(raw)) buf io.BytesIO() im.save(buf, formatPNG) buf.seek(0) book xlsxwriter.Workbook(memory_img.xlsx) sheet book.add_worksheet(demo) sheet.set_row(0, 90) sheet.set_column(A:A, 18) sheet.insert_image(0, 0, placeholder.png, { image_data: buf, x_scale: 0.5, y_scale: 0.5, }) book.close()注意image_data传的是 BytesIO 对象文件名参数仍需给一个占位字符串xlsxwriter 用它推断格式实际数据以image_data为准。3.4 行列映射参数对照参数含义常用值row行索引0 起循环变量 idxcol列索引0 起图片列固定为 1x_scale水平缩放0.1–1.0y_scale垂直缩放0.1–1.0x_offset水平偏移像素2–8y_offset垂直偏移像素2–8object_position随单元格行为1image_data内存图片流BytesIO4. 验证请求与成功结果核对脚本跑完会打印「完成共写入 N 张图片」。但打印数字不等于 Excel 里真的对了必须打开 xlsx 核对。第一步确认文件生成。在脚本同目录执行ls -lh batch_images.xlsx能看到文件且大小合理200 张缩略图通常几百 KB 到几 MB。第二步打开 xlsx 数图片。Excel 里点任意一张图按CtrlA会全选所有图片对象状态栏或名称框附近能看到数量。也可以逐个点核对是否每行都有。第三步核对位置。重点看三处图片是否落在 B 列对应行、有没有盖住 A 列序号、行高是否够。如果图片压到了下一行说明 ROW_HEIGHT 偏小或 y_scale 偏大。第四步用 Python 反向校验图片数量。xlsx 本质是 zip图片存在xl/media/下import zipfile with zipfile.ZipFile(batch_images.xlsx) as z: media [n for n in z.namelist() if n.startswith(xl/media/)] print(media 文件数:, len(media))这个数字应该和写入的图片数一致。如果少了多半是某张图路径不存在或格式不被支持xlsxwriter 默认对缺失文件不报错会静默跳过。可以在循环里加os.path.exists(path)判断并打印警告。第五步检查图片是否变形。等比缩放算对了就不会拉伸如果发现某张图明显变扁回去看x_scale和y_scale是否被写成了不同值。5. 本篇常见错误排查5.1 报错 KeyError 或图片不显示最常见的是路径问题。insert_image的 filename 是相对当前工作目录不是相对脚本文件。如果你在别的目录执行脚本images/001.jpg就找不到。解决用绝对路径或os.path.join(os.path.dirname(__file__), images, fname)。xlsxwriter 对不存在的文件不抛异常只是不插入所以「图片数量对不上」往往就是路径错了。加一行判断if not os.path.exists(path): print(f跳过缺失文件: {path}) continue5.2 401 与 local proxy failed如果你在用 AI 辅助生成脚本时遇到401 Unauthorized检查 Key 是否填对、是否过期。TaoToken 的 Key 在控制台 API Keys 页面生成Base URL 必须是https://taotoken.net/api少写/api或写成别的路径都会 401。local proxy failed通常是本地网络配置或客户端代理设置问题检查客户端里 Base URL 有没有被错误改写以及本机是否有拦截流量的软件。这类报错和插图脚本无关属于接入层问题按接入文档逐项核对即可。5.3 reading choices 报错Error reading choices一般出现在调用模型接口解析响应时说明返回结构不符合预期。先确认 Model ID 写对再确认请求体格式。用 curl 直接打一次curl https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer sk-你的Key \ -H Content-Type: application/json \ -d {model:claude-sonnet-4-20250514,messages:[{role:user,content:hi}]}能正常返回 JSON 就说明接入没问题报错在客户端解析层。5.4 OAuth 相关报错Claude Code 接入时如果报 OAuth 错误多半是认证方式冲突。用 API Key 方式时确保ANTHROPIC_API_KEY已设置且没有残留的 OAuth token 干扰。清理旧的凭据缓存后重试。5.5 图片叠在一起如果所有图片都堆在同一个单元格检查循环里insert_image的 row 参数是不是写成了常量。正确写法是sheet.insert_image(idx, 1, path, ...)idx 随循环递增。5.6 行高不生效set_row必须在insert_image之前或之后调用都行但行号要对。如果设了第 1 行行高却往第 2 行插图自然没效果。另外如果同一行被多次set_row以最后一次为准。6. 继续用 AI 提效的接入入口插图脚本跑通后下一步通常是把「读数据源→清洗→生成 Excel→插图→校验」串成自动化流程或者做成定时任务。这个阶段用 AI 辅助写胶水代码、生成异常处理、补日志效率会高不少。需要 Key 就去 API Keys 页面生成接入细节查接入文档。想先验证模型响应是否正常可以用模型对话页面直接发一条消息试。如果打算长期做编码和 Agent 类任务Coding Plan 更适合额度和调用方式按编码场景设计。配置三件套再强调一次Base URL 用https://taotoken.net/apiKey 从控制台拿Model ID 按实际使用的模型填。把这三样填进你用的客户端就能让 AI 帮你继续打磨这套 Excel 插图流程。