java 如何防止Selenium 3.0(Geckodriver)创建临时Firefox配置文件?

bnlyeluc  于 2023-01-15  发布在  Java
关注(0)|答案(6)|浏览(152)

我运行的是最新版本的 Selenium WebDriverGeckodriver)。我想阻止 Selenium 在启动新的 WebDriver 示例时在临时文件目录中创建临时Firefox配置文件。相反,我想直接使用原始Firefox配置文件。这有双重好处。首先,它节省了时间(将简档复制到临时目录要花费大量的时间)。第二,它确保会话期间创建的cookie保存到原始配置文件中。在Selenium开始依赖 Geckodriver 之前我可以通过在SeleniumHQ中编辑类FirefoxProfile.class来执行solve this problem,如下所示:

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。
我真的不知道为什么四分之三的人会用同样的错误重复同样的答案。他们读过这个问题吗?唯一一个试图解决这个问题的答案是“生活是复杂的,但它是不完整的”。谢谢。

7uzetpgm

7uzetpgm1#

更新日期: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 * 遍布整个代码库。

初始发布日期:2021年5月26日

我不确定你能阻止Selenium创建一个临时的Firefox配置文件。
gecko documents
"配置文件在系统临时文件夹中创建。当提供配置文件时,编码的配置文件也在此提取。默认情况下,geckodriver将在此位置创建新的配置文件。"
目前我看到的唯一解决方案是需要修改Geckodriver源文件以防止创建临时文件夹/配置文件。
我正在查看源代码。这些文件可能是正确的,但我需要查看源代码:

以下是需要梳理的其他一些文件:
https://searchfox.org/mozilla-central/search?q=tempfile&path=
这看起来很有希望:
https://searchfox.org/mozilla-central/source/testing/geckodriver/doc/Profiles.md
"geckodriver使用[配置文件]来检测Firefox的行为。用户通常会依赖geckodriver来生成临时的、一次性的配置文件。当WebDriver会话过期时,这些配置文件会被删除。
在用户需要使用定制的、准备好的配置文件的情况下,geckodriver将修改配置文件以确保正确的行为。请参见下面的[* 自动化首选项 *]了解在这种情况下用户定义首选项的优先级。
可以通过两种不同的方式提供自定义配置文件:

    • 1.通过将--profile /some/location附加到[args能力],这将指示geckodriver * 就地 * 使用简档;**

我发现这个问题试图这样做:how do I use an existing profile in-place with 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类设置所需的首选项,如果需要使用插件,请使用驱动程序。install_addon("path/to/addon")您可以通过pip install selenium--pre * 安装Selenium 4(测试版)

我注意到在你的代码中你写的是 * user. js *,这是FireFox的一个自定义文件。你有没有考虑过在Gecko之外手动创建这些文件?
你也看过mozprofile吗?

tuwxkamq

tuwxkamq2#

感谢link中的生活很复杂的答案中提供的源代码。我有机会看一下geckodriver的源代码。

解释

我相信您无法在源代码中找到任何rust_tmp的原因是它是由Profile::new()函数随机生成的。
当我深入查看代码结构时,我发现
browser. rs
是浏览器实际加载的位置,它是通过marionette. rs调用的。如果您仔细注意,每当初始化新会话时,LocalBrowser::new方法将被调用,配置文件也将在该状态下加载。然后通过检查browser. rs文件,将有一个块代码行60 - 70用于为新会话示例实际生成配置文件。现在,需要做的是修改此路径以加载您的自定义配置文件。

简短回答

正在下载geckodriver-0.30.0的zip文件,并使用您喜欢的zip程序进行解压缩:P
查看geckodriver源代码的src/www.example.com,第60 - 70行,希望您会看到类似以下内容:browser.rs**of geckodriver source, in line 60 - 70, hoping you will see something like this:

let is_custom_profile = options.profile.is_some();

        let mut profile = match options.profile {
            Some(x) => x,
            None => Profile::new()?,
        };

将其更改为您喜欢的文件夹(希望您知道一些 rust 代码),例如:

/*
        let mut profile = match options.profile {
            Some(x) => x,
            None => Profile::new()?,
        };
        */
        let path = std::path::Path::new("path-to-profile");
        let mut profile = Profile::new_from_path(path)?;

使用prefer rust编译器重新编译,例如:

Cargo build

注解

希望这些信息能对你有所帮助。这并不全面,但希望它是足够好的提示,你喜欢它是可能的,写一些额外的代码来加载配置文件从环境或传递从参数,这是可能的,但我不是生 rust 的开发人员,所以懒得提供一个在这里。
上面的解决方案对我来说很好,我可以直接加载和使用我的配置文件。顺便说一句,我在Archlinux上工作,有 rust 信息:* * 货物1.57.0**。
TBH,这是我第一次在stackoverflow上推送评论,所以如果我说错了或者回答不清楚,请随时纠正我:P

更新

1.我在geckodriver 0.30.0中工作,它与Life is complex中提到的geckodriver 0.29.1不同。但两个版本之间的变更只是拆分操作,因此版本0.29.1中的类似修改路径将包含在文件src/www.example.com中的方法MarionetteHandler::start_browser * 中。 marionette.rs.
1.由于我的出发点是生活是复杂的answer,请查看它以获得更多信息。

envsm3lx

envsm3lx3#

默认情况下,如果没有设置任何选项,新的驱动程序会创建一个新的配置文件。要使用现有的配置文件,一种方法是在创建firefox驱动程序之前设置系统属性webdriver.firefox.profile。下面是一个可以创建firefox驱动程序的小代码片段(假设你有geckodriver的位置和firefox配置文件):

System.setProperty("webdriver.gecko.driver","path_to_gecko_driver");
System.setProperty("webdriver.firefox.profile", "path_to_firefox_profile");
WebDriver driver = new FirefoxDriver();

您甚至可以使用env.变量设置这些系统属性,而不必在任何地方定义它们。
另一种方法是使用FirefoxOptions类,它允许你配置很多选项。首先,看看org.openqa.selenium.firefox.FirefoxDriverorg.openqa.selenium.firefox.FirefoxOptions。一个小例子:

FirefoxOptions options = new FirefoxOptions();
options.setProfile(new FirefoxProfile(new File("path_to_your_profile")));
WebDriver driver = new FirefoxDriver(options);

希望这是有帮助的。

bqjvbblv

bqjvbblv4#

您可以创建干净的firefox profile,并将其命名为SELENIUM
因此,当初始化Webdriver时,获取您已经通过代码创建的配置文件,这样它就不会一直创建任何新的临时配置文件。

ProfilesIni allProfiles = new ProfilesIni();
 FirefoxProfile desiredProfile = allProfiles.getProfile("SELENIUM");
 WebDriver driver = new FirefoxDriver(desiredProfile);

这样,您就可以确保在执行测试的任何时候都会使用这个概要文件。

  • -阿俊
kcrjzv8t

kcrjzv8t5#

你可以用以下方法来解决这个问题

FirefoxProfile profile = new FirefoxProfile(new File("D:\\Selenium Profile..."));                  

    WebDriver driver = new FirefoxDriver(profile);

还有一个选项,但它继承了配置文件以前使用的所有cookie、缓存内容等,让我们看看它会是什么样子--

System.setProperty("webdriver.firefox.profile", "MySeleniumProfile");

    WebDriver driver = new FirefoxDriver(...);

希望这能简短地回答你的问题。

sgtfey8w

sgtfey8w6#

我已经提出了一个解决方案,它1)可以与Selenium 4. 7. 0一起工作--然而,我不明白为什么它不能与3. x一起工作,2)允许用户通过环境变量动态地传递现有的Firefox配置文件--如果这个环境变量不存在,就简单地"正常"工作,3)如果您不想要配置文件目录的临时副本,不要将源概要文件目录传递给Selenium。
我下载了Geckodriver 0.32.0并进行了修改,这样您只需通过环境变量FIREFOX_PROFILE_DIR提供Firefox配置文件目录。例如,在C#中,在创建FirefoxDriver之前,请调用:

Environment.SetEnvironmentVariable("FIREFOX_PROFILE_DIR", myProfileDir);

browser.rs第88行中的内容变更为Rust,替换为:

let mut profile = match options.profile {
        ProfileType::Named => None,
        ProfileType::Path(x) => Some(x),
        ProfileType::Temporary => Some(Profile::new(profile_root)?),
    };

与:

let mut profile = if let Ok(profile_dir) = std::env::var("FIREFOX_PROFILE_DIR") {
        Some(Profile::new_from_path(Path::new(&profile_dir))?)
    } else {
        match options.profile {
            ProfileType::Named => None,
            ProfileType::Path(x) => Some(x),
            ProfileType::Temporary => Some(Profile::new(profile_root)?),
        }
    };

您可以参考我的Git commit来查看与原始Geckodriver代码的差异。

相关问题