Can Selenium use a specific Firefox profile without making a copy
Asked Answered
S

2

7

I'm using selenium in python under Linux and have it setup to use a specific Firefox profile. That part is working fine. However, it is creating a copy of the profile in /tmp and not using the profile directory directly in the location I specify ('~/.mozilla/firefox/ki1relie.testprof') with webdriver.FirefoxProfile. Is there an option to tell selenium that I want to use the original profile directory without copying it?

Stater answered 12/3, 2018 at 12:13 Comment(4)
Possible duplicate of Is it Firefox or Geckodriver, which creates "rust_mozprofile" directoryFormally
It should be possible with the -profile argument: options.add_argument('-profile "/path to profile directory"').Pentimento
That doesn't work. I've tried multiple options and combinations of options, but none work completely.Stater
I can confirm that while this should be possible, it seems to always crash firefox.Murder
T
1

Good reading on the problem is the constructor of firefox_profile.FirefoxProfile() code. It directly states that

def __init__(self, profile_directory=None):
    """
    Initialises a new instance of a Firefox Profile
:args:
 - profile_directory: Directory of profile that you want to use. If a
   directory is passed in it will be cloned and the cloned directory
   will be used by the driver when instantiated.
   This defaults to None and will create a new
   directory when object is created.
"""

in OOP you can subclass it, if you want. like this:

import copy
import json
import os

from selenium import webdriver
from selenium.webdriver.firefox import firefox_profile
    
class MyVeryFirefoxProfile(firefox_profile.FirefoxProfile):

    def __init__(self, profile_directory=None):
        """
        Initialises a new instance of a Firefox Profile

        :args:
         - profile_directory: Directory of profile that you want to use.
           If a directory is passed it will be used as is, with no cloning attempts.
           Otherwise it will be handled as usual (new empty profile dir on user's temp)
        """
        if (profile_directory == None):
            # just pass it to super
            super().__init__(profile_directory)
            return
                    
        if not firefox_profile.FirefoxProfile.DEFAULT_PREFERENCES:
            with open(os.path.join(os.path.dirname(firefox_profile.__file__),
                                   firefox_profile.WEBDRIVER_PREFERENCES)) as default_prefs:
                firefox_profile.FirefoxProfile.DEFAULT_PREFERENCES = json.load(default_prefs)

        self.default_preferences = copy.deepcopy(
            firefox_profile.FirefoxProfile.DEFAULT_PREFERENCES['mutable'])
        self.profile_dir = profile_directory
        self.tempfolder = None # if smart enough, it will understand :-)
        #===================================================================
        # newprof = os.path.join(self.tempfolder, "webdriver-py-profilecopy")
        # shutil.copytree(self.profile_dir, newprof,
        #                 ignore=shutil.ignore_patterns("parent.lock", "lock", ".parentlock"))
        # self.profile_dir = newprof
        # os.chmod(self.profile_dir, 0o755)
        #===================================================================
        self._read_existing_userjs(os.path.join(self.profile_dir, "user.js"))
        self.extensionsDir = os.path.join(self.profile_dir, "extensions")
        self.userPrefs = os.path.join(self.profile_dir, "user.js")
        if os.path.isfile(self.userPrefs):
            os.chmod(self.userPrefs, 0o644)

def main():
    profile = MyVeryFirefoxProfile(os.path.abspath(FIREFOX_PROFILE_PATH))
    driver = webdriver.Firefox(firefox_profile=profile)
    .. blah ..
    # and no more bloating temp folders ;-)
    
    # but don't forget to fool this killer just before calling quit()
    driver.profile = None
    driver.quit()

    # otherwise your profile will be killed from disk
Transient answered 9/12, 2020 at 13:55 Comment(0)
B
0

In recent versions of Selenium and geckodriver, you must pass the desired profile directory to geckodriver using command line arguments:

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

options = Options()
options.add_argument("--profile")
options.add_argument("/path/to/profile_dir")
with webdriver.Firefox(options=options) as driver:
    ...

The answer provided, where a custom subclass is made of FirefoxProfile, does not work. Using FirefoxProfile is now deprecated in favor of Options.

If you do specify a FirefoxProfile, subclassed or not, the profile directory contents will be zipped, base64 encoded, and transferred to the geckodriver as a text string. The geckodriver application will then unzip it into a new temporary directory, defeating all of your plans.

See https://firefox-source-docs.mozilla.org/testing/geckodriver/Profiles.html for more information on geckodriver command line arguments and profile behavior.

I have confirmed that passing the directory to geckodriver via command line arguments as shown here works perfectly with Selenium 4.9.1 and Python 3.10, enabling the use of a persistent profile directory.

This is helpful, for example, when working with websites that require 2FA, but be careful that any stored cookies don't impact your tests.

Belmonte answered 6/6, 2023 at 20:13 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.