I want to use, on a webdriver Firefox instance, the "new tab instead of window" option.
1/ I created a profile with this option on, but when I use the profile a lot of options are OK but not this one.
2/ After the load of the profile I tried to change the option in the code but it does n't work.
My code :
profile = webdriver.FirefoxProfile(os.path.join(s_path, name))
profile.set_preference("browser.link.open_newwindow.restriction", 0)
profile.set_preference("browser.link.open_newwindow", 3)
profile.set_preference("browser.link.open_external", 3)
profile.set_preference("browser.startup.homepage","http://www.google.fr")
profile.update_preferences()
print(os.path.join(s_path, name))
driver = webdriver.Firefox(set_profile())
All is OK (the start homepage is google.fr) except this option which is not OK.
It seems that Selenium copy the profile in a temp dir. where users.js have the wrong line :
user_pref("browser.link.open_newwindow", 2);
Python 3.4.2, Windows 7, Firefox 39.0, Selenium lib 2.46
From what I've researched, browser.link.open_newwindow is a frozen setting and it's always synced with the value 2. If you dig up the source of the selenium Python bindings, you would find a set of frozen settings that is applied after your custom settings are set.
Note that in java bindings this set of default frozen settings is explicitly hardcoded:
/**
* Profile preferences that are essential to the FirefoxDriver operating correctly. Users are not
* permitted to override these values.
*/
private static final ImmutableMap<String, Object> FROZEN_PREFERENCES =
ImmutableMap.<String, Object>builder()
.put("app.update.auto", false)
.put("app.update.enabled", false)
.put("browser.download.manager.showWhenStarting", false)
.put("browser.EULA.override", true)
.put("browser.EULA.3.accepted", true)
.put("browser.link.open_external", 2)
.put("browser.link.open_newwindow", 2) // here it is
// ...
And a bit of an explanation coming from Firefox only supports windows not tabs:
This is a known issue and unfortunately we will not be supporting tabs.
We force Firefox to open all links in a new window. We can't access
the tabs to know when to switch. When we move to marionette (Mozilla
project) in the future we should be able to do this but for now it is
working as intended
A workaround solution would be to change the target of a link manually - may not work in all of the cases depending on how a new link is opened.
"browser.link.open_newwindow" is a frozen preference, which means that it can't be modified using profile.set_preference("browser.link.open_newwindow", 3)
The solution is to use profile.DEFAULT_PREFERENCES["frozen"]["browser.link.open_newwindow"] = 3 instead. (the other non-frozen preferences can be set with set_preference without problem)
Related
I am trying to do what the title says, using Python's Selenium Webdriver, with an existing Firefox profile living in <PROFILE-DIR>.
What I've tried
used the options.profile option on the driver:
#!/usr/bin/env python
from selenium.webdriver.firefox.firefox_profile import FirefoxProfile
from selenium.webdriver import Firefox, DesiredCapabilities
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.firefox.options import Options
options = Options()
options.profile = '<PROFILE_DIR>'
webdriver = Firefox(options=options)
This copies the existing profile to a temporary location. I can see it works because the new session I start has access to the profile's old cookies, etc. But it is not what I am after: I want to use the profile in-place.
tried to pass the profile as '--profile ' to the args capability: change the code above to
capabilities = DesiredCapabilities.FIREFOX.copy()
capabilities['args'] = '--profile <PROFILE-DIR>'
webdriver = Firefox(desired_capabilities=capabilities)
Nothing doing: viewing the geckodriver.log after closing the session still shows me something like Running command: "/usr/bin/firefox" "--marionette" "-foreground" "-no-remote" "-profile" "/tmp/rust_mozprofileOFKY46", i.e. still using a temporary profile (which is not even a copy of the existing one; the old cookies are not there).
tried the same with capabilities['args'] = ['-profile', '<PROFILE-DIR>'] instead (i.e. a list of strings instead of a single string); same result.
read a bunch of other SO posts, none of which do it. This is mostly because they're specific to Chrome (to whose driver you can apparently pass command-line options; I haven't seen something like this for geckodriver), or because they fall back to copies of existing profiles.
The most relevant answer in this direction implements essentially the same hack I'd thought of, in a pinch:
start the driver with a copy of your existing profile using options.profile as described above;
close the driver instance manually when done (e.g. with a Ctrl+C, or SIGINT) so that the temp-profile directory isn't deleted;
copy over all of the stuff that's left on top of the existing profile, giving you access to whatever leftovers you wanted from the automated session.
This is ugly and feels unnecessary. Besides, geckodriver's failure to remove temporary profiles (which I'd be relying on) is considered a bug..
Surely I'm misunderstanding how to pass those capability options mentioned above, or some such thing. But the docs could do a better job of giving examples.
I've come up with a solution that allows the user to use a Firefox profile in place, by passing the profile path dynamically via an environment variable to Geckodriver.
I start by downloading 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.
The code change is in src/browser.rs, line 88, replacing:
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.
I want a way to find the default browser in Python. I've seen https://docs.python.org/3.8/library/webbrowser.html
and there might be a way of converting a webbrowser instance which I do not know.
Does anybody know how I could go about doing that?
Also, I'm using Windows - it doesn't need to work for Mac or Ubuntu.
Edit:
I've already tried this website and it gives me an error saying that 'the file path does not exist'.
Additionally, I don't want to open a tab in the default browser, I just want the name.
Edit #2:
The following code works but returns Internet Explorer instead of my actual default which is Chrome.
from winreg import*
with OpenKey(HKEY_CLASSES_ROOT,r"http\\shell\\open\\command") as key:
cmd = QueryValue(key, None)
I have figured it out. I'm pretty sure this only works on Windows, but here's the code:
from winreg import *
with OpenKey(HKEY_CURRENT_USER, r"Software\\Microsoft\\Windows\\Shell\\Associations\\UrlAssociations\\http\\UserChoice") as key:
browser = QueryValueEx(key, 'Progid')[0]
It will return ChromeHTML for Chrome, FirefoxURL for Firefox, and IE.HTTP for Internet Explorer.
You can try this
import webbrowser
default_browser = webbrowser.get()
default_browser_name = default_browser.name
default_browser_basename = default_browser.basename
As the docs say
webbrowser.get([name])
Return a controller object for the browser type name. If name is empty, return a controller for a default browser appropriate to the caller’s environment.
In Selenium IDE it tells me to use the command
selectWindow
and that the target is:
name={"id":"a52b0dc5-b1f5-2020-01db-139f01c0204a","containerVersion":"7-GA","webContextPath":"/345","preferenceLocation":"https:/url/345/prefs","relayUrl":"https:/url/o345/is/eas/rpc_relay.uncompressed.html","lang":"en_US","currentTheme":{"themeName":"a_default","themeContrast":"standard","themeFontSize":12},"345":true,"layout":"desktop","url":"/brvt","guid":"260f0022-66de-a78b-3ce8-8de63a3bdbec","version":1,"locked":false}
How do I convert this in python using driver.switch_to_window ?
I have tried:
driver.switch_to_window(driver.find_element_by_name("{"id":"a52b0dc5-b1f5-2020-01db-139f01c0204a","containerVersion":"7-GA","webContextPath":"/345","preferenceLocation":"https:/url/345/prefs","relayUrl":"https:/url/o345/is/eas/rpc_relay.uncompressed.html","lang":"en_US","currentTheme":{"themeName":"a_default","themeContrast":"standard","themeFontSize":12},"345":true,"layout":"desktop","url":"/brvt","guid":"260f0022-66de-a78b-3ce8-8de63a3bdbec","version":1,"locked":false}"))
Suggestions?
A painfully easy method to switch between windows (2 ideally) gist.github.com/ab9-er/08c3ce7d2d5cdfa94bc7.
def change_window(browser):
"""
Simple window switcher without the need of playing with ids.
#param browser: Current browser instance
"""
curr = browser.current_window_handle
all_handles = browser.window_handles
for handle in list(set([curr]) - set(all_handles)):
return browser.switch_to_window(handle)
My script runs a command every X seconds.
If a command is like "start www" -> opens a website in a default browser I want to be able to close the browser before next time the command gets executed.
This short part of a script below:
if "start www" in command:
time.sleep(interval - 1)
os.system("Taskkill /IM chrome.exe /F")
I want to be able to support firefox, ie, chrome and opera, and only close the browser that opened by URL.
For that I need to know which process to kill.
How can I use python to identify my os`s default browser in windows?
The solution is going to differ from OS to OS. On Windows, the default browser (i.e. the default handler for the http protocol) can be read from the registry at:
HKEY_CURRENT_USER\Software\Classes\http\shell\open\command\(Default)
Python has a module for dealing with the Windows registry, so you should be able to do:
from _winreg import HKEY_CURRENT_USER, OpenKey, QueryValue
# In Py3, this module is called winreg without the underscore
with OpenKey(HKEY_CURRENT_USER,
r"Software\Classes\http\shell\open\command") as key:
cmd = QueryValue(key, None)
You'll get back a command line string that has a %1 token in it where the URL to be opened should be inserted.
You should probably be using the subprocess module to handle launching the browser; you can retain the browser's process object and kill that exact instance of the browser instead of blindly killing all processes having the same executable name. If I already have my default browser open, I'm going to be pretty cheesed if you just kill it without warning. Of course, some browsers don't support multiple instances; the second instance just passes the URL to the existing process, so you may not be able to kill it anyway.
I would suggest this. Honestly Python should include this in the webbrowser module that unfortunately only does an open bla.html and that breaks anchors on the file:// protocol.
Calling the browser directly however works:
# Setting fallback value
browser_path = shutil.which('open')
osPlatform = platform.system()
if osPlatform == 'Windows':
# Find the default browser by interrogating the registry
try:
from winreg import HKEY_CLASSES_ROOT, HKEY_CURRENT_USER, OpenKey, QueryValueEx
with OpenKey(HKEY_CURRENT_USER, r'SOFTWARE\Microsoft\Windows\Shell\Associations\UrlAssociations\http\UserChoice') as regkey:
# Get the user choice
browser_choice = QueryValueEx(regkey, 'ProgId')[0]
with OpenKey(HKEY_CLASSES_ROOT, r'{}\shell\open\command'.format(browser_choice)) as regkey:
# Get the application the user's choice refers to in the application registrations
browser_path_tuple = QueryValueEx(regkey, None)
# This is a bit sketchy and assumes that the path will always be in double quotes
browser_path = browser_path_tuple[0].split('"')[1]
except Exception:
log.error('Failed to look up default browser in system registry. Using fallback value.')
I'm using the Selenium webdriver (in Python) to automate the donwloading of thousands of files. I want the files to be saved in different folders. The following code works, but it requires quitting and re-starting the webdriver multiple times, which slows down the process:
some_list = ["item1", "item2", "item3"] # over 300 items on the actual code
for item in some_list:
download_folder = "/Users/myusername/Desktop/" + item
os.makedirs(download_folder)
fp = webdriver.FirefoxProfile()
fp.set_preference("browser.download.folderList", 2)
fp.set_preference("browser.download.manager.showWhenStarting", False)
fp.set_preference("browser.download.dir", download_folder)
fp.set_preference("browser.helperApps.neverAsk.saveToDisk", "text/plain")
browser = webdriver.Firefox(firefox_profile = fp)
# a bunch of code that accesses the site and downloads the files
browser.close()
browser.quit()
So, at every iteration I have to quit the webdriver and re-start it, which is pretty innefficient. Is there a better way to do this? Apparently we can't change the Firefox profile after the webdriver is instantiated (see this and this previous questions), but perhaps there is some alternative I'm missing?
(Mac OS X 10.6.8, Python 2.7.5, Selenium 2.2.0)
No, I don't think you can do it.
Option one: specify different default directories for one FirefoxProfile
You can't. In my opinion, this is the issue with Firefox, not Selenium. However, this Firefox limitation looks like the correct design to me. browser.download.dir is the default download destination, if it allows multiple directories, then that's not "default" anymore.
Option two: switch multiple FirefoxProfile for one driver instance
If not doing it in Firefox, can FirefoxProfile be switched for same driver instance? As far as I know, the answer is no. (You have already done some research on this)
Option three: use normal non-Selenium way to do the downloading
If you want to avoid using this auto-downloading approach and do it the normal way (like Auto-it etc.), then it falls in the category of "How To Download Files With Selenium And Why You Shouldn’t". But in this case, your code can be simplified.
some_list = ["item1", "item2", "item3"] # over 300 items on the actual code
for item in some_list:
download_folder = "/Users/myusername/Desktop/" + item
some_way_magically_do_the_downloading(download_folder)