
在办公自动化圈子里飞书文档附件批量下载一直是个又痛又刚需的场景。尤其当文档里堆满了PDF、Word、PPT、Excel和视频素材时手动逐个保存太折磨人关键还不一定拿到源文件。我自己用RPA方案踩了无数坑最近把流程重构成2.0终于能稳定批量导出全部常用格式而且都是源文件。这里完整记录一下整套方案的思路、关键细节和实战经验给正在为同样问题头疼的人做个参考。这个方案适合谁简单说凡是经常在飞书里归档文件、做知识库整理、批量处理文档附件的同学都适用。哪怕你不会写代码把思路和步骤看完也能用现成的RPA工具跑起来如果你懂点Python那2.0这套“接口RPA”的思路可以给你省下至少两个星期的弯路。1. 需求分析与方案迭代飞书文档确实能在线预览很多附件但“能预览”和“方便批量下载”完全是两回事。我们团队最早的需求很简单每季度要把飞书上收集的几十份PDF、Word、PPT、Excel归档到NAS上还要保留原始视频文件。一开始我试过手动下载一份文档里可能有十几个附件一个个点进去找下载按钮已经很烦了更麻烦的是部分附件下载后根本不是源文件。1.1 为什么飞书文档附件批量导出这么麻烦很多人以为飞书文档里的附件就是个普通文件链接直接复制地址就能下载实际操作过才知道不是那么回事。飞书把附件当作云媒体文件页面上的每个附件都对应一个file_token你点下载时前端会带着这个file_token去请求服务端服务端校验完登录态和权限后再返回一个临时有效的下载URL。整个过程涉及登录态、鉴权参数和临时签名所以用“查看源代码找链接”这种方式很难拿到稳定地址。更头疼的是飞书网页版没有“下载全部附件”这种按钮。在多级目录、表格、流程图里嵌入的附件更是分散在不同区块手工操作容易遗漏。视频类附件尤其明显在线预览时播放的是转码后的版本如果直接页面下载有时拿到的还是带水印的预览文件而非原始高清素材Office文件也类似预览开放的PDF版可能把原始Word、PPT的内容“缓存”成了另一个文件再下载时文件名一样但内容已经不是可编辑源文件了。1.2 从1.0到2.0我改了什么设计思路最早一版方案是纯RPA用影刀模拟鼠标点击定位页面上的附件元素然后一个个点下载。这套方案在附件少、类型单一的场景下能跑但只要遇到十几份文档或者几十个附件的清单就非常不稳定。一是浏览器下载弹窗会拦截重复请求二是点击坐标判断容易误触三是视频文件经常只是点开了预览窗口没有真正触发下载。2.0版本我换成了“接口驱动为主、RPA兜底”的双通道架构。核心思路是让RPA负责登录态、页面滚动、元素采集等浏览器层的事让Python脚本负责调用飞书接口、解析真实下载地址、批量下载、校验文件完整性。这样既避开了纯点击模拟的脆弱性又能拿到带鉴权的原始下载URL从而保证导出的是源文件。整体稳定性提升非常明显从原来跑一次要盯着大半天变成现在挂上流程就能睡觉。2. 关键技术解构下载链接、源文件与RPA工具选型很多自动化方案失败都是因为只看页面、不看接口。做飞书附件下载之前一定要先理解背后文件存储和鉴权的逻辑否则写出来的脚本就是碰运气。2.1 飞书文档里“附件”到底是什么用通俗的话讲飞书云文档里的附件不是我们理解的那种“挂在网页角落的静态文件”而是一个存在于云端对象存储里的媒体对象。每个附件对象有一个全局唯一的file_token文档页面通过这个token来引用它。你对附件做任何操作比如预览、下载都要拿着这个token去请求飞书网关并且带上你的登录凭证。页面里能看到的附件名称、大小、缩略图其实是从一个media接口返回的元数据映射出来的。如果你抓包会看到类似这样的响应字段file_name、media_type、size、download_url、extra。把握住这一点方案就清晰了——想办法拿到每个附件的file_token再通过接口换取download_url最后用带凭证的HTTP客户端把文件流落盘。这里有三种路径供选择。第一种是纯浏览器Cookie方式登录飞书网页版后把请求头里的Cookie复制出来配合页面接口返回的download_url去下载。这种最简单不需要企业管理员权限但Cookie会过期适合临时用。第二种是自建应用方式用管理员账号创建一个飞书开放平台自建应用开通云文档相关权限通过tenant_access_token调用媒体下载接口。这种方式最稳定也更符合规范适合长期任务。第三种是RPA浏览器内核方式让RPA打开飞书页面利用浏览器已注入的身份去请求下载不需要单独维护Cookie。三条路我都试过2.0方案里默认推荐第二种但为了方便小团队也保留第一种作为降级方案。2.2 为什么我用“影刀RPAPython脚本”混合架构工具选型上我一开始也纠结过。纯PythonPlaywright理论上可以解决所有问题但开发成本高团队里非技术同事根本看不懂后期维护全靠我一人。纯影刀RPA可视化编排虽然上手快但处理JSON、批量重命名、文件头校验这类逻辑又很吃力。后来发现最好的办法是两者结合这也是2.0方案架构上的核心。影刀RPA负责四件事打开目标飞书文档、滚动页面直到附件全部渲染、收集附件对应的DOM节点信息、把页面里的file_token和文件名写入本地JSON文件。Python脚本负责三件事读取JSON后向飞书接口请求真实下载地址、用requests或httpx流式下载、下载后做文件类型校验和重命名。两边通过本地中间文件通信不搞复杂的服务调用出问题也好排查。这套搭配的好处是即便接口规则变了我还能退回RPA的界面点击流程作为兜底而真正要对接新的附件类型时只需要改Python解析逻辑不用动整套UI流程。对于有多个飞书文档需要处理的批量任务这个架构的扩展性也很强。2.3 拿到“源文件”的三种路径与鉴别方法“源文件”这个词来自需求方意思是下载出来的东西应该和上传时完全一致。要保证这一点有三个关键动作。第一个动作是选择正确的请求参数。从页面接口拿download_url时注意避免带previewtrue或transcodetrue之类的参数这些参数会让服务端返回预览版。更稳妥的做法是直接走开放平台的/open-apis/drive/v1/medias/{file_token}/download接口这个接口返回的是原始字节流前提是你的自建应用已经授权相关权限。第二个动作是看响应头。Content-Disposition里的filename字段通常会给一个文件名如果它和页面里展示的附件名不一致比如Word变成了PDF那就说明你拿到的不是源文件。视频也一样如果原文件名是mp4响应里却是m3u8或flv那就要换个请求通道。第三个动作是用文件头做最终校验。我为了防止脚本“假成功”会在下载后读取文件开头的几个字节去判断真实格式PDF文件头是%PDFDOCX和XLSX都是PK开头但内部结构不同MP4文件头里有ftyp。这些都可以用Python的filetype库或struct模块完成校验。只要文件头匹配就算接口参数有偏差也能及时发现并重新下载。3. 实操过程手把手搭建附件批量下载流程下面记录我实际搭建这套2.0方案的完整过程按步骤拆开写方便你照着做。3.1 环境准备权限、目录、工具准备工作分为三块。第一块是飞书侧最好准备一个“云文档管理员”账号登录飞书开放平台创建一个自建应用然后在“权限管理”里开通docx:document:readonly、drive:drive:readonly或者media:media:download这类与云文档读取相关的权限具体权限名称以你当前控制台实际可选项为准。如果你们企业不允许自建应用那就用第一种Cookie方案至少能跑通临时任务。第二块是本机环境安装Python 3.9以上版本建议用3.11或3.12太老的版本对httpx等库支持不好。影刀RPA建议装稳定版社区版即可。下载目录建议用纯英文路径比如D:\feishu_output避免中文路径在某些环节出现编码问题。这个坑我踩过后面还会细说。第三块是依赖库在Python环境里安装requests、httpx、filetype、pandas可选每个都用pip install装好。如果你要走开放平台接口还需要安装python-jose或直接用requests自己做JWT签名换取tenant_access_token但很多飞书SDK已经封装了可以直接用官方lark-oapi库省去不少签名细节。3.2 第一步抓取附件列表与元数据打开目标飞书文档后先不要急着写脚本用肉眼判断一下附件区域结构。飞书文档里的附件通常出现在文档正文的“附件”块点击附件会弹出预览面板面板里有文件名、大小、下载按钮。这些信息都在页面的DOM属性里关键是找到包含file_token的节点。我用影刀做这一步时会启用“网页自动化”的“获取相似元素”功能选择页面上的一个附件元素让它自动匹配同类型的其他元素。匹配到的结果会带出节点文本和属性列表比如>[ {name: 2024年度预算表.xlsx, file_token: doxcn1234567890, type: excel}, {name: 季度汇报.pptx, file_token: doxcnabcdefg, type: ppt}, {name: 宣传片原始素材.mp4, file_token: doxcnhijklmn, type: video} ]注意页面里的文件名可能被截断最好去解析元素的title属性或aria-label否则后面重命名时容易出错。3.3 第二步解析真实下载地址含示例代码拿到附件元数据后接下来要看用哪种方式下载。如果走开放平台接口直接调用飞书官方SDK就好逻辑比较清晰。如果走页面Cookie方式核心是模拟浏览器请求在请求头里带上登录后的Cookie和必要的Referer、User-Agent。下面是我验证可用的一套Python示例注释都写在关键位置import datetime import requests COOKIE 你的飞书登录Cookie DOCUMENT_URL https://yourcompany.feishu.cn/docx/xxxxx def download_attachment(file_token, file_name, output_dir): # 这里用飞书开放接口示例如果走页面接口url换成抓包得到的接口地址 url fhttps://open.feishu.cn/open-apis/drive/v1/medias/{file_token}/download headers { Authorization: Bearer你的tenant_access_token, User-Agent: Mozilla/5.0, Referer: DOCUMENT_URL, } # 注意如果走页面Cookie方案就把Authorization换成Cookie头 # headers[Cookie] COOKIE resp requests.get(url, headersheaders, streamTrue, timeout30) if resp.status_code 200: # 从响应头里提取原始文件名兜底用传入的file_name content_disposition resp.headers.get(Content-Disposition, ) # 简单解析filename* if filename* in content_disposition: real_name content_disposition.split(filename*)[1].strip().strip() elif filename in content_disposition: real_name content_disposition.split(filename)[1].strip() else: real_name file_name # 过滤非法字符 real_name .join(c for c in real_name if c not in /\\:*?|) with open(f{output_dir}/{real_name}, wb) as f: for chunk in resp.iter_content(chunk_size1024 * 256): if chunk: f.write(chunk) print(f{datetime.datetime.now()} 下载完成: {real_name}) else: print(f下载失败: {resp.status_code} {file_token})这里有一点要提醒Cookie方案虽然简单但同一个Cookie并发请求太多容易被风控所以我建议把下载请求串行化每个文件之间至少间隔1秒。如果你文件数量特别多可以在影刀流程里加入随机延时模拟真人操作。3.4 第三步批量下载、重命名与断点处理批量下载不能直接循环请求就完事还要考虑失败重试和重命名冲突。我通常会在下载脚本外面再包一层循环读取JSON文件逐条下载失败自动重试最多3次每次重试前等待2秒。如果重试还是失败就把这个附件信息单独写到failed.json方便后续重新跑。重命名规则我最终定为“文档名_附件名”的格式比如飞书文档附件下载方案2.0_使用手册.pdf。这样在归档时一眼能看出文件来源于哪篇文档避免多个文档里出现同名附件而互相覆盖。用代码实现时只要在保存前检查输出目录里是否已有同名字文件如果有就追加.1、.2这样的序号。断点续传这块值得单独说。小文件无所谓但视频动辄几百MB甚至几个GB中途断流很常见。飞书返回的download_url是否支持HTTP Range取决于服务端配置我测试下来有些临时链接支持有些不支持。所以在下载大文件时我的做法是先用requests.get获取总大小再循环尝试如果中途断开就根据本地已下载的文件大小重新发起带Range头的请求。如果服务端不支持Range就干脆在下一次重试时从头开始并写一个本地标记避免死循环。3.5 第四步完整性校验与结果清单下载不等于成功校验通过才算完。我习惯在下载完成后立即做三件事。第一件是看文件大小如果接口返回的size字段和本地文件大小对不上直接判失败。第二件是用filetype库读取文件头确认扩展名和真实格式一致。第三件是尝试打开文件做轻量验证PDF可以读取页数Word/Excel可以用openpyxl或docx库打开视频可以用opencv读取第一帧。校验通过后我会把本次任务的结果汇总成CSV字段包括文档链接、附件名、文件类型、文件大小、下载耗时、校验结果、存储路径。跑完一批后这个CSV就是给业务方看的交付清单后续排查也方便。如果某类文件需要单独交付我还可以用这个CSV自动把文件复制到对应分类目录。4. 实战中踩过的坑与排查经验再完美的方案也是从坑里爬出来的。下面这五个问题都是我在实际运行中遇到频率最高的特别是第一个和第五个几乎每个上来做飞书批量下载的人都会遇到。4.1 下载链接失效返回登录跳转页异常表现是下载下来的文件只有几KB用记事本打开发现是一段HTML内容提示“正在登录”或“跳转中”。原因几乎都是Cookie过期或请求头里缺失必要参数。飞书对下载接口的鉴权卡得很严登录态一旦失效所有带Cookie的请求都会被重定向到统一登录页。解决办法很简单不要用长期不变的Cookie去跑大任务尽量在任务开始前从浏览器当前会话里提取Cookie跑之前先做一次轻量探测请求确认返回的是文件流而不是HTML。如果你用的是自建应用tenant_access_token则不存在这个问题token可以定时刷新。所以在2.0方案里我最推荐的方式还是开放平台接口Cookie只是应急。4.2 页面虚拟滚动导致附件列表抓不全飞书文档长的时候页面会开启虚拟滚动也就是只渲染可视区域内的DOM节点。如果RPA一上来就去抓附件往往只能抓到前几个后面的附件节点根本没有加载出来。很多人第一次跑脚本只下载了半个文档就是因为这个原因。解决思路是在抓取前先“滚到底”。用影刀的滚动页面功能每次向下滚动一屏等待1到2秒直到检测不到页面高度变化为止然后回到顶部再从头抓一次。这样能让所有附件区块全部渲染。如果你用浏览器Console直接抓也可以多次执行window.scrollBy(0, window.innerHeight)把所有节点信息累计后写入数组。4.3 大文件下载中断与限流视频文件下载到一半突然报错是另一个高频问题通常是因为临时下载URL有有效期或者网络代理中途断开。更隐蔽的是飞书对高频请求有限流策略连续下载十几个大文件后接口响应变慢甚至返回429或503。我的做法是给每个下载请求设置合理的超时和重试机制同时限制并发数。小文件可以3个并发大文件一定要串行。另外把大文件任务安排到非工作时间跑比如凌晨2点成功率会高很多。如果公司网络比较慢还可以把下载目录挂载到NAS上让脚本直接在局域网内写文件。4.4 同名附件互相覆盖这个问题很讨厌因为文档A和文档B里可能都有产品说明书.pdf。如果只按文件名保存后下载的会把先下载的覆盖掉。我一开始没注意丢掉过两份重要资料。后来在保存路径里加入了文档标识或file_token比如D:\feishu_output\{文档_token}\{附件名}.{ext}。虽然目录嵌套深了一点但绝不会冲突。如果你希望保持扁平目录可以在文件重名时追加时间戳或短哈希。从结果来看按文档划分子目录也方便后续按篇目回顾推荐这样做。4.5 Office文件下载后打不开根本不是原文件这个坑最隐蔽。页面上明明显示Word文档下载下来文件名也是.docx但用Office打开却提示文件损坏。用filetype查文件头才发现里面其实是一个PDF或HTML。原因是飞书对某些在线预览过的Office文件在页面点击下载时会重新合成一份预览版文件名保留原样但内容已经不是原始可编辑文件。遇到这种情况就不能从页面下载链接走了必须回到飞书开放平台的原始媒体接口。如果接口返回的file_type和页面展示的类型不一致直接重新请求medias/download接口。另外下载完成后一定要用文件头校验这一步能帮你拦截99%的“假Office文件”。尤其是从网页上看到“预览版”三个字时要高度警惕。5. 把方案沉淀成可复用工具问题排查完之后这套流程已经能稳定跑通了。但要想让它长期服务于团队还需要把这套流程从“我手动跑”升级成“任何人能跑”。5.1 命令行封装与参数设计我最终把下载核心逻辑打包成了一个Python脚本提供四个参数文档URL、请求凭证Cookie或token、输出目录、是否启用校验。这样除了影刀流程在RPA客户端里跑也可以直接在命令行里跑python download_feishu_attachments.py \ --url https://yourcompany.feishu.cn/docx/xxxxx \ --token tenant_access_token \ --output D:/feishu_output \ --verify脚本内部会先根据URL获取文档中所有附件的元数据再逐个下载。我把和飞书交互的部分抽成了独立模块以后如果接口版本升级只需要替换一个函数即可。为了保证可复现我还在项目里放了一份requirements.txt里面固定版本号避免同事跑的时候因为依赖不一致翻车。5.2 增量下载与定时调度一旦跑通了手动下载就会产生新需求每天自动把飞书文档里新增的附件同步到本地。我用一个简单的downloaded_tokens.json保存已经成功下载的file_token每次任务开始前先加载这个文件下载完成后把新token追加进去。这样重复跑任务时已经下载过的附件会被跳过耗时大大缩短。定时调度方面Windows用户可以用“任务计划程序”把批处理文件设置成每天凌晨执行Linux或服务器用户直接在crontab里加一行即可。这里要特别注意如果脚本跑的时候需要登录飞书必须确保机器上的Cookie或token有足够长的有效期最好结合定时刷新token的逻辑。我目前的做法是脚本每次运行前先调用一次开放平台的token接口拿到新的tenant_access_token再去下载。5.3 团队使用与合规边界方案最终能给团队用还需要考虑权限边界。不要让所有人的Cookie都塞进同一套脚本而是尽量走企业自建应用通过飞书的权限体系控制谁能下载哪些文档。下载目录也应该按团队或按文档域隔离防止越权访问。合规方面要特别注意附件可能会涉及个人信息或商业敏感内容批量下载后要设好访问权限不要放到公共网盘上更不要提交到公开代码仓库。另外飞书开放平台对接口调用频率有明确限制如果团队人多建议由管理员统一维护一个下载服务其他人不要直接调接口。用RPA时也要避免高频点击触发风控最好设置随机间隔。毕竟做自动化的目的不是和平台对抗而是把重复劳动减轻保持一个安全、合理的使用节奏。最后再分享一点个人经验从1.0到2.0这版方案我最后悔的是没有在一开始就先抓一遍接口导致1.0版本绕了远路。现在这套“影刀RPAPython脚本开放平台接口”的组合已经在我们团队稳定跑了几个月累计处理了上千个附件最重的一个视频文件有3个多GB也没有出过错。如果你也在做飞书办公自动化我会建议你第一步先打开浏览器开发者工具看清楚附件下载背后的请求长什么样再去想RPA怎么写。下载完成后也一定要验证文件头确保你拿到的真的是源文件而不是预览文件的替身。