简体   繁体   English

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

[英]How to Prevent Selenium 3.0 (Geckodriver) from Creating Temporary Firefox Profiles?

I'm running the latest version of Selenium WebDriver with Geckodriver .我正在使用 Geckodriver 运行最新版本的Selenium WebDriver I want to prevent Selenium from creating temporary Firefox Profiles in the temporary files directory when launching a new instance of WebDriver .我想防止Selenium在启动WebDriver的新实例时在临时文件目录中创建临时 Firefox 配置文件。 Instead I want to use the original Firefox Profile directly.相反,我想直接使用原始的 Firefox 配置文件。 This has double benefit.这有双重好处。 First, it saves time (it takes significant amount of time for the profile to be copied to the temporary directory).首先,它节省了时间(将配置文件复制到临时目录需要花费大量时间)。 Second, it ensures that cookies created during session are saved to the original profile.其次,它确保将在 session 期间创建的 cookies 保存到原始配置文件中。 Before Selenium started relying on Geckodriver I was able to solve this problem by editing the class FirefoxProfile.class in SeleniumHQ as seen below:在 Selenium 开始依赖Geckodriver之前,我能够通过在SeleniumHQ中编辑 class FirefoxProfile.class解决这个问题,如下所示:

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);
  }
 }
}

This would stop Selenium from creating a temporary Firefox Profile when the parameter disableTempProfileCreation was set to true.当参数disableTempProfileCreation设置为 true 时,这将阻止 Selenium 创建临时 Firefox 配置文件。

However, now that Selenium is being controlled by Geckodriver this solution no longer works as the creation (and launch) of Firefox Profile is controlled by Geckodriver.exe (which is written in Rust language).但是,现在 Selenium 由Geckodriver控制,此解决方案不再有效,因为 Firefox 配置文件的创建(和启动)由Geckodriver.exe (用Rust语言编写)控制。 How can I achieve the same objective with Geckodriver ?如何使用Geckodriver实现相同的目标? I don't mind editing the source code.我不介意编辑源代码。 I'm using Java.我正在使用 Java。

Thanks谢谢

Important Update:重要更新:

I would like to thank everyone for taking the time to respond to this question.我要感谢大家花时间回答这个问题。 However, as stated in some of the comments, the first 3 answers do not address the question at all - for two reasons.但是,正如某些评论中所述,前 3 个答案根本没有解决问题- 原因有二。 First of all, using an existing Firefox Profile will not prevent Geckodriver from copying the original profile to a temporary directory (as indicated in the OP and clearly stated by one or more of the commentators below).首先,使用现有的 Firefox 配置文件不会阻止Geckodriver将原始配置文件复制到临时目录(如 OP 中所示,并由下面的一位或多位评论员明确说明)。 Second, even if it did it is not compatible with Selenium 3.0.其次,即使它与 Selenium 3.0 不兼容。

I'm really not sure why 3 out of 4 answer repeat the exact same answer with the exact same mistake.我真的不确定为什么 4 个答案中有 3 个会重复完全相同的答案并犯同样的错误。 Did they read the question?他们读过问题了吗? The only answer the even attempts to address the question at hand is the answer by @Life is complex however it is incomplete.甚至尝试解决手头问题的唯一答案是@Life 的答案很复杂但不完整。 Thanks.谢谢。

UPDATED POST 05-30-2021 2021 年 5 月 30 日更新后


This is the hardest question that I have every tried to answer on Stack Overflow.这是我在 Stack Overflow 上尝试过的最难回答的问题。 Because it involved the interactions of several code bases written in multiple languages (Java, Rust and C++).因为它涉及使用多种语言(Java、Rust 和 C++)编写的多个代码库的交互。 This complexity made the question potentially unsolvable.这种复杂性使这个问题可能无法解决。

My last crack at this likely unsolvable question:我最后一次破解这个可能无法解决的问题:

Within the code in your question you are modifying the file user.js This file is still used by Selenium .在您问题的代码中,您正在修改文件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);
    }
  }

So in theory you should be able to modify capabilities.rs in the geckodriver source code.所以理论上你应该可以修改geckodriver源代码中的capabilities.rs That file contains the temp_dir .该文件包含temp_dir

As I stated this in only a theory, because when I looked at the Firefox source, which has temp_dir spread throughout the code base.正如我在理论上所说的那样,因为当我查看 Firefox 源时,它的temp_dir分布在整个代码库中。

ORIGINAL POST 05-26-2021原帖 05-26-2021


I'm not sure that you can prevent Selenium from creating a temporary Firefox Profile.我不确定您是否可以阻止 Selenium 创建临时 Firefox 配置文件。

From the gecko documents :壁虎文件

"Profiles are created in the systems temporary folder. This is also where the encoded profile is extracted when profile is provided. By default geckodriver will create a new profile in this location ." “配置文件是在系统临时文件夹中创建的。这也是在提供配置文件时提取编码配置文件的位置。默认情况下,geckodriver 将在此位置创建一个新配置文件。”

The only solution that I see at the moment would require you modify the Geckodriver source files to prevent the creation of temporary folders/profiles.我目前看到的唯一解决方案是要求您修改 Geckodriver 源文件以防止创建临时文件夹/配置文件。

I'm currently looking at the source.我目前正在查看源代码。 These files might be the correct ones, but I need to look at the source more:这些文件可能是正确的,但我需要更多地查看源代码:

Here are some other files that need to be combed through:以下是一些需要梳理的其他文件:

https://searchfox.org/mozilla-central/search?q=tempfile&path= https://searchfox.org/mozilla-central/search?q=tempfile&path=


This looks promising:这看起来很有希望:

https://searchfox.org/mozilla-central/source/testing/geckodriver/doc/Profiles.md https://searchfox.org/mozilla-central/source/testing/geckodriver/doc/Profiles.md

"geckodriver uses [profiles] to instrument Firefox' behaviour. The user will usually rely on geckodriver to generate a temporary, throwaway profile. These profiles are deleted when the WebDriver session expires. “geckodriver 使用 [profiles] 来检测 Firefox 的行为。用户通常会依赖 geckodriver 生成临时的一次性配置文件。当 WebDriver session 过期时,这些配置文件将被删除。

In cases where the user needs to use custom, prepared profiles, geckodriver will make modifications to the profile that ensures correct behaviour.如果用户需要使用自定义的、准备好的配置文件,geckodriver 将对配置文件进行修改以确保正确的行为。 See [ Automation preferences ] below on the precedence of user-defined preferences in this case.在这种情况下,有关用户定义的首选项的优先级,请参阅下面的 [自动化首选项]。

Custom profiles can be provided two different ways:可以通过两种不同的方式提供自定义配置文件:

1. by appending --profile /some/location to the [ args capability], which will instruct geckodriver to use the profile in-place ; 1. 通过将--profile /some/location附加到 [ args功能],这将指示 geckodriver就地使用配置文件;

I found this question on trying to do this: how do I use an existing profile in-place with Selenium Webdriver?我在尝试这样做时发现了这个问题: 如何使用 Selenium Webdriver 就地使用现有配置文件?

Also here is an issue that was raised in selenium on Github concerning the temp directory.这里还有一个在 Github 上的 selenium 中提出的关于临时目录的问题。 https://github.com/SeleniumHQ/selenium/issues/8645 https://github.com/SeleniumHQ/selenium/issues/8645


Looking through the source of geckodriver v0.29.1 I found a file where the profile is loaded.查看geckodriver v0.29.1的源代码,我发现了一个加载配置文件的文件。

source: capabilities.rs来源: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)
        }
    }

source: marionette.rs资料来源: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),
            )
        })
    }
}

After looking through the gecko source it looks like mozprofile::profile::Profile is coming from FireFox and not geckodriver查看壁虎源后,看起来mozprofile::profile::Profile来自 FireFox 而不是 geckodriver


It seems that you might have issues with profiles when you migrate to Selenium 4.当您迁移到 Selenium 4 时,您似乎可能会遇到配置文件问题。

ref: https://github.com/SeleniumHQ/selenium/issues/9417参考: https://github.com/SeleniumHQ/selenium/issues/9417

For Selenium 4 we have deprecated the use of profiles as there are other mechanisms that we can do to make the start up faster.对于 Selenium 4,我们已弃用配置文件,因为我们可以采取其他机制来加快启动速度。 Please use the Options class to set preferences that you need and if you need to use an addon use the driver.install_addon("path/to/addon") you can install selenium 4, which is in beta, via pip install selenium --pre Please use the Options class to set preferences that you need and if you need to use an addon use the driver.install_addon("path/to/addon") you can install selenium 4, which is in beta, via pip install selenium --预


I noted in your code you were writing to user.js , which is a custom file for FireFox.我在您的代码中注意到您正在写入user.js ,这是 FireFox 的自定义文件。 Have you considered creating on these files manually outside of Gecko?您是否考虑过在 Gecko 之外手动创建这些文件?

Also have you looked atmozprofile ?你也看过mozprofile吗?

The new driver by default creates a new profile if no options are set.如果未设置任何选项,则默认情况下新驱动程序会创建一个新配置文件。 To use a existing profile, one way to do this is to set the system property webdriver.firefox.profile before creating the firefox driver.要使用现有配置文件,一种方法是在创建 firefox 驱动程序之前设置系统属性webdriver.firefox.profile A small code snippet that can create a firefox driver (given you have locations for geckodriver, and the 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();

You could even set these system properties using the env.您甚至可以使用 env.xml 设置这些系统属性。 variables and skip defining them everywhere.变量并跳过在任何地方定义它们。

Another way to do this is to use the FirefoxOptions class which allows you to configure a lot of options.另一种方法是使用 FirefoxOptions class,它允许您配置很多选项。 To start with, take a look at org.openqa.selenium.firefox.FirefoxDriver and org.openqa.selenium.firefox.FirefoxOptions . To start with, take a look at org.openqa.selenium.firefox.FirefoxDriver and org.openqa.selenium.firefox.FirefoxOptions . A small example:一个小例子:

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

Hope this is helpful.希望这会有所帮助。

You can create firefox profile which will be clean and name it as SELENIUM您可以创建 firefox 配置文件,该配置文件将是干净的并将其命名为SELENIUM

So When initializing the Webdriver get the profile which you have already created through the code, so that it wont create any new temp profiles all the time.因此,在初始化 Webdriver 时,获取您已经通过代码创建的配置文件,这样它就不会一直创建任何新的临时配置文件。

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

That way, you assure that this profile will be used anytime you do the tests.这样,您就可以确保在您进行测试的任何时候都会使用此配置文件。

-Arjun -阿琼

You can handle this by using --您可以使用 --

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

    WebDriver driver = new FirefoxDriver(profile);

There is one more option but it inherits all the cookies, cache contents, etc. of the previous uses of the profile let's see how it will be --还有一个选项,但它继承了以前使用配置文件的所有 cookies、缓存内容等,让我们看看它将如何——

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

    WebDriver driver = new FirefoxDriver(...);

Hope this answers your question in short.希望这能简短地回答您的问题。

Thanks to source code provided in answer of Life is complex in link .. I have the chance to look through geckodriver source.感谢Life is complex in link的答案中提供的源代码。我有机会浏览 geckodriver 源代码。

EXPLANATION解释

I believe that the reason you could not find out any rust_tmp in source because it is generated randomly by Profile::new() function.我相信您在源代码中找不到任何rust_tmp的原因是因为它是由Profile::new() function 随机生成的。

When I look deeper in code structure, I saw that browser.rs is the place where the browser is actually loaded which is called through marionette.rs .当我深入研究代码结构时,我发现browser.rs是实际加载浏览器的地方,通过marionette.rs调用。 If you noticing carefully, LocalBrowser::new method will be called whenever a new session is initialized and the profile will be loaded in that state also.如果您仔细注意,每当初始化新的 session 时都会调用LocalBrowser::new方法,并且配置文件也将加载到 state 中。 Then by checking browser.rs file, there will be a block code line 60 - 70 used to actually generate profile for new session instance.然后通过检查browser.rs文件,将有一个块代码行 60 - 70 用于实际为新的 session 实例生成配置文件。 Now, what need to do is modifying this path to load your custom profile.现在,需要做的是修改此路径以加载您的自定义配置文件。

SHORT ANSWER简短的回答

Downloading zip file of geckodriver-0.30.0 , extracting it by your prefer zip program:P下载 geckodriver -0.30.0的 zip 文件,通过您喜欢的 zip 程序提取它:P

Looking on src/browser.rs of geckodriver source, in line 60 - 70, hoping you will see something like this:查看 geckodriver 源代码的src/browser.rs ,在第 60 - 70 行,希望您会看到如下内容:

        let is_custom_profile = options.profile.is_some();

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

Change it to your prefer folder ( hoping you know some rust code ), example:将其更改为您喜欢的文件夹(希望您知道一些 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)?;

Re-compile with prefer rust compiler, example:使用 prefer rust 编译器重新编译,例如:

Cargo build

NOTE笔记

Hoping this info will help you someway.希望这些信息对您有所帮助。 This is not comprehensive but hoping it is good enough hint for you like it is possible to write some extra code to load profile from env or pass from argument, it is possible but I'm not rust developer so too lazy for providing one in here.这并不全面,但希望它对您来说足够好,比如可以编写一些额外的代码来从 env 加载配置文件或从参数传递,这是可能的,但我不是 rust 开发人员,所以懒得在这里提供一个.

The above solution is work fine for me and I could load and use directly my profile from that.上述解决方案对我来说很好,我可以从中直接加载和使用我的个人资料。 Btw, I work on Archlinux with rust info: cargo 1.57.0 .顺便说一句,我使用 rust info: cargo 1.57.0Archlinux上工作。

TBH, this is first time I push comment on stackoverflow, so feel free to correct me if I'm wrong or produce unclear answer:P TBH,这是我第一次对 stackoverflow 发表评论,如果我错了或给出不清楚的答案,请随时纠正我:P

Update更新

  1. I worked in geckodriver 0.30.0 which will not be the same as geckodriver 0.29.1 mentioned by Life is complex .我在geckodriver 0.30.0工作,这与Life is complex提到的geckodriver 0.29.1 不同 But the change between 2 versions just be split action, so the similar modify path in version 0.29.1 will be included in method MarionetteHandler::start_browser in file src/marionette.rs .但是 2 个版本之间的更改只是拆分操作,因此 0.29.1 版本中类似的修改路径将包含在src/marionette.rs文件中的方法MarionetteHandler::start_browser中。

  2. Since my starting point is Life is complex answer , please looking through it for more information.由于我的出发点是生活是复杂的答案,请浏览它以获取更多信息。

I've come up with a solution that 1) works with Selenium 4.7.0--however, I don't see why it wouldn't work with 3.x as well, 2) allows the user to pass in an existing Firefox profile dynamically via an environment variable--and if this environment variable doesn't exist, simply acts "normally", and 3) if you do not want a temporary copy of the profile directory, simply do not pass the source profile directory to Selenium.我想出了一个解决方案,1) 与 Selenium 4.7.0 一起工作——但是,我不明白为什么它也不能与 3.x 一起工作,2) 允许用户传入现有的 Firefox通过环境变量动态配置文件——如果此环境变量不存在,只需“正常”操作,并且 3) 如果您不想要配置文件目录的临时副本,只需不要将源配置文件目录传递给 Selenium .

I downloaded Geckodriver 0.32.0 and made it so that you simply need to provide the Firefox profile directory via the environment variable FIREFOX_PROFILE_DIR .我下载了 Geckodriver 0.32.0并制作了它,以便您只需通过环境变量FIREFOX_PROFILE_DIR提供 Firefox 配置文件目录。 For example, in C#, before you create the FirefoxDriver, call:例如,在 C# 中,在创建 FirefoxDriver 之前,调用:

Environment.SetEnvironmentVariable("FIREFOX_PROFILE_DIR", myProfileDir);

The change to Rust is in browser.rs , line 88, replacing:对 Rust 的更改在browser.rs的第 88 行中,替换为:

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

with:和:

    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)?),
        }
    };

You may refer to my Git commit to see the diff against the original Geckodriver code.您可以参考我的Git 提交以查看与原始 Geckodriver 代码的差异。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM