news 2026/10/9 21:42:00

Python + Selenium + webdriver-manager 网页自动化截图实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python + Selenium + webdriver-manager 网页自动化截图实战

做网页自动化的人,早晚都会遇到一个需求:把网页当前的样子变成一张图片,留着存档、做巡检、发报告,或者仅仅是“眼见为实”。以前我接到这类需求,第一反应是requests拿页面源码,但很多页面是动态渲染出来的,拿到的只是没填充数据的空壳。后来切到selenium,配上webdriver-manager自动匹配浏览器驱动,一套截图脚本几分钟就能跑通,省掉了手动下载驱动、还要对版本号的麻烦。

这篇文章把完整的实现过程、关键参数、为什么这样选型和踩过的坑都整理出来。适合刚入门 Python、想给网页做自动化截图的朋友参考,也适合测试岗的同学拿去改造成 UI 巡检工具。不需要你有很深的浏览器内核知识,只要会基本的 Python 语法就够了。

1. 网页截图工具的技术选型:为什么是 selenium + webdriver-manager

1.1 截图工具的常见实现路线

做网页截图,市面上其实有好几条路可以走。最朴素的是直接用requests拉 HTML,配合html2image之类的东西转换,但这条路对现代网页基本走不通——现在的前端页面大量依赖 JavaScript 动态渲染数据,直接拿回来的 HTML 里往往只有骨架,没有真实的商品价格、图表数据、登录后内容,截出来的图跟用户看到的是两回事。

另一条路是 Playwright,微软出的自动化框架,API 设计比 Selenium 现代很多,自带截图 API,还能自动管理浏览器。我用过一段时间,确实好用,但如果你身边同事、网上资料大多用的是 Selenium,出问题时能搜到的答案反而更多。还有 Node 生态里的 Puppeteer,也不错,但要为了截图去搭一套 Node 环境,对 Python 开发来说不划算。

Selenium 的最大优势是生态成熟。它是 WebDriver 规范的老牌实现,资料多、案例多、各种奇奇怪怪的报错几乎都有人踩过。配合 Python 使用,代码写起来也简单:启动浏览器、访问 URL、调用截图方法,三步就完事。它就像给浏览器装了一个遥控器,通过 WebDriver 协议向浏览器下发指令,我们不用关心底层是怎么通过 DevTools 通信的,只要知道“调这个方法 = 按了遥控器上的截图键”就行。

1.2 webdriver-manager 到底解决了什么问题

用 Selenium 有个绕不开的麻烦:它需要浏览器驱动。Chrome 要用 chromedriver,Firefox 要用 geckodriver,这些驱动负责在测试代码和浏览器之间翻译指令。问题在于,驱动和浏览器版本是严格对应的——Chrome 大版本更新了,旧驱动就不能用了,你得手动去 Chrome for Testing 官网找对应版本,下载、解压、放到某目录、写进环境变量,或者代码里写死路径。

WebDriver-Manager 干的事就是把这坨手动操作替换掉。它在代码里自动检测你本机 Chrome 的版本号,然后到对应源下载匹配的 chromedriver,缓存到用户目录,再把这个缓存路径交回给 Selenium,全程不需要你关心驱动下载到了哪里。

可以这么理解:驱动是浏览器和 Selenium 之间的“翻译官”,版本不对就像翻译官拿着过时的词典,谈话双方明明是同一个意思,他却翻译得牛头不对马嘴。Manual 下载就是每次都要亲自去给翻译官找新词典,webdriver-manager 则是雇了个助理,让他全权负责词典的更新和携带。

这个库还支持 Chrome、Firefox、Edge 等多类浏览器,API 也统一,比如 Firefox 就是把ChromeDriverManager换成GeckoDriverManager,其余逻辑基本不变。这就意味着,你的截图脚本想换个浏览器内核做对比,改动成本非常低。

1.3 为什么驱动版本必须和浏览器版本匹配

这背后其实是浏览器与 WebDriver 之间通过 CDP(Chrome DevTools Protocol,Chrome 浏览器调试协议)通信的机制。Selenium 发指令给驱动,驱动再通过 CDP 和浏览器实例对话。浏览器每次更新都会调整内部实现,旧版驱动如果不知道新版的命令格式,就会回报 unknow error。

WebDriver 协议本身是 W3C 标准,设计上是向后兼容的,但浏览器的快速迭代导致驱动必须跟着发版。所以你会看到官方文档里永远强调“确保 chromedriver 和 Chrome 主版本号一致”。版本对应关系建议永远以官方维护的表格为准,不要凭记忆猜。Chrome 是 120 版本,驱动就用 120.x,这种“主版本对齐”基本能保证稳定运行。

2. 环境准备:版本关系搞不懂,后面全是坑

2.1 先想清楚 Python 版本问题

这个项目对 Python 版本不算挑剔,Python 3.8 以上基本都能跑。我自己的习惯是装 Python 3.10 或 3.11,不是因为它功能新,而是因为 Selenium、WebDriver-Manager 这些库在新版本上的测试最充分,遇到问题处理起来也容易。

环境方面,强烈建议用虚拟环境(venv),不要直接往全局 Python 里装。原因很现实:每个项目依赖的库版本不一样,今天这个项目要 Selenium 4.x,明天那个项目要 3.x,全局环境没法同时满足。虚拟环境就像给每个项目单独开一个工作舱,依赖互不干扰,想删就删,不会一不小心把公司线上项目需要的库版本给覆盖掉。创建命令很简单:

python -m venv venv source venv/bin/activate # Windows 用 venv\Scripts\activate

看到命令行前面出现了(venv)就说明环境激活成功。接着检查一下 Python 版本,确认没问题再继续装依赖。

2.2 三行命令装完依赖,但有个版本对应要先搞懂

依赖其实就两个核心库,一条命令搞定:

pip install selenium webdriver-manager

国内网络环境下,如果下载速度慢或者超时,可以加-i https://pypi.tuna.tsinghua.edu.cn/simple指定清华源。装完之后可以验证一下版本号:

pip show selenium webdriver-manager

版本不是越高越好,但也不要用手动下载的老版本。Selenium 4.x 及以上是现在的主流,API 比 3.x 清晰不少,尤其是 Service 对象的引入,让驱动的指定方式变得更严谨。

同时确认你电脑里装了 Chrome 浏览器。这点看起来废话,但真有人会在没装浏览器的服务器上跑这类代码,然后报错cannot find Chrome binary。Chrome 的版本号可以在浏览器地址栏输入chrome://version查看,我们需要的是第一位数字,比如 126.0.6478.126,就记 126。虽然 webdriver-manager 会自动检测,但你自己心里有数,排查问题时更容易判断报错是因为版本不匹配,还是网络问题。

这里多提一句:Selenium 4 和 3 在启动驱动的方式上有差异。旧写法webdriver.Chrome(executable_path="...")在 4.x 中已经被标记为废弃,新写法是通过Service指定驱动路径。关于这一点,下面的代码会体现出来,建议新写的项目直接按新 API 来。

3. 核心代码实现:从打开浏览器到保存截图

3.1 最小可用的完整截图脚本

下面是一份可以直接运行的代码,注释写得比较全。它做的事情就是打开 Chrome 无头浏览器,访问一个页面,等待几秒,截全屏图,然后退出:

import os import time from selenium import webdriver from selenium.webdriver.chrome.service import Service from webdriver_manager.chrome import ChromeDriverManager # 配置 Chrome 启动参数 options = webdriver.ChromeOptions() options.add_argument("--headless=new") # 无头模式:不弹出浏览器窗口 options.add_argument("--no-sandbox") # 部分 Linux 环境下必须加 options.add_argument("--disable-gpu") # 服务器环境减少 GPU 兼容问题 options.add_argument("--window-size=1920,1080") # 设置窗口/截图分辨率 # 自动获取与本地 Chrome 匹配的 chromedriver service = Service(ChromeDriverManager().install()) driver = webdriver.Chrome(service=service, options=options) try: url = "https://example.com" driver.get(url) # 等待页面加载完成,直接 sleep 是最简单但不优雅的做法 time.sleep(3) # 保存截图,返回 True 表示成功 result = driver.save_screenshot("screenshot.png") if not result: print("截图失败,检查页面是否正常打开") else: print("截图已保存,尺寸为", driver.get_window_size()) finally: driver.quit()

如果你只是想在本地试一试,这个代码足够了。ChromeDriverManager().install()首次运行时会下载驱动,之后命中缓存就直接复用,第二次开始启动速度会快很多。

但要注意,time.sleep(3)是写死的等待时间,如果页面资源多、加载慢,3 秒很可能不够,截出来是一半内容甚至白屏。更稳妥的方式是用显式等待,后面我会单独讲。

3.2 ChromeOptions 里的参数到底有什么讲究

--headless=new是核心参数。它让浏览器在后台运行,不弹出任何界面,特别适合部署在服务器上,或者你在本地写脚本的同时还想继续用浏览器干别的活。旧的--headless已经被标记为过时,新版 Chrome 推荐--headless=new,它对 HTML 的渲染逻辑跟有头模式差别更小,截出来的图也更接近真实用户所见。

--no-sandbox在本地 macOS/Windows 上通常不是必须的,Linux 服务器上缺了它经常报权限错误。这是 Chrome 的沙箱隔离机制和服务器默认用户权限冲突导致的,加上这个参数就是在告诉浏览器“别开沙箱了,我信任这个环境”。个人本机调试可以不加,线上部署最好加上。

--disable-gpu是为了规避服务器上没有 GPU 导致的黑屏、花屏问题。你本地开发机有显卡,这个问题不明显,但在云服务器上很常见。加上之后强制使用软件渲染,稳定性更高,截图内容不会缺图层。

--window-size决定了截图的分辨率。这句话很关键:无头模式下,浏览器窗口大小往往默认只有 800x600,你不设置这个参数,截出来的图就是又窄又矮的。你要的是像用户用 1920 宽度屏幕看到的效果,就得把窗口设成对应尺寸。这也是新手最常忽略的参数,没有之一。

另外,这两个参数也建议按需添加:

options.add_argument("--disable-extensions") # 禁用浏览器扩展,避免干扰 options.add_argument("--blink-settings=imagesEnabled=true") # 开启图片加载

后者默认就是开启的,但如果你之前做过页面性能优化、把图片加载关掉过,就会影响截图内容,显式加上更稳妥。

3.3 显式等待:比 sleep 更靠谱的等待方式

time.sleep最大的问题就是拍脑袋定时间。网络慢的时候不够用,网络快的时候浪费时间。Selenium 提供了两种官方等待机制:

from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By # 最多等待 10 秒,直到页面上出现 id 为 “content” 的元素 WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.ID, "content")) )

这段代码的意思是:轮询页面,每 0.5 秒检查一次指定元素是否出现,超过 10 秒就抛异常。它的好处是,页面 2 秒加载完就 2 秒继续,5 秒加载完就 5 秒继续,不会因为固定等待而白白浪费时间。

截图场景里,最常用的几个条件是presence_of_element_located(元素出现)、visibility_of_element_located(元素可见)、element_to_be_clickable(元素可点击)。具体用哪个,取决于你的目标页面。

比如截一个商品详情页,至少要等“价格”元素渲染出来,才能保证图里有核心信息;截一个登录后的后台面板,就要等某个图表容器出现。如果没有合适的元素条件,退而求其次,等待document.readyState == "complete"也行,但注意它只表示文档结构加载完,不保证动态数据填充完毕。

3.4 从单页截图进化到批量截图

有了基础版本,下一个需求通常就是批量截图:给一串 URL,逐个打开、逐个截图、存成不同的文件。批量场景下有个关键认知:如果 URL 都是同一个网站的页面,可以只启动一个浏览器实例连续访问,不需要每个页面都重新启动一次浏览器,那样既慢又容易触发风控。

import os import time from datetime import datetime from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from webdriver_manager.chrome import ChromeDriverManager urls = load_urls() # 你的 URL 列表 options = webdriver.ChromeOptions() options.add_argument("--headless=new") options.add_argument("--window-size=1920,1080") options.add_argument("--no-sandbox") options.add_argument("--disable-gpu") service = Service(ChromeDriverManager().install()) driver = webdriver.Chrome(service=service, options=options) # 按日期建目录,避免文件堆积 out_dir = datetime.now().strftime("shots_%Y%m%d") os.makedirs(out_dir, exist_ok=True) for idx, url in enumerate(urls, start=1): try: driver.get(url) # 等页面标题出现,说明已经跳转完成 WebDriverWait(driver, 10).until(EC.title_contains("")) time.sleep(2) # 留点缓冲时间 filename = os.path.join(out_dir, f"{idx:03d}_{url.replace('https://', '').replace('/', '_')}.png") driver.save_screenshot(filename) print(f"[{idx}] 已保存: {filename}") except Exception as e: print(f"[{idx}] 截图失败: {url} -> {e}") driver.quit()

这个版本里我加了几个实用细节:

  • 按日期建目录,避免多次运行文件堆在一起。
  • 文件用序号加域名组合命名,方便追溯。
  • 每一条都包了 try/except,单个页面失败不会中断整批任务。
  • driver.quit()放在最后,确保循环结束释放浏览器进程。

有一点需要提醒:如果目标网站有反爬机制,高频连续截图容易被限制。稍微懂礼貌一点的做法是每次截图之间加随机延时,比如time.sleep(2 + random.uniform(0.5, 2)),不要完全固定间隔。

4. 实测过程中的参数调优

4.1 不同页面的截图表现:全屏截图与长页面问题

我在实际测试中发现,save_screenshot()截取的是“当前视口”的内容,但对于比较长的页面,它并不会自动把整个页面拼起来。你设置--window-size=1920,1080,它截的就是 1920x1080 那么大一块;页面内容超过一屏,下面的部分就漏掉了。

为什么?因为save_screenshot的底层行为就是截取浏览器可视区域,而浏览器在无头模式下就认为你的屏幕只有那么大。Chrome 支持一个--force-device-scale-factor参数影响缩放,还有一个思路是先把窗口高度设置为页面总高度,再截图。Selenium 官方还提供了一个add_cdp_capabilities的方式,可以直接调用 Chrome DevTools Protocol 的Page.captureScreenshot接口来截全图。这个方法更底层,但代码量多一些,适合需要高精度的场景。

如果不想引入复杂逻辑,很多场景下直接把--window-size的纵向开大,比如--window-size=1920,3000,也能覆盖大多数页面。缺点是你无法事先知道页面多长,要么调大到一个足够用的值,要么动态获取文档高度再重设窗口。

4.2 动态计算页面高度并调整窗口

这里给出一个实际项目里更优雅的解法。核心思路是先用一个小窗口打开页面,通过 JavaScript 获取到页面完整的滚动高度,再重新设置窗口大小,然后截图:

# 打开页面 driver.get(url) # 获取页面完整高度 scroll_height = driver.execute_script("return document.body.scrollHeight") window_height = driver.execute_script("return window.innerHeight") # 如果页面高度大于当前窗口高度,就重新设置窗口大小 if scroll_height > window_height: driver.set_window_size(1920, scroll_height) time.sleep(1) # 等待布局稳定 driver.save_screenshot("full_page.png")

这个方案能解决大多数长页面截不全的问题。但要注意,有些页面通过懒加载机制实现“滚动到什么位置就加载什么内容”,比如图片是在滚动到可视区域附近才加载的,那么单纯把窗口调高、不触发滚动事件,图片依然不会加载出来,截出来的图底部是空白的。

针对懒加载页面,更可靠的做法是用程序模拟滚动到底部、等待图片加载、再滚回顶部,然后再截图。简单实现如下:

# 模拟滚动加载 previous_height = 0 while True: driver.execute_script("window.scrollTo(0, document.body.scrollHeight)") time.sleep(2) # 等待懒加载内容出现 new_height = driver.execute_script("return document.body.scrollHeight") if new_height == previous_height: break previous_height = new_height # 回到顶部并截图 driver.execute_script("window.scrollTo(0, 0)") time.sleep(1) driver.set_window_size(1920, driver.execute_script("return document.body.scrollHeight")) driver.save_screenshot("full_page_loaded.png")

这个循环的终止条件就是“页面高度不再变化”,说明滚到底也没有新内容加载了。实话说,这种技巧对内容特别多的网站不一定完美,但由于不用引入额外定位,体验已经比直接截图好很多。

4.3 图片保存格式与路径的选择

Selenium 的save_screenshot只支持 PNG 格式,这是它的限制。如果你需要 JPEG 或者 WebP,就得自己加一步图像转换。通常做法是先用driver.get_screenshot_as_png()拿到二进制的 PNG 数据,再用 Pillow 库转成别的格式:

from PIL import Image import io png_data = driver.get_screenshot_as_png() img = Image.open(io.BytesIO(png_data)) img.convert("RGB").save("screenshot.jpg", quality=85)

这里有两个细节值得提。第一,如果页面背景是透明的(少数页面会这样),转成 JPEG 时背景会变黑,最好还是用 PNG;第二,质量参数quality=85能兼顾文件大小和清晰度,如果你只是存档用,80 到 85 是一个不错的区间,肉眼基本看不出差别,文件体积能小不少。

文件路径的坑我也踩过几次。Windows 下文件名里不能包含英文冒号、反斜杠等特殊字符,所以用 URL 直接做文件名前,最好把特殊字符替换掉。上面的代码里用replace("/", "_"),但还是建议再做一次清洗,保守一点:

safe_name = url.replace("https://", "").replace("http://", "") safe_name = "".join(c if c.isalnum() or c in "._-" else "_" for c in safe_name)

这句把除了字母数字和._-之外的字符全部替换成下划线,避免各种隐藏的编码问题。

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

5.1 排错速查表

我用这个方案写了不下十个自动化脚本,把最常遇到的报错整理成一份速查表,方便大家直接对照着看。每次程序跑不通的时候,先看报错文本属于哪一类,而不是急着改代码。

报错信息(关键词)可能原因解决办法
Fail to download chromedriver网络无法访问驱动下载源,或源不稳定换镜像源,或者手动下载驱动后通过Service指定路径
SessionNotCreatedException: This version of ChromeDriver only supports Chrome version X浏览器和驱动版本失配清理 webdriver-manager 缓存,或者升级 webdriver-manager 到最新版
WebDriverException: unknown error: cannot find Chrome binary系统里没有安装 Chrome,或路径不是默认路径安装 Chrome,或者用options.binary_location指定浏览器路径
截图结果全白/只有背景色页面资源加载太慢,等待时间不足改用显式等待,或者增加time.sleep缓冲
截图尺寸不对,比如只有 800x600无头模式下没有设置--window-size加上--window-size=1920,1080后再启动
chrome not reachable上一次运行进程没有driver.quit(),残留了僵尸进程杀掉残留进程,或者重启终端/服务器
DevToolsActivePort file doesn't existChrome 启动参数冲突,通常是临时目录权限问题加--no-sandbox,必要时指定--user-data-dir到有权限的路径
图片中有大面积灰色图标图片懒加载未触发模拟滚动到底部,等加载完成后再滚回顶部截图

表格里的前两类问题在初学阶段出现频率最高,尤其是版本失配,它也是很多人第一次接触 webdriver-manager 的原因。如果升级完库依然报版本不对,最直接的办法是删除 webdriver-manager 的缓存目录,让它强制重新下载。缓存位置一般在用户目录下,比如~/.wdm,删掉再运行脚本就会重新拉取。

5.2 我踩过的三个细节坑

第一个坑是 webdriver-manager 的缓存机制。你以为它自动检测到浏览器升级了就会自动更新驱动,实际上它默认优先复用缓存里已有的版本。如果你的 Chrome 从 126 升到了 127,但缓存里还有 126 的驱动,脚本不会主动重新下载,而是继续用旧驱动,直到跑出版本报错。解决办法就是删~/.wdm目录,或者手动调用ChromeDriverManager().install()的新版本 API,强制重装。

第二个坑是 headless 模式和窗口尺寸的关系。我在一台服务器上部署截图任务时,明明代码里设了--window-size=1920,1080,截出来的图片却还是 800 宽的小图。查了半天,发现问题出在启动参数传递顺序,以及 Chrome 新版 headless 模式的内部处理逻辑。后来我把无头模式的写法从--headless改成--headless=new,问题就消失了。新版无头模式对窗口尺寸的解析更接近真实浏览器,如果你还在用旧写法,建议尽快升级。

第三个坑是忘记driver.quit()。脚本在本地跑的时候,你可能觉得它结束就结束了,没什么影响。但在服务器上跑批量任务,每次不退出浏览器,Chrome 进程就会一直挂在后台吃内存。跑了几百个 URL 之后,服务器内存被占满,后续任务全部卡死。正确做法是写try/finally或者with语句,确保不管中间报不报错,解析器退出后都会关闭浏览器。不要小看这一句,在长任务里它会把你从内存爆炸的边缘拉回来。

5.3 面对异步渲染页面的确定性策略

有些页面不是一次性渲染完的,而是异步请求接口,拿到数据再更新 DOM。这种页面你在请求刚发出去的时候截图,大概率截到的是占位图。除了显式等待元素出现,我还有一个习惯:等待网络请求数量从有到无,或者等待某个关键图片加载完成。

Selenium 判断图片加载完成没有直接的 API,通常是通过元素属性检查。比如等待一个<img>标签的naturalWidth大于 0:

from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By WebDriverWait(driver, 10).until( lambda d: d.execute_script( "return document.querySelector('img.hero') ? document.querySelector('img.hero').naturalWidth > 0 : false" ) )

虽然代码写起来比单纯等标题复杂,但在截图场景里很实用。判断依据是:图片真正加载完成之前,naturalWidth永远是 0,这个值不会被 CSS 尺寸影响,非常稳定。

6. 从截图工具到自动化巡检的扩展思路

单页截图是最小场景,但真实工作中,这个工具通常只是完整链路的一部分。我自己就把它扩展成了一个简单的页面巡检工具:每天定时爬一批页面,截图存档,然后用 Python 的 Pillow 库对比前后两天的截图像素差异,一旦发现页面关键区域变化超过阈值,就推送邮件提醒。

这个扩展过程其实很简单,核心代码就是上面这些,加三样东西就够:定时调度(Linux 上有 cron,也可以直接用schedule库)、邮件发送(smtplib)、图像对比(PIL.ImageChops)。

另一种常见扩展是配合 OCR。截完图后用 Tesseract 识别图里的文字,再和预期关键词做匹配,比如“价格”“库存”“已售罄”等。这在商品页面监控、活动页面巡检中都非常有用,不需要前端给你任何接口,所见即所得。

如果你想做得更工程化,还可以把 URL 列表放到配置文件里,用yaml或json维护;或者用数据库记录每次截图的元信息,方便回溯。不过这些都属于锦上添花了,核心还是先把截图的稳定性练好。

最后再分享一个操作上的小习惯。我写截图脚本时,一定会在driver.get(url)之后把滚动位置强制恢复到顶部,再等一会儿截图。原因很简单:Chrome 有时候会记住上次访问该页面的滚动位置,或者页面有“回到原来位置”的自动行为,不处理的话,截出来的图可能是页面中间的某个位置,而不是首屏。这个坑很难从代码层面一眼看出,但看到截图内容的瞬间你就明白了。

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

用PyQt5打造轻量级数据库操作工具:从QSqlTableModel到SQLite实战

简介&#xff1a;一份基于Python PyQt5开发的数据库操作小工具源码&#xff0c;同时附带了SQLite数据库文件&#xff0c;面向正在学习PyQt5界面编程与sqlite3数据库交互的开发者&#xff0c;尤其适合需要轻量级桌面数据库管理场景的动手实践。包内共171个文件&#xff0c;压缩包…

作者头像 李华
网站建设 2026/10/9 21:39:36

Godot引擎移植鸿蒙PC:跨生态适配的技术断层与分阶段实践

1. 项目概述&#xff1a;这不是一次简单的“移植”&#xff0c;而是一场跨生态的系统级适配Godot 游戏编辑器移植鸿蒙 PC——光看标题&#xff0c;很多人第一反应是“不就是换个平台编译一下&#xff1f;”但我在游戏引擎底层开发和跨平台工具链打磨上干了十多年&#xff0c;亲…

作者头像 李华
网站建设 2026/10/9 21:39:21

HiClaw 开源版本地安装:5 分钟跑通 OpenClaw 团队协作

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/9 21:35:55

低功耗电源管理实战:PCA9422+ATmega32替代分立器件方案

最近在跟进一个低功耗采集终端的电源部分&#xff0c;原来的方案其实是“散件堆叠”&#xff1a;一颗 LDO 稳 3.3V&#xff0c;一颗升降压给通信模块&#xff0c;再用 GPIO 控制负载开关。MCU 用的是 ATmega32&#xff0c;控制逻辑不算复杂。可真把板子拿回来实测&#xff0c;问…

作者头像 李华
网站建设 2026/10/9 21:35:50

Evolver 与 Hermess Agent 实战:把 endpoint 改到 TaoToken 的配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/10/9 21:34:34

极简命名与状态闭环:用七个状态位构建可复现的自动化流程

1. 从一串“1”说起&#xff1a;这个项目到底在做什么第一次看到“1111111”这个标题&#xff0c;我脑子里蹦出来的第一个念头是&#xff1a;这要么是随手敲的占位符&#xff0c;要么就是一个刻意用极简符号命名的项目。做过几年项目的人都知道&#xff0c;真正被反复打磨的东西…

作者头像 李华