news 2026/10/2 11:41:27

Python + Playwright 实现网页表单批量自动化填写实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python + Playwright 实现网页表单批量自动化填写实战

提到程序自动化填写网页表单数据,可能很多人第一反应是爬虫、抢票脚本这类偏“灰色”的用途。但实际上,日常工作中最常见的需求反而是非常朴素的重复录入:每天从Excel里整理一批新信息,打开后台系统,一条条复制粘贴到网页表单里,点提交,再重复下一单。量大一点、字段多一点的时候,不光手累,还特别容易串行,把“手机号”填到“备注”里,点完提交才发现,又要回头改。

我前阵子就接过这么一档子事:内部管理系统月底要集中登记一批设备资产信息,小一千条记录,全靠人肉填的话,一天都未必填得完,眼睛还得一直盯着屏幕。所以我就用 Python 配合 Playwright 写了一个批量表单自动化填写的小脚本,把读取数据、定位元素、填写表单、提交校验、记录日志这一整套流程串了起来。实测下来,原来需要大半天的工作量,压缩到几分钟跑完,中间偶尔有填错的记录也能通过日志快速定位修掉。

这篇文章就把这套方案的完整思路和实操细节展开讲一遍,包括方案选型、Playwright 环境搭建、各种表单控件的处理方式、批量数据的读取组织、常见坑的排查,以及运行稳定性的设计。适合有 Python 基础、想把手头重复的网页录入工作自动化但不知道从哪入手的同学参考。当然,所有自动化操作都要用在自己有权限、被允许自动化的系统上,不要去做任何违反平台规则的事。

1. 项目整体设计与方案选型

1.1 这个项目到底在解决什么问题

一句话说清楚这个项目的目标:用程序代替人工,把外部数据源里的结构化数据自动填写到网页表单中,并完成提交确认。

看起来很简单,但拆开之后会涉及几个关键环节。首先是数据怎么来,通常是从 Excel、CSV、JSON 这类文件里读取,也可能是调用接口拉取,数据质量参差不齐,字段可能为空、格式可能不对。其次是表单怎么识别,不同系统的前端实现差异很大,有的是老式同步页面,有的是前后端分离的 SPA(单页应用),还有的表单控件是第三方组件封装的,定位元素的难度完全不同。再次是表单怎么填,普通的输入框、下拉框、单选按钮、日期选择器、文件上传,每种控件的操作方式都不一样。最后是提交后怎么确认,页面是跳转还是局部刷新,成功提示是什么,失败提示是什么,都需要脚本能够准确判断,否则跑了一遍也不知道到底成没成。

这个项目关注的核心不是某一个孤立环节,而是“数据 -> 页面 -> 表单 -> 提交 -> 校验”的完整链路。把它打通之后,受益的不仅是录入效率,更重要的是准确率。人工填表在几千条数据的场景下,出错几乎是必然的;程序填表只要定位逻辑写对、数据校验做好,每一条都能按同样的标准执行。

所以,在规划这个项目的时候,我从一开始就没打算写“一次性临时脚本”,而是按一个小工具的标准来做:数据文件可替换,浏览器行为可观察,运行日志可追踪,失败记录可重试。后面所有方案选型都是围绕这几个目标展开的。

1.2 技术选型:为什么是 Playwright 而不是 Selenium

提到网页自动化,很多人的第一反应是 Selenium。它资历老、资料多、网上教程一抓一大把,很多测试团队的老框架也都是基于 Selenium 的。但我这次没有选它,原因其实挺实际的。

一是驱动管理。Selenium 需要单独下载对应浏览器版本的 driver,比如 Chrome 升级了,driver 版本不匹配脚本就直接跑不了。Playwright 把这一步封装掉了,安装的时候会连带把浏览器内核一起装好,版本配套问题基本不用操心。二是等待机制。Selenium 默认的隐式等待、显式等待写起来比较啰嗦,而 Playwright 的定位操作自带自动等待,元素没有出现就等,超时再报错,脚本写起来干净很多。三是 API 设计。Playwright 的 API 风格更现代,支持链式调用,对 iframe、新标签页、弹窗这些场景的处理也更顺手。

我把两者做个小对比,方便你自己判断:

对比项SeleniumPlaywright
浏览器驱动管理手动下载并匹配版本安装时自动下载配套浏览器
自动等待需要显式编写等待逻辑定位操作内置自动等待
元素定位 APIfind_element 系列locator 链式定位
iframe / 多标签处理需要切换 contextframe_locator / 多 page 原生支持
调试手段依赖外部工具自带 trace、截图、录屏

当然,Playwright 也不是没有缺点。比如它较新的 API 设计需要一点学习成本,社区的中文资料比 Selenium 少一些,不过这两年文档和教程已经非常丰富了。还有一个实际问题是,如果目标系统只支持非常老的浏览器,Playwright 的兼容性可能不如 Selenium 那么广。但对我手里这种内部管理系统场景来说,Playwright 的方案完全够用,开发效率还高出一截,所以最终确定用它。

2. 环境准备与最小可运行示例

2.1 环境安装:Python + Playwright

这个项目我用的是 Python 环境,版本 3.10 以上就行。整体安装分两步。

第一步,装 Python 包:

pip install playwright

第二步,安装浏览器内核。这一步别漏,只装包不装内核是跑不起来的:

playwright install chromium

这里解释一下,Playwright 支持 Chromium、Firefox、WebKit 三种内核,日常做 Web 自动化选 Chromium 就够了。它下载的是 Playwright 自己管理的浏览器版本,和系统里装的 Chrome 互不影响,这样可以保证驱动和内核始终是配套的。

装完之后,可以先用一小段代码验证一下环境是否正常:

from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(headless=False) page = browser.new_page() page.goto("https://example.com") print(page.title()) browser.close()

运行这个脚本,如果能看到浏览器窗口打开并打印出页面标题,说明环境已经就绪了。

一个小提醒:在公司内网环境,有时默认源下载 Chromium 会比较慢或失败,可以设置 Playwright 的下载源镜像,或者通过环境变量指定浏览器下载路径。这个不是标准配置,但实践中经常遇到,卡住的话优先检查这一步。

2.2 第一个自动化脚本:打开页面并填入数据

环境确认之后,我建议先别急着写批量逻辑,先拿一个最简单的页面跑通“打开表单 -> 填写字段 -> 提交”的完整流程。

举个例子,假设目标页面上有一个登录表单,包含用户名和密码两个输入框,一个提交按钮。Playwright 的代码是这样的:

from playwright.sync_api import sync_playwright with sync_playwright() as p: browser = p.chromium.launch(headless=False) page = browser.new_page() page.goto("http://your-system.com/login") # 填写用户名 page.locator("#username").fill("admin") # 填写密码 page.locator("#password").fill("123456") # 点击登录按钮 page.locator("button[type='submit']").click() # 等待跳转或出现某个登录后的元素 page.wait_for_url("**/index") print("登录成功") browser.close()

这段代码看起来简单,但里面藏着一个关键点:fill 和 click 这两个操作都带有自动等待能力。fill 会等元素出现在 DOM 里并且可交互,click 会等元素处于稳定可点击状态,不需要像 Selenium 那样在每个操作前手动加 WebDriverWait。

我在实际中见过很多初学者在这时候犯一个错误:一上来就用 sleep 等待页面加载。不是说 sleep 完全不能用,而是它固定等多少秒就等多少秒,网络慢的时候等不够,快的时候又浪费大量无用时间。项目里等元素的核心手段应该是 Playwright 自带的动作等待,sleep 最多在极少数“实在没有合适等待条件”的场景里兜底。

2.3 等待机制:为什么不能无脑 sleep

展开说下等待机制。很多人有固化思维:“写完点击之后,页面要跳转,那就先等两秒再操作下一页。”这个思路的问题在于:页面加载速度受网络、服务器状态、页面复杂度影响很大,固定等 2 秒只是赌它恰好 2 秒内完成。

Playwright 的推荐做法是等待一个“条件”。这个条件可以是元素出现、URL 变化、接口响应,也可以是某个元素消失。比如上面登录的例子,点击提交按钮之后,页面可能跳转到/index,那就用wait_for_url等 URL 变成目标地址;如果跳转后某个侧边栏菜单需要加载出来,那就等这个菜单元素可见。

我自己的实践习惯是:

  • 表单提交后,优先等一个结果类元素出现,比如“提交成功”的提示文本、列表中新增的记录。
  • 如果页面会跳转,用wait_for_url。
  • 如果上述都没有稳定的标记,再考虑用page.wait_for_timeout作为兜底,但会避免在核心流程里大量依赖它。

这套规则在后面的批量脚本里非常重要,因为批量场景下如果每一步都靠 sleep 等,累计时间会非常可观,而且稳定性极差。自动等待看着只是省了几行代码,实际上是把脚本从“靠运气跑”变成了“按条件跑”。

3. 核心细节:元素定位与常见表单控件处理

3.1 元素定位:让脚本“看得见”页面

表单填写的核心操作就是定位元素,然后往里面填数据。Playwright 的定位器用locator方法,支持 CSS 选择器、文本、层级关系等多种方式,比 Selenium 的 find_element 灵活不少。

先看几种最常用的定位思路:

# 通过 id 定位 page.locator("#username") # 通过属性定位 page.locator("input[name='email']") page.locator("input[placeholder='请输入手机号']") # 通过文本定位按钮 page.locator("button:has-text('提交')") # 通过标签关联的文本定位输入框 page.get_by_label("手机号") # 链式定位,先找到某个表单容器,再找里面的输入框 page.locator("#main-form").locator("input")

在我个人经验里,一个非常实用的原则是:优先选择不易变化的定位方式。有些前端系统里,输入框的 id 是动态生成的,每次打开页面都可能变化,这种时候用name或者placeholder往往更稳定。而像“提交”“保存”这种按钮,用文本定位通常比用深层 CSS 路径直观得多,也更不容易被前端改成别的选择器后失效。

还有一个隐藏的坑:页面上可能同时存在多个匹配相同选择器的元素。比如有两个表单,里面都有input[name='name'],直接定位就会报错或者选中第一个。这时候需要收紧范围,把locator限定到正确的表单内部。Playwright 还提供了.first、.last、.nth(index)来处理多元素情况,但我的建议是尽量通过更精确的定位器来避免这种问题,而不是依赖“取第一个”。

3.2 不同表单控件的填写方式

不同的表单控件,在 Playwright 里的操作方法差异比较大。我整理了常见的几种场景:

普通文本框

page.locator("#title").fill("测试标题")

多行文本域和输入框一样用 fill,只是内容里可能包含换行符:

page.locator("#description").fill("第一行\n第二行")

下拉选择框原生 select 用 select_option 方法,可以根据 value、label 或 index 选择:

page.locator("#category").select_option(value="asset") page.locator("#category").select_option(label="固定资产") page.locator("#category").select_option(index=1)

需要注意,select_option只对原生<select>有效。很多现代前端框架用了自定义下拉组件,比如点击一个 div 弹出列表然后选中的那种,这时候select_option会直接报错。我遇到这种情况时,常规做法是先点击下拉框的当前展示区域,再在弹出的列表中定位要选的项进行点击,本质上是模拟人的操作。

单选框和复选框

# 选中 page.locator("#agreement").check() # 取消选中 page.locator("#agreement").uncheck()

用check的好处是,如果元素已经处于选中状态,它会跳过操作,不会报错;如果用 click 硬点,可能会导致状态反转,反而把已选中的给点掉了。

日期输入框HTML 的日期输入框,可以直接用 fill 写入2025-03-15格式的文本:

page.locator("#purchaseDate").fill("2025-03-15")

但如果是日期组件是自定义的,比如常见的时间选择器插件,直接 fill 往往没用。这种我一般是点击日期框后,直接用键盘输入日期,或者逐一切换年、月、日然后点击具体日期。处理方式视组件而定。

文件上传

Playwright 的set_input_files可以直接设置上传文件的路径,不需要真的操作系统文件选择对话框:

page.locator("input[type='file']").set_input_files("/path/to/file.png")

多个文件就传一个路径列表。这个方法也可以用来覆盖已选择的文件,比如上传错了需要换文件。

对比来看,绝大多数表单控件都有对应的自动化操作方式,关键是先判断控件是原生 HTML 还是自定义组件。判断方法很简单:在浏览器开发者工具里看这个元素的标签。如果是input、select、textarea,原生方法基本都能处理;如果标签是div或有很重封装的组件类名,就得走“模拟点击”路线。

3.3 数据来源:从 Excel / JSON 批量读取

脚本本身只是“手”,数据源才是“脑”。批量填写的第一个准备工作,就是设计好数据从哪读、怎么读。

我这次的项目里,数据是存在 Excel 里的,字段有设备编号、设备名称、类别、所属部门、采购日期、备注、照片路径。用openpyxl读取比较直接:

from openpyxl import load_workbook wb = load_workbook("设备登记清单.xlsx") ws = wb.active rows = [] for row in ws.iter_rows(min_row=2, values_only=True): code, name, category, department, date, remark, photo = row rows.append({ "code": code, "name": name, "category": category, "department": department, "date": date.strftime("%Y-%m-%d") if date else "", "remark": remark, "photo": photo, })

这里有一个很关键的处理:Excel 里的日期单元格读出来是datetime对象,直接往页面上的日期输入框填会出现格式问题,所以需要先格式化成字符串。另一个常见问题是 Excel 里某些列可能有空值,或者单元格前后带空格,这些都要在读数据的时候做好清洗,不然填到表单里容易校验失败。

如果数据是 JSON 格式,读取更简单:

import json with open("data.json", "r", encoding="utf-8") as f: rows = json.load(f)

无论数据源是什么,我建议在真正开始填表单前,先打印或保存一条汇总日志,比如“本次共读取到 962 条数据,其中采购日期为空的数据有 12 条”。这一步能提前拦截很多数据问题,避免脚本跑到一半突然因为某条脏数据抛异常。

4. 实操过程:一个完整的批量填写示例

4.1 场景设计:内部设备信息登记

光讲语法和原理比较零散,我把整个项目串成一个完整例子。假设现在有一个内部设备管理系统,页面上有一个设备信息登记表单,包含以下字段:

  • 设备编号(文本框)
  • 设备名称(文本框)
  • 设备类别(下拉选择框,可选:电脑、打印机、网络设备、办公家具)
  • 所属部门(自定义下拉组件)
  • 采购日期(日期输入框)
  • 备注(多行文本域)
  • 设备照片(文件上传)

所有数据来自一个 Excel 文件,大约有 900 多条记录。我的目标是把这 900 多条全部自动填完,每条填写完会自动提交,然后进入下一条。

这个场景非常典型,字段种类覆盖了基本的表单控件:文本框、下拉框、自定义组件、日期、文件上传。跑通它之后,换到别的系统上改改定位器就能用。

4.2 脚本完整实现

脚本的整体逻辑是:读取数据 -> 打开登记页面 -> 按字段逐项填写 -> 提交表单 -> 等待提交成功标记 -> 回到列表页/或继续下一条 -> 记录日志。

完整脚本如下(已脱敏):

import time from pathlib import Path from openpyxl import load_workbook from playwright.sync_api import sync_playwright BASE_URL = "http://your-system.com" EXCEL_PATH = "设备登记清单.xlsx" PHOTO_DIR = Path("photos") def read_excel(path): wb = load_workbook(path) ws = wb.active rows = [] for row in ws.iter_rows(min_row=2, values_only=True): code, name, category, department, date, remark, photo = row rows.append({ "code": str(code).strip() if code else "", "name": str(name).strip() if name else "", "category": str(category).strip() if category else "", "department": str(department).strip() if department else "", "date": date.strftime("%Y-%m-%d") if date else "", "remark": str(remark).strip() if remark else "", "photo": str(photo).strip() if photo else "", }) return rows def fill_form(page, item): page.locator("#deviceCode").fill(item["code"]) page.locator("#deviceName").fill(item["name"]) page.locator("#deviceCategory").select_option(label=item["category"]) # 自定义下拉组件:点击后选择对应项 page.locator("#department-select").click() page.locator(".department-option:has-text('" + item["department"] + "')").click() # 日期输入 if item["date"]: page.locator("#purchaseDate").fill(item["date"]) # 备注 page.locator("#remark").fill(item["remark"]) # 文件上传 photo_path = PHOTO_DIR / item["photo"] if photo_path.exists(): page.locator("input[type='file']").set_input_files(str(photo_path)) else: print(f"[警告] 照片文件不存在: {photo_path}") def main(): rows = read_excel(EXCEL_PATH) total = len(rows) print(f"共读取 {total} 条数据") with sync_playwright() as p: browser = p.chromium.launch(headless=False) page = browser.new_page() page.goto(BASE_URL + "/login") # 这里略去登录处理,登录成功后保持会话 page.goto(BASE_URL + "/device/add") for idx, item in enumerate(rows, start=1): try: fill_form(page, item) page.locator("button:has-text('提交')").click() # 等待成功提示出现 page.locator(".success-tip").wait_for(timeout=8000) print(f"[成功] {idx}/{total} {item['code']}") except Exception as e: print(f"[失败] {idx}/{total} {item['code']}: {e}") page.screenshot(path=f"fail_{idx}.png") # 出现失败时暂停,方便人工排查 input("按回车继续处理下一条数据...") # 回到新增页面,清理表单状态 page.goto(BASE_URL + "/device/add") time.sleep(0.5) browser.close() if __name__ == "__main__": main()

这个脚本里的几个设计点都值得说明。

第一,fill_form函数把单条数据的填写逻辑独立出来,后续如果要适配不同的表单页面,只需要改这一个函数里的定位器即可。第二,每条记录都进入 try-except 包裹,失败时截图并打印错误信息,不会因为一条脏数据让整个程序崩溃。第三,失败后使用input("按回车继续")暂停,这是在有人值守的情况下最直接的人工介入方式,我可以趁机看看页面到底出了什么问题,手动处理完再继续。

有一个细节需要注意:每次提交后,表单可能还会保留上一次填写的数据,直接开始填下一条会串数据。所以脚本在每条记录处理完之后主动跳转回新增页面,强制刷新出一个干净的表单。这也是一种很实用的“状态隔离”技巧。

4.3 运行结果与日志优化

跑通第一版之后,我并没有直接用终端 print 去盯结果,而是加了一层简单的日志输出,把每次填写的情况写入文件,方便跑完全程后拉出来复盘。

一个比较简单的做法是用 logging 模块:

import logging logging.basicConfig( filename="run.log", level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s" )

然后把 print 都换成 logging。跑完之后,用 grep 统计成功和失败的数量:

grep "成功" run.log | wc -l grep "失败" run.log | wc -l

如果失败超过 0 条,就把失败的行号、设备编号和报错信息提取出来,针对性地修数据或修定位器,再单独补跑失败的部分。

日志里面还应记录一条关键信息:每一条数据的原始内容和程序实际填进去的字段值。比如填写完某条数据后,把 item 字典的完整内容打一条日志,万一有字段数据本身不对,回头对照日志就能确认是数据问题还是脚本问题。

5. 常见问题与排查技巧实录

5.1 元素找不到 / 定位失败

做网页自动化,遇到最多的报错基本就是定位不到元素。Playwright 的报错信息一般会明确指出是“等待超时”还是“找到多个元素”,但光看报错还不够,必须学会自己排查。

我总结了一套排查顺序:

  1. 等没等够:元素是否出现在 DOM 里?动态渲染的页面需要点击某个 tab 才出现对应表单,或者接口返回慢导致列表没出来。这时候不要扩大超时时间,应该先确认触发条件是否满足。
  2. 选没选对:打开浏览器开发者工具,手动复制实际页面的 HTML 结构,看看 id、name、类名是否和代码里写的一致。有时候后天改版过,定位器全部失效,就是这种原因。
  3. 是不是同一个元素:页面上可能有多个相同 id 或相同文案的元素,尤其常见于表格和弹窗并存的页面。Playwright 会提示 strict mode violation,也就是严格模式冲突。解决办法是收紧定位范围,比如先限定在弹窗内。
  4. 是不是在 iframe 里:如果目标元素被嵌在 iframe 中,主文档里直接定位是永远找不到的。这种要先用 frame_locator 切进 iframe 再定位。

5.2 iframe、弹窗与新标签页

iframe 处理是这个项目里绕不开的话题。不少后台系统的表单其实都嵌在 iframe 里,或者附件上传区域是独立 iframe。Playwright 的使用方式是:

frame = page.frame_locator("iframe[src='/form']") frame.locator("#username").fill("test")

注意,这里frame_locator返回的对象可以直接继续调用 locator,所有后续操作都限定在这个 iframe 内部,不需要像 Selenium 那样来回 switch。

弹窗方面,常见的 alert、confirm 框用 dialog 事件处理:

page.on("dialog", lambda dialog: dialog.accept())

如果系统是点击按钮后新开标签页,Playwright 的expect_page可以捕获这个新页面:

from playwright.sync_api import expect with context.expect_page() as new_page_info: page.locator("button[target='_blank']").click() new_page = new_page_info.value new_page.wait_for_load_state()

多标签页的场景下,每个page对象是独立的,操作哪个页面的元素就在哪个 page 上定位,千万不要跨 page 混用选择器。

5.3 验证码与登录态保持

网页表单自动化绕不开的一个话题是登录和验证。我的原则很明确:不要尝试去破解或绕过任何验证机制,这既不合规也不稳定,还可能给你自己和目标系统带来麻烦。

如果目标系统有验证码,我的处理方式是分两步。

第一步,尽量复用登录态。通过 Playwright 手动登录一次后,把浏览器的存储状态保存下来,后续脚本直接加载,避免每次运行都重新登录:

# 手动登录后,保存状态 context.storage_state(path="state.json") # 下次直接加载 context = browser.new_context(storage_state="state.json")

很多内部系统登录后会话可以维持几天,这个技巧能省掉大量重复登录的时间。

第二步,如果系统在操作过程中出现了人机验证页面,比如那种滑块或点选验证,脚本识别到特征后主动暂停,等我人工处理完再继续。这相当于在自动流程中设置了一个“人工介入闸口”。做法上可以监控特定元素出现,一旦出现就触发输入等待:

if page.locator("#captcha-panel").count() > 0: input("检测到人工验证,请手动处理,处理完成后按回车继续...")

这种“自动化为主、人工兜底”的思路,在真实业务场景里往往是最靠谱的方案,既保证了效率,又不越界。

5.4 稳定性设计:超时、重试与断点续跑

批量跑几百上千条数据时,稳定性比功能本身更重要。脚本挂了不可怕,可怕的是跑到一半挂了,你不知道从哪条继续,跑重了还会产生重复数据。

我的经验是几个维度同时做。

超时控制。所有等待操作都要设置超时时间。Playwright 的wait_for默认是 30 秒,批量场景里我会压到 8-10 秒,因为正常情况下提交结果几秒内就该出来,等太久说明大概率出问题了。

失败重试与暂停策略。单条数据失败,先重试一次,如果还是失败就跳过或暂停。重试逻辑要小心,如果失败原因是重复提交、表单校验不通过,重试一百次也没用,反而可能造成脏数据。我一般在重试前判断失败类型,只有超时类错误才值得重试。

断点续跑。简单实现方式是在日志里记录当前处理到的行号,或者单独维护一个“已处理列表”,重启脚本时跳过这些行。这样即使中途浏览器崩溃,重新跑一遍也只会补充处理未完成的部分。

在我的项目里,最稳定的一版方案是这样:每条数据处理前先检查“已处理记录集合”,处理成功才写入集合;重启后自动跳过集合里的行;单条失败先重试一次;重试仍失败则截图并暂停等待人工处理。这套组合下来,近千条数据的投放过程基本做到了无人值守。


最后再分享一点个人感受。表单自动化的门槛其实不高,Python 基础加一点 Playwright 的 API 知识就能上手,难点从来不是“能不能填”,而是“填得稳不稳、准不准”。我在这个项目里踩过的坑,大部分都集中在等待条件、元素定位和数据清洗上,这三块做好,整个脚本的骨架就立住了。另外,这种自动化工具做出来之后,也别忘了守住边界——只在你有权限、被允许自动化的系统和场景下使用,别拿它去刷接口、跑数据、突破平台规则。工具本身是中性的,用在哪、怎么用,才是决定它价值的关键。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 11:40:52

Paperclip协议:用文件系统重构AI Agent状态管理

1. “Paperclip”不是回形针&#xff1a;它正在重构AI Agent的工程范式最近在几个技术社区里频繁刷到“paperclip”这个词&#xff0c;尤其和OpenClaw、React、Node.js绑在一起出现——比如“agent failed before reply: session file locked (timeout 60000ms) openclaw”这种…

作者头像 李华
网站建设 2026/10/2 11:40:37

203.诊断

实验室不大&#xff0c;大约二十平方米左右&#xff0c;但布置得井井有条。进门左手边是一张不锈钢操作台&#xff0c;台上摆放着电子天平、切割机、镶嵌机和磨抛机等样品制备设备。右手边靠墙的位置&#xff0c;一台崭新的台式直读光谱仪静静地矗立着&#xff0c;银白色的外壳…

作者头像 李华
网站建设 2026/10/2 11:38:35

一路走来没有敌人,都是老师

一路走来没有敌人&#xff0c;都是老师01 【没有敌人&#xff0c;都是老师】 卓大&#xff0c;我们是今年疯狂电路第三名&#xff0c; 我们把方案全部开源了&#xff0c; 开源的讲解还在持续更新中。  卓大你好&#xff0c;我们是疯狂电路的Soberup队。 在备赛的过程中&#x…

作者头像 李华
网站建设 2026/10/2 11:38:02

不干胶标签印刷实力厂家推荐,广东源头工厂资质齐全用户力荐

找不干胶标签印刷厂&#xff0c;为什么广东的源头工厂更值得信任?做美妆、食品、医药、日化生意的老板&#xff0c;几乎都遇到过同样的难题&#xff1a;产品做出来了&#xff0c;标签却迟迟定不下来。找小作坊&#xff0c;怕品质不稳、交期没保障;找大厂&#xff0c;又常常面临…

作者头像 李华