惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Fortinet All Blogs
爱范儿
爱范儿
P
Proofpoint News Feed
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
T
Tailwind CSS Blog
J
Java Code Geeks
宝玉的分享
宝玉的分享
Jina AI
Jina AI
B
Blog
N
Netflix TechBlog - Medium
Recent Announcements
Recent Announcements
aimingoo的专栏
aimingoo的专栏
腾讯CDC
C
Check Point Blog
The Cloudflare Blog
阮一峰的网络日志
阮一峰的网络日志
博客园 - Franky
罗磊的独立博客
B
Blog RSS Feed
WordPress大学
WordPress大学
小众软件
小众软件
博客园 - 叶小钗
M
MIT News - Artificial intelligence
GbyAI
GbyAI

竹林里有冰的博客

Nuxt SSG 博客的尾斜杠到底怎么加? | 竹林里有冰的博客 小米 Xiaomi Book Pro 14 (Ultra X7) Linux 兼容性实测 | 竹林里有冰的博客 国内(大陆)版小米 FCM 熄屏断连:Rootless 环境下的尝试与可能的解决方案 | 竹林里有冰的博客 我没法访问 dl.google.com —— 记一次 TUN 下的网络 debug | 竹林里有冰的博客 Vercel 的缓存控制,你注意过吗? | 竹林里有冰的博客 小记 —— Caddy 在 Layer 4 上的流量代理实践 | 竹林里有冰的博客 你的域名后缀拖慢你的网站速度了嘛?——再谈 DNS 冷启动 | 竹林里有冰的博客 DNS 冷启动:小型站点的“西西弗斯之石” | 竹林里有冰的博客 HTTP/2 Server Push 已事实性“死亡”,我很怀念它 | 竹林里有冰的博客 Nuxt Content v3 中数组字段的筛选困境与性能优化 | 竹林里有冰的博客 后 OCSP 时代,浏览器如何应对证书吊销新挑战 | 竹林里有冰的博客 初试 Github Action Self-hosted Runner,想说爱你不容易 | 竹林里有冰的博客 DNS 解析延迟毁了我的图床优化 | 竹林里有冰的博客 Vue Markdown 渲染优化实战(下):告别 DOM 操作,拥抱 AST 与函数式渲染 | 竹林里有冰的博客 Vue Markdown 渲染优化实战(上):从暴力刷新、分块更新到 Morphdom 的华丽变身 | 竹林里有冰的博客 node-sass 迁移至 dart-sass 踩坑实录 | 竹林里有冰的博客 前端中的量子力学——一打开 F12 就消失的 Bug | 竹林里有冰的博客 2025 年,如何为 web 页面上展示的视频选择合适的压缩算法? | 竹林里有冰的博客 el-image 和 el-table 怎么就打架了?Stacking Context 是什么? | 竹林里有冰的博客 2025年,前端如何使用 JS 将文本复制到剪切板? | 竹林里有冰的博客 ssh 拯救世界——通过 ssh 隧道在内网服务器执行 APT 更新 | 竹林里有冰的博客 Cudy TR3000 吃鹅(daed)记 | 竹林里有冰的博客 使用 Cloudflare Workers 监控 Fedora Copr 构建状态 | 竹林里有冰的博客 基于 Cloudflare Workers 实现的在线服务状态检测告警系统 | 竹林里有冰的博客 构建部署在 Cloudflare Workers 上的 TG Bot | 竹林里有冰的博客 2024年,Firefox 是唯一还在坚持执行在线的 SSL 证书吊销状态检查的主流浏览器 | 竹林里有冰的博客 小爱课程表适配不完全指北——以 ZJUT 本科正方教务系统为例 | 竹林里有冰的博客 将博客从 waline v2 更新到 waline v3 | 竹林里有冰的博客 给家里云装上 Fedora 41 KDE 后,我是如何配置的 | 竹林里有冰的博客 为 Hexo 添加 follow 认证 | 竹林里有冰的博客
手动指定 python-selenium 的 driver path 以解决在中国大陆...
竹林里有冰 · 2023-09-02 · via 竹林里有冰的博客

之前因为学校社团迎新的需求,就临时写了一个 QQ Bot,最近又给 bot 加上了 /q 的功能,原理是通过 python 的 selenium 去启动一个 headless Firefox 去截由 jinja2 模板引擎生成的 html 的图。

每次这个 bot 重启的时候都因为 selenium 而需要花费好几秒的时间,甚至经常概率性启动失败。我就寻思者应该把这个图片生成的 generator 从 bot 中抽出来,这样就不至于每次重启 bot 都要遭此一劫。但就在我将 generator 打包成 docker 部署上云服务器的时候,发现居然无法启动。于是手动进 docker 的 shell 开 python 的交互式终端,发现在创建 firefox 的 webdriver 对象的时候异常缓慢,等了半分钟以后蹲到一个报错如下:

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/selenium/webdriver/common/driver_finder.py", line 38, in get_path
    path = SeleniumManager().driver_location(options) if path is None else path
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/selenium/webdriver/common/selenium_manager.py", line 95, in driver_location
    output = self.run(args)
             ^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/selenium/webdriver/common/selenium_manager.py", line 141, in run
    raise WebDriverException(f"Unsuccessful command executed: {command}.\n{result}{stderr}")
selenium.common.exceptions.WebDriverException: Message: Unsuccessful command executed: /usr/local/lib/python3.11/site-packages/selenium/webdriver/common/linux/selenium-manager --browser firefox --output json.
{'code': 65, 'message': 'error sending request for url (https://github.com/mozilla/geckodriver/releases/latest): connection error: unexpected end of file', 'driver_path': '', 'browser_path': ''}


The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.11/site-packages/selenium/webdriver/firefox/webdriver.py", line 59, in __init__
    self.service.path = DriverFinder.get_path(self.service, options)
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/selenium/webdriver/common/driver_finder.py", line 41, in get_path
    raise NoSuchDriverException(msg) from err
selenium.common.exceptions.NoSuchDriverException: Message: Unable to obtain driver for firefox using Selenium Manager.; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors/driver_location

我才发现 selenium 试图访问 https://github.com/mozilla/geckodriver/releases/latest 且访问失败了。仔细阅读了 selenium 项目的文档发现新版本的 selenium 会尝试自动下载 webdriver:

As of Selenium 4.6, Selenium downloads the correct driver for you. You shouldn’t need to do anything.

表面上看上去我不需要做任何事情,但项目组忽略了中国大陆的网络环境。

服务是要在境内的云服务器上跑的,我也不敢开代理,现在比较靠谱的方案是我去手动指定 Firefox 的 geckodriver,避免 selenium 去帮我自动下载一份。在 python-selenium 的官方文档中是让我们创建 Firefox 的 webdriver 时去传入一个 executable_path='geckodriver' 的关键词参数,可惜这是过时的用法,应该是维护者还没来得及更新文档。

随后便在 stackoverflow 上找到了新版 selenium 手动指定 Chrome 的 chromedriver 的方法

from selenium import webdriver
from selenium.webdriver.chrome.service import Service

service = Service(executable_path='chromedriver.exe') 
options = webdriver.ChromeOptions()
driver = webdriver.Chrome(service=service, options=options)
# ...
driver.quit()

原文给的是 chrome 的方案,但 Firefox 的方案基本也是一致的,应该也是去创建一个 service 对象,猜一猜就能猜到。

from selenium import webdriver
from selenium.webdriver.firefox.service import Service
from selenium.webdriver.firefox.options import Options

service = Service(executable_path='/root/geckodriver') # 我这里是 docker 打包,懒得创建一个普通用户了,就直接用了 root 用户的 home 目录
options = Options("--headless")
driver = webdriver.Firefox(service=service, options=options)
# ...
driver.quit()

手动指定 geckodriver 后,在我 1 核 1 G 的小主机上创建 webdriver 对象,基本都可以秒完成。