Selenium 可以与现有的浏览器会话进行交互吗?

有人知道 Selenium (最好是 WebDriver)是否能够在启动 Selenium Client 之前通过已经运行的浏览器进行通信和操作吗?

我的意思是,如果 Selenium 能够在不使用 Selenium 服务器的情况下与浏览器进行通信(例如,可以手动启动一个 Internet Explorer)。

246654 次浏览

有可能,但你得破解一下,有密码的 你需要做的是运行独立服务器和“补丁”RemoteWebDriver

public class CustomRemoteWebDriver : RemoteWebDriver
{
public static bool newSession;
public static string capPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestFiles", "tmp", "sessionCap");
public static string sessiodIdPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "TestFiles", "tmp", "sessionid");


public CustomRemoteWebDriver(Uri remoteAddress)
: base(remoteAddress, new DesiredCapabilities())
{
}


protected override Response Execute(DriverCommand driverCommandToExecute, Dictionary<string, object> parameters)
{
if (driverCommandToExecute == DriverCommand.NewSession)
{
if (!newSession)
{
var capText = File.ReadAllText(capPath);
var sidText = File.ReadAllText(sessiodIdPath);


var cap = JsonConvert.DeserializeObject<Dictionary<string, object>>(capText);
return new Response
{
SessionId = sidText,
Value = cap
};
}
else
{
var response = base.Execute(driverCommandToExecute, parameters);
var dictionary = (Dictionary<string, object>) response.Value;
File.WriteAllText(capPath, JsonConvert.SerializeObject(dictionary));
File.WriteAllText(sessiodIdPath, response.SessionId);
return response;
}
}
else
{
var response = base.Execute(driverCommandToExecute, parameters);
return response;
}
}
}

我使用的是 Rails + Cucumber + Selenium WebDriver + PhantomJS,我一直在使用一个修补过的 Selenium WebDriver 版本,该版本在测试运行期间保持 PhantomJS 浏览器处于开放状态。看看这篇博文: http://blog.sharetribe.com/2014/04/07/faster-cucumber-startup-keep-phantomjs-browser-open-between-tests/

请看我对这篇文章的回答: 如何从 Ruby 文件在已经打开的浏览器上执行命令

使用 JavaScript selenium-webdriver客户端非常简单:

首先,确保 WebDriver 服务器正在运行。例如,下载 ChromeDriver,然后运行 chromedriver --port=9515

其次,创建驱动程序 像这样:

var driver = new webdriver.Builder()
.withCapabilities(webdriver.Capabilities.chrome())
.usingServer('http://localhost:9515')  // <- this
.build();

这里有一个完整的例子:

Var 网络驱动程序 = 要求(‘ selenium-网络驱动程序’) ;

var driver = new webdriver.Builder()
.withCapabilities(webdriver.Capabilities.chrome())
.usingServer('http://localhost:9515')
.build();


driver.get('http://www.google.com');
driver.findElement(webdriver.By.name('q')).sendKeys('webdriver');
driver.findElement(webdriver.By.name('btnG')).click();
driver.getTitle().then(function(title) {
console.log(title);
});


driver.quit();

这是一个重复的答案 重新连接到 python selenium 中的驱动程序 * * 这适用于所有驱动程序和 java api。

  1. 打开司机
driver = webdriver.Firefox()  #python
  1. 从驱动程序对象提取到 session _ id 和 _ url。
url = driver.command_executor._url       #"http://127.0.0.1:60622/hub"
session_id = driver.session_id            #'4e167f26-dc1d-4f51-a207-f761eaf73c31'
  1. 使用这两个参数连接到您的驱动程序。
driver = webdriver.Remote(command_executor=url,desired_capabilities={})
driver.close()   # this prevents the dummy browser
driver.session_id = session_id

你又和你的司机连上了。

driver.get("http://www.mrsmart.in")

到目前为止,所有的解决方案都缺乏某些功能。 我的解决办法是:

public class AttachedWebDriver extends RemoteWebDriver {


public AttachedWebDriver(URL url, String sessionId) {
super();
setSessionId(sessionId);
setCommandExecutor(new HttpCommandExecutor(url) {
@Override
public Response execute(Command command) throws IOException {
if (command.getName() != "newSession") {
return super.execute(command);
}
return super.execute(new Command(getSessionId(), "getCapabilities"));
}
});
startSession(new DesiredCapabilities());
}
}

Javascript 解决方案:

我已经成功地使用这个函数附加到现有的浏览器会话

webdriver.WebDriver.attachToSession(executor, session_id);

文档可以找到 给你

我在 python 中得到了一个解决方案,我修改了基于我发现的 PersisenBrowser 类的 webDriver 类。

Https://github.com/axelpalmerin/personal/commit/fabddb38a39f378aa113b0cb8d33391d5f91dca5

替换 webDriver 模块/usr/local/lib/python2.7/dist-package/selenium/webDriver/remote/webdriver.py

用途:

from selenium.webdriver.common.desired_capabilities import DesiredCapabilities


runDriver = sys.argv[1]
sessionId = sys.argv[2]


def setBrowser():
if eval(runDriver):
webdriver = w.Remote(command_executor='http://localhost:4444/wd/hub',
desired_capabilities=DesiredCapabilities.CHROME,
)
else:
webdriver = w.Remote(command_executor='http://localhost:4444/wd/hub',
desired_capabilities=DesiredCapabilities.CHROME,
session_id=sessionId)


url = webdriver.command_executor._url
session_id = webdriver.session_id
print url
print session_id
return webdriver

受 Eric 回答的启发,下面是我对硒3.7.0的这个问题的解决方案。与 http://tarunlalwani.com/post/reusing-existing-browser-session-selenium/的解决方案相比,其优点是每次连接到现有会话时不会出现一个空白的浏览器窗口。

import warnings


from selenium.common.exceptions import WebDriverException
from selenium.webdriver.remote.errorhandler import ErrorHandler
from selenium.webdriver.remote.file_detector import LocalFileDetector
from selenium.webdriver.remote.mobile import Mobile
from selenium.webdriver.remote.remote_connection import RemoteConnection
from selenium.webdriver.remote.switch_to import SwitchTo
from selenium.webdriver.remote.webdriver import WebDriver




# This webdriver can directly attach to an existing session.
class AttachableWebDriver(WebDriver):
def __init__(self, command_executor='http://127.0.0.1:4444/wd/hub',
desired_capabilities=None, browser_profile=None, proxy=None,
keep_alive=False, file_detector=None, session_id=None):
"""
Create a new driver that will issue commands using the wire protocol.


:Args:
- command_executor - Either a string representing URL of the remote server or a custom
remote_connection.RemoteConnection object. Defaults to 'http://127.0.0.1:4444/wd/hub'.
- desired_capabilities - A dictionary of capabilities to request when
starting the browser session. Required parameter.
- browser_profile - A selenium.webdriver.firefox.firefox_profile.FirefoxProfile object.
Only used if Firefox is requested. Optional.
- proxy - A selenium.webdriver.common.proxy.Proxy object. The browser session will
be started with given proxy settings, if possible. Optional.
- keep_alive - Whether to configure remote_connection.RemoteConnection to use
HTTP keep-alive. Defaults to False.
- file_detector - Pass custom file detector object during instantiation. If None,
then default LocalFileDetector() will be used.
"""
if desired_capabilities is None:
raise WebDriverException("Desired Capabilities can't be None")
if not isinstance(desired_capabilities, dict):
raise WebDriverException("Desired Capabilities must be a dictionary")
if proxy is not None:
warnings.warn("Please use FirefoxOptions to set proxy",
DeprecationWarning)
proxy.add_to_capabilities(desired_capabilities)
self.command_executor = command_executor
if type(self.command_executor) is bytes or isinstance(self.command_executor, str):
self.command_executor = RemoteConnection(command_executor, keep_alive=keep_alive)


self.command_executor._commands['GET_SESSION'] = ('GET', '/session/$sessionId')  # added


self._is_remote = True
self.session_id = session_id  # added
self.capabilities = {}
self.error_handler = ErrorHandler()
self.start_client()
if browser_profile is not None:
warnings.warn("Please use FirefoxOptions to set browser profile",
DeprecationWarning)


if session_id:
self.connect_to_session(desired_capabilities)  # added
else:
self.start_session(desired_capabilities, browser_profile)


self._switch_to = SwitchTo(self)
self._mobile = Mobile(self)
self.file_detector = file_detector or LocalFileDetector()


self.w3c = True  # added hardcoded


def connect_to_session(self, desired_capabilities):
response = self.execute('GET_SESSION', {
'desiredCapabilities': desired_capabilities,
'sessionId': self.session_id,
})
# self.session_id = response['sessionId']
self.capabilities = response['value']

使用方法:

if use_existing_session:
browser = AttachableWebDriver(command_executor=('http://%s:4444/wd/hub' % ip),
desired_capabilities=(DesiredCapabilities.INTERNETEXPLORER),
session_id=session_id)
self.logger.info("Using existing browser with session id {}".format(session_id))
else:
browser = AttachableWebDriver(command_executor=('http://%s:4444/wd/hub' % ip),
desired_capabilities=(DesiredCapabilities.INTERNETEXPLORER))
self.logger.info('New session_id  : {}'.format(browser.session_id))

此代码片段成功地允许重用现有的浏览器实例,同时避免了引发重复的浏览器。

from selenium import webdriver
from selenium.webdriver.remote.webdriver import WebDriver


# executor_url = driver.command_executor._url
# session_id = driver.session_id


def attach_to_session(executor_url, session_id):
original_execute = WebDriver.execute
def new_command_execute(self, command, params=None):
if command == "newSession":
# Mock the response
return {'success': 0, 'value': None, 'sessionId': session_id}
else:
return original_execute(self, command, params)
# Patch the function before creating the driver object
WebDriver.execute = new_command_execute
driver = webdriver.Remote(command_executor=executor_url, desired_capabilities={})
driver.session_id = session_id
# Replace the patched function with original function
WebDriver.execute = original_execute
return driver


bro = attach_to_session('http://127.0.0.1:64092', '8de24f3bfbec01ba0d82a7946df1d1c3')
bro.get('http://ya.ru/')

似乎硒并不正式支持这个特性。但是,Tarun Lalwani 已经创建了可用的 Java 代码来提供这个特性。参考 -http://tarunlalwani.com/post/reusing-existing-browser-session-selenium-java/

下面是从上面链接复制的工作示例代码:

import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.remote.*;
import org.openqa.selenium.remote.http.W3CHttpCommandCodec;
import org.openqa.selenium.remote.http.W3CHttpResponseCodec;


import java.io.IOException;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.Collections;


public class TestClass {
public static RemoteWebDriver createDriverFromSession(final SessionId sessionId, URL command_executor){
CommandExecutor executor = new HttpCommandExecutor(command_executor) {


@Override
public Response execute(Command command) throws IOException {
Response response = null;
if (command.getName() == "newSession") {
response = new Response();
response.setSessionId(sessionId.toString());
response.setStatus(0);
response.setValue(Collections.<String, String>emptyMap());


try {
Field commandCodec = null;
commandCodec = this.getClass().getSuperclass().getDeclaredField("commandCodec");
commandCodec.setAccessible(true);
commandCodec.set(this, new W3CHttpCommandCodec());


Field responseCodec = null;
responseCodec = this.getClass().getSuperclass().getDeclaredField("responseCodec");
responseCodec.setAccessible(true);
responseCodec.set(this, new W3CHttpResponseCodec());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}


} else {
response = super.execute(command);
}
return response;
}
};


return new RemoteWebDriver(executor, new DesiredCapabilities());
}


public static void main(String [] args) {


ChromeDriver driver = new ChromeDriver();
HttpCommandExecutor executor = (HttpCommandExecutor) driver.getCommandExecutor();
URL url = executor.getAddressOfRemoteServer();
SessionId session_id = driver.getSessionId();




RemoteWebDriver driver2 = createDriverFromSession(session_id, url);
driver2.get("http://tarunlalwani.com");
}
}

您的测试需要从现有的浏览器会话创建 RemoteWebDriver。要创建驱动程序,您只需要知道“ session info”,即浏览器运行的服务器的地址(在我们的例子中是本地的)和浏览器会话 ID。要获得这些详细信息,我们可以使用 selenium 创建一个浏览器会话,打开所需的页面,然后最后运行实际的测试脚本。

我不知道是否有一种方法可以获得会话信息,这个会话不是由 selenium 创建的。

下面是会话信息的一个例子:

远程服务器地址: http://localhost:24266。每个会话的端口号不同。 会话 Id: 534c7b561aacdd6dc319f60fed27d9d6。

Python 溶液。

from selenium import webdriver
from selenium.webdriver.remote.webdriver import WebDriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities






executor_url = "http://localhost:4444/wd/hub"
# Create a desired capabilities object as a starting point.
capabilities = DesiredCapabilities.FIREFOX.copy()
capabilities['platform'] = "WINDOWS"
capabilities['version'] = "10"


# ------------------------ STEP 1 --------------------------------------------------




# driver1 = webdriver.Firefox()
driver1 = webdriver.Remote(command_executor=executor_url, desired_capabilities=capabilities)
driver1.get('http://google.com/')
url = driver1.command_executor._url
print(driver1.command_executor._url)
print(driver1.session_id)
print(driver1.title)


# Serialize the session id in a file
session_id = driver1.session_id


# ------------------ END OF STEP 1 --------------------------------------------------


# Pass the session id from step 1 to step 2


# ------------------------ STEP 2 --------------------------------------------------
def attach_to_session(executor_url, session_id):
original_execute = WebDriver.execute
def new_command_execute(self, command, params=None):
if command == "newSession":
# Mock the response
return {'success': 0, 'value': None, 'sessionId': session_id}
else:
return original_execute(self, command, params)
    

# Patch the function before creating the driver object
WebDriver.execute = new_command_execute


temp_driver = webdriver.Remote(command_executor=executor_url)
# Replace the patched function with original function
WebDriver.execute = original_execute
return temp_driver


# read the session id from the file
driver2 = attach_to_session(executor_url, existing_session_id)
driver2.get('http://msn.com/')


print(driver2.command_executor._url)
print(driver2.session_id)


print(driver2.title)
driver2.close()
# ------------------ END OF STEP 2 --------------------------------------------------

给你开始,如果浏览器是手动打开的,那么可以使用远程调试:

  1. 从铬开始

    chrome --remote-debugging-port=9222
    

或者可选择的侧写

chrome.exe --remote-debugging-port=9222 --user-data-dir="C:\selenum\ChromeProfile"
  1. 然后: Java:
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
    

//Change chrome driver path accordingly
System.setProperty("webdriver.chrome.driver", "C:\\selenium\\chromedriver.exe");
ChromeOptions options = new ChromeOptions();
options.setExperimentalOption("debuggerAddress", "127.0.0.1:9222");
WebDriver driver = new ChromeDriver(options);
System.out.println(driver.getTitle());

巨蟒:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
   

chrome_options = Options()
chrome_options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
#Change chrome driver path accordingly
chrome_driver = "C:\chromedriver.exe"
driver = webdriver.Chrome(chrome_driver, chrome_options=chrome_options)
print driver.title

使用 Chrome 内置的远程调试。打开远程调试端口启动 Chrome。我在 OS X 上这样做过:

sudo nohup /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 &

告诉 Selenium 使用远程调试端口:

from selenium import webdriver


options = webdriver.ChromeOptions()
options.add_argument('--remote-debugging-port=9222')
driver = webdriver.Chrome("./chromedriver", chrome_options=options)

在尝试了这些解决方案中的大部分后,这个解决方案对我来说是最好的。

对于那些在这个问题上挣扎的人来说,这里有一些小贴士可以让你的生活更轻松一些:

1-使用 司机经理代替手动安装的驱动程序(以避免兼容性问题)

from webdriver_manager.chrome import ChromeDriverManager


driver = webdriver.Chrome(ChromeDriverManager().install(),options=chrome_options)

2-确保关闭运行中的 chrome 实例,然后使用调试端口启动新实例

chrome.exe --remote-debugging-port=9222 --user-data-dir="C:\selenum\ChromeProfile"