java - 如何防止 Selenium 3.0 (Geckodriver) 创建临时 Firefox 配置文件?
问题描述
我正在使用Geckodriver运行最新版本的Selenium WebDriver。我想阻止Selenium在启动WebDriver的新实例时在临时文件目录中创建临时 Firefox 配置文件。相反,我想直接使用原始的 Firefox 配置文件。这有双重好处。首先,它节省了时间(将配置文件复制到临时目录需要大量时间)。其次,它确保在会话期间创建的 cookie 被保存到原始配置文件中。在 Selenium 开始依赖Geckodriver之前,我可以通过编辑类来解决这个问题,如下所示:FirefoxProfile.class
SeleniumHQ
public File layoutOnDisk() {
File profileDir;
if (this.disableTempProfileCreation) {
profileDir = this.model;
return profileDir;
} else {
try {
profileDir = TemporaryFilesystem.getDefaultTmpFS().createTempDir("ABC", "XYZ");
File userPrefs = new File(profileDir, "user.js");
this.copyModel(this.model, profileDir);
this.installExtensions(profileDir);
this.deleteLockFiles(profileDir);
this.deleteExtensionsCacheIfItExists(profileDir);
this.updateUserPrefs(userPrefs);
return profileDir;
} catch (IOException var3) {
throw new UnableToCreateProfileException(var3);
}
}
}
disableTempProfileCreation
当参数设置为 true时,这将阻止 Selenium 创建临时 Firefox 配置文件。
但是,现在 Selenium 由Geckodriver控制,此解决方案不再适用,因为 Firefox Profile 的创建(和启动)由Geckodriver.exe
(用Rust
语言编写)控制。如何使用Geckodriver实现相同的目标?我不介意编辑源代码。我正在使用 Java。
谢谢
重要更新:
我要感谢大家抽出时间来回答这个问题。但是,正如一些评论中所述,前 3 个答案根本没有解决这个问题- 有两个原因。首先,使用现有的 Firefox 配置文件不会阻止Geckodriver
将原始配置文件复制到临时目录(如 OP 中所示,并由以下一位或多位评论员明确说明)。其次,即使它与 Selenium 3.0 不兼容。
我真的不确定为什么 4 个答案中有 3 个会以完全相同的错误重复完全相同的答案。他们读过这个问题吗?甚至尝试解决手头问题的唯一答案是@Life的答案很复杂,但它不完整。谢谢。
解决方案
2021 年 5 月 30 日更新后
这是我在 Stack Overflow 上尝试过的最难回答的问题。因为它涉及用多种语言(Java、Rust 和 C++)编写的多个代码库的交互。这种复杂性使这个问题可能无法解决。
我最后一次破解这个可能无法解决的问题:
在您问题的代码中,您正在修改文件user.js该文件仍由Selenium使用。
public FirefoxProfile() {
this(null);
}
/**
* Constructs a firefox profile from an existing profile directory.
* <p>
* Users who need this functionality should consider using a named profile.
*
* @param profileDir The profile directory to use as a model.
*/
public FirefoxProfile(File profileDir) {
this(null, profileDir);
}
@Beta
protected FirefoxProfile(Reader defaultsReader, File profileDir) {
if (defaultsReader == null) {
defaultsReader = onlyOverrideThisIfYouKnowWhatYouAreDoing();
}
additionalPrefs = new Preferences(defaultsReader);
model = profileDir;
verifyModel(model);
File prefsInModel = new File(model, "user.js");
if (prefsInModel.exists()) {
StringReader reader = new StringReader("{\"frozen\": {}, \"mutable\": {}}");
Preferences existingPrefs = new Preferences(reader, prefsInModel);
acceptUntrustedCerts = getBooleanPreference(existingPrefs, ACCEPT_UNTRUSTED_CERTS_PREF, true);
untrustedCertIssuer = getBooleanPreference(existingPrefs, ASSUME_UNTRUSTED_ISSUER_PREF, true);
existingPrefs.addTo(additionalPrefs);
} else {
acceptUntrustedCerts = true;
untrustedCertIssuer = true;
}
// This is not entirely correct but this is not stored in the profile
// so for now will always be set to false.
loadNoFocusLib = false;
try {
defaultsReader.close();
} catch (IOException e) {
throw new WebDriverException(e);
}
}
所以理论上你应该可以修改geckodriver源代码中的capabilities.rs 。该文件包含temp_dir。
正如我在理论上所说的那样,因为当我查看 Firefox 源代码时,它的temp_dir分布在整个代码库中。
原帖 05-26-2021
我不确定您是否可以阻止 Selenium 创建临时 Firefox 配置文件。
从壁虎文件:
“配置文件是在系统临时文件夹中创建的。这也是在提供配置文件时提取编码配置文件的位置。默认情况下,geckodriver 将在此位置创建一个新配置文件。”
我目前看到的唯一解决方案是要求您修改 Geckodriver 源文件以防止创建临时文件夹/配置文件。
我目前正在查看源代码。这些文件可能是正确的,但我需要更多地查看源代码:
https://searchfox.org/mozilla-central/source/browser/app/profile/firefox.js
https://searchfox.org/mozilla-central/source/testing/mozbase/mozprofile/mozprofile/profile.py
以下是一些需要梳理的其他文件:
https://searchfox.org/mozilla-central/search?q=tempfile&path=
这看起来很有希望:
https://searchfox.org/mozilla-central/source/testing/geckodriver/doc/Profiles.md
“geckodriver 使用 [profiles] 来检测 Firefox 的行为。用户通常会依赖 geckodriver 生成临时的一次性配置文件。当 WebDriver 会话到期时,这些配置文件将被删除。
如果用户需要使用自定义的、准备好的配置文件,geckodriver 将对配置文件进行修改以确保正确的行为。在这种情况下,有关用户定义的首选项的优先级,请参阅下面的 [自动化首选项]。
可以通过两种不同的方式提供自定义配置文件:
1. 通过附加--profile /some/location
到 [ args
capability],这将指示 geckodriver 就地使用配置文件;
我在尝试这样做时发现了这个问题:如何使用 Selenium Webdriver 就地使用现有配置文件?
这里还有一个在 Github 上的 selenium 中提出的关于临时目录的问题。https://github.com/SeleniumHQ/selenium/issues/8645
查看geckodriver v0.29.1的源代码,我发现了一个加载配置文件的文件。
来源:capabilities.rs
fn load_profile(options: &Capabilities) -> WebDriverResult<Option<Profile>> {
if let Some(profile_json) = options.get("profile") {
let profile_base64 = profile_json.as_str().ok_or_else(|| {
WebDriverError::new(ErrorStatus::InvalidArgument, "Profile is not a string")
})?;
let profile_zip = &*base64::decode(profile_base64)?;
// Create an emtpy profile directory
let profile = Profile::new()?;
unzip_buffer(
profile_zip,
profile
.temp_dir
.as_ref()
.expect("Profile doesn't have a path")
.path(),
)?;
Ok(Some(profile))
} else {
Ok(None)
}
}
资料来源:marionette.rs
fn start_browser(&mut self, port: u16, options: FirefoxOptions) -> WebDriverResult<()> {
let binary = options.binary.ok_or_else(|| {
WebDriverError::new(
ErrorStatus::SessionNotCreated,
"Expected browser binary location, but unable to find \
binary in default location, no \
'moz:firefoxOptions.binary' capability provided, and \
no binary flag set on the command line",
)
})?;
let is_custom_profile = options.profile.is_some();
let mut profile = match options.profile {
Some(x) => x,
None => Profile::new()?,
};
self.set_prefs(port, &mut profile, is_custom_profile, options.prefs)
.map_err(|e| {
WebDriverError::new(
ErrorStatus::SessionNotCreated,
format!("Failed to set preferences: {}", e),
)
})?;
let mut runner = FirefoxRunner::new(&binary, profile);
runner.arg("--marionette");
if self.settings.jsdebugger {
runner.arg("--jsdebugger");
}
if let Some(args) = options.args.as_ref() {
runner.args(args);
}
// https://developer.mozilla.org/docs/Environment_variables_affecting_crash_reporting
runner
.env("MOZ_CRASHREPORTER", "1")
.env("MOZ_CRASHREPORTER_NO_REPORT", "1")
.env("MOZ_CRASHREPORTER_SHUTDOWN", "1");
let browser_proc = runner.start().map_err(|e| {
WebDriverError::new(
ErrorStatus::SessionNotCreated,
format!("Failed to start browser {}: {}", binary.display(), e),
)
})?;
self.browser = Some(Browser::Host(browser_proc));
Ok(())
}
pub fn set_prefs(
&self,
port: u16,
profile: &mut Profile,
custom_profile: bool,
extra_prefs: Vec<(String, Pref)>,
) -> WebDriverResult<()> {
let prefs = profile.user_prefs().map_err(|_| {
WebDriverError::new(
ErrorStatus::UnknownError,
"Unable to read profile preferences file",
)
})?;
for &(ref name, ref value) in prefs::DEFAULT.iter() {
if !custom_profile || !prefs.contains_key(name) {
prefs.insert((*name).to_string(), (*value).clone());
}
}
prefs.insert_slice(&extra_prefs[..]);
if self.settings.jsdebugger {
prefs.insert("devtools.browsertoolbox.panel", Pref::new("jsdebugger"));
prefs.insert("devtools.debugger.remote-enabled", Pref::new(true));
prefs.insert("devtools.chrome.enabled", Pref::new(true));
prefs.insert("devtools.debugger.prompt-connection", Pref::new(false));
}
prefs.insert("marionette.log.level", logging::max_level().into());
prefs.insert("marionette.port", Pref::new(port));
prefs.write().map_err(|e| {
WebDriverError::new(
ErrorStatus::UnknownError,
format!("Unable to write Firefox profile: {}", e),
)
})
}
}
查看 gecko 源代码后,看起来mozprofile::profile::Profile来自 FireFox 而不是 geckodriver
当您迁移到 Selenium 4 时,您似乎可能会遇到配置文件问题。
参考:https ://github.com/SeleniumHQ/selenium/issues/9417
对于 Selenium 4,我们不推荐使用配置文件,因为我们可以采取其他机制来加快启动速度。请使用 Options 类来设置您需要的首选项,如果您需要使用插件,请使用 driver.install_addon("path/to/addon") 您可以通过 pip install selenium 安装处于测试阶段的 selenium 4 --预
我在您的代码中注意到您正在写入user.js,这是 FireFox 的自定义文件。您是否考虑过在 Gecko 之外手动创建这些文件?
你也看过mozprofile吗?
推荐阅读
- python - 如何为 CatBoostRegressor 指定多个 eval_metric?
- firebase - 如何使用颤振从 Firestore 获取子集合?
- amazon-web-services - AWS SDK for Java S3 存储桶访问密钥 ID 问题
- python - 为什么NN预测不好?
- python - Django 组模型未显示在 Django 管理员中
- php - Laravel (PHP) : get a result from arrays to multidimensional associative array
- firebase - 在颤动的后端的 2 个不同的 Firestore 集合中搜索具有其 ID 的文档
- c# - 如何从 SQL Server 创建模型类
- python - 烧瓶 wtform 验证失败
- vba - VBA 将 UTF-8 字符附加到文本文件而不获取“???”