¿Puede Selenium interactuar con una sesión de navegador existente?

103

¿Alguien sabe si Selenium (preferiblemente WebDriver) es capaz de comunicarse y actuar a través de un navegador que ya se está ejecutando antes de iniciar un cliente de Selenium?

Me refiero a si Selenium puede comunicarse con un navegador sin usar el servidor Selenium (podría ser un Internet Explorer lanzado manualmente, por ejemplo).

Ángel romero
fuente

Respuestas:

35

Esta es una solicitud de función bastante antigua: permitir que webdriver se conecte a un navegador en ejecución . Así que oficialmente no es compatible.

Sin embargo, hay un código de trabajo que afirma ser compatible con esto: https://web.archive.org/web/20171214043703/http://tarunlalwani.com/post/reusing-existing-browser-session-selenium-java/ .

Robert Munteanu
fuente
Muchas gracias porque en ese enlace encontré una clase que me permite hacer eso, pero desafortunadamente no puedo usar esa solución con IE (solo con Firefox). Voy a lanzar un IEDriver regular y comunicarme con él desde otros procesos usando un middleware. Si tiene una idea de por qué la clase no funciona en IE, se lo agradecería. Gracias.
Angel Romero
Robert, es 2018 ahora. ¿Podría actualizar su respuesta?
MasterJoe
En caso de que alguien lo necesite, probé y probé algún código Java para hacer que el selenio use una sesión de navegador existente - stackoverflow.com/a/51145789/6648326 .
MasterJoe
54

Esta es una respuesta duplicada ** Vuelva a conectarse a un controlador en python selenium ** Esto es aplicable en todos los controladores y para la API de Java.

  1. abrir un controlador
driver = webdriver.Firefox()  #python
  1. extraer a session_id y _url del objeto del controlador.
url = driver.command_executor._url       #"http://127.0.0.1:60622/hub"
session_id = driver.session_id            #'4e167f26-dc1d-4f51-a207-f761eaf73c31'
  1. Utilice estos dos parámetros para conectarse a su controlador.
driver = webdriver.Remote(command_executor=url,desired_capabilities={})
driver.close()   # this prevents the dummy browser
driver.session_id = session_id

Y está conectado nuevamente con su conductor.

driver.get("http://www.mrsmart.in")
Manoj Sahu
fuente
1
Esto es exactamente lo que estaba buscando. Gracias.
milso
6
Me funciona, excepto que aparece un navegador ficticio duplicado cada vez.
Pavel Vlasov
También obtengo la ventana ficticia, no es un gran problema, pero durante la depuración es molesto. ¿Alguna idea sobre cómo deshacerse de él?
Steve Gon
1
+1. Funciona para mi propósito de evitar los inicios de sesión de autenticación de 2 factores, sin embargo, hay navegadores ficticios duplicados. Puedo vivir con ello.
Sam
1
selenium.common.exceptions.SessionNotCreatedException: Message: Session is already started
Cerin
23

Este fragmento de código permite reutilizar correctamente la instancia del navegador existente y, al mismo tiempo, evita levantar el navegador duplicado. Encontrado en el blog de Tarun Lalwani .

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/')
Pavel Vlasov
fuente
2
¿Hay alguna forma de encontrar la identificación de sesión existente y la URL del ejecutor a través de la automatización? En mi caso, otra aplicación abrió una sesión de navegador y quiero usar eso. ¿Puede recomendarme cómo encontrar la identificación de sesión del navegador de eso?
Sun Shine
Probablemente pueda volcar la url del comando ejecutor y la identificación de la sesión en un archivo cuando se inicie el script y leerlo del archivo cuando desee volver a conectar la sesión del navegador.
SK Venkat
@SKVenkat, ¿cómo puedo obtener la identificación de sesión de la ventana de Chrome? La abrí usando pywinauto y ahora quiero ejecutar selenuim en ella, ¿hay alguna forma de Python para obtener la identificación de sesión de la pestaña de Chrome?
Tayyab Nasir
@TayyabNasir, tenga la amabilidad de mirar la respuesta anterior. La quinta línea que se comentó # session_id = driver.session_ides la forma en que puede recuperar la identificación de sesión de una ventana de Chrome usando python selenium api. Supongo que cada pestaña en una sesión de Chrome no tiene una identificación única.
SK Venkat
3
@SK Quiero la identificación de sesión de la ventana de Chrome que abrí manualmente, no abrí esa ventana con selenio
Tayyab Nasir
12

Es posible. Pero tienes que hackearlo un poco, hay un código. Lo que tienes que hacer es ejecutar un servidor independiente y "parchear" 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;
        }
    }
}
Alex Ilyin
fuente
4
Basado en esta excelente solución, he escrito una publicación de blog completa en la que he discutido cómo conectarse a una instancia de navegador ya abierta de Chrome. El código fuente completo también se adjunta en esa publicación del blog. binaryclips.com/2015/08/25/…
joinsaad
4

Parece que esta función no es compatible oficialmente con selenium. Pero, Tarun Lalwani ha creado código Java funcional para proporcionar la función. Consulte: http://tarunlalwani.com/post/reusing-existing-browser-session-selenium-java/

Aquí está el código de muestra de trabajo, copiado del enlace anterior:

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

Su prueba debe tener un RemoteWebDriver creado a partir de una sesión de navegador existente. Para crear ese controlador, solo necesita conocer la "información de la sesión", es decir, la dirección del servidor (local en nuestro caso) donde se está ejecutando el navegador y la identificación de la sesión del navegador. Para obtener estos detalles, podemos crear una sesión de navegador con selenium, abrir la página deseada y finalmente ejecutar el script de prueba real.

No sé si hay alguna forma de obtener información de sesión para una sesión que no fue creada por selenium.

A continuación, se muestra un ejemplo de información de la sesión:

Dirección del servidor remoto: http: // localhost: 24266 . El número de puerto es diferente para cada sesión. Id de sesión: 534c7b561aacdd6dc319f60fed27d9d6.

MasterJoe
fuente
"No sé si hay alguna manera de obtener información de sesión para una sesión que no fue creada por selenium". en realidad, es un problema que he estado intentando durante un par de días ... sin éxito todavía
slesh
@slesh: le sugiero que cree una nueva pregunta para eso y tal vez ofrezca 100 de sus puntos si no recibe suficiente atención.
MasterJoe
Gracias por la referencia al trabajo de Tarun Lalwani. Entre su página y tu respuesta, pude averiguarlo. Las importaciones habrían sido agradables, así como los comentarios que explican el propósito de algunas de las declaraciones. Pero en general, muy útil.
Tihamer
4

Inspirado por la respuesta de Eric, aquí está mi solución a este problema para el selenio 3.7.0. En comparación con la solución en http://tarunlalwani.com/post/reusing-existing-browser-session-selenium/ , la ventaja es que no habrá una ventana del navegador en blanco cada vez que me conecte a la sesión existente.

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']

Para usarlo:

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))
Calabaza grande
fuente
3

Todas las soluciones hasta ahora carecían de cierta funcionalidad. Aquí está mi solución:

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());
    }
}
Yanir
fuente
¿Qué funcionalidad agrega esto (que faltan las otras)?
jalanb
1
Internamente, solo el método startSession (...) inicializará el objeto de capacidades. El objeto de capacidades es necesario para muchos métodos, como takeScreenshot, executeScript y más. Pero al pasar por startSession, tendrá que crear una nueva creación de sesión. Esta sobrecarga omite la creación de una nueva sesión, pero aún conduce a la inicialización del objeto de capacidades.
Yanir
amigo, no compares cadenas con ==
Norill Tempest
3

Solución Javascript:

Me he conectado correctamente a la sesión del navegador existente usando esta función

webdriver.WebDriver.attachToSession(executor, session_id);

La documentación se puede encontrar aquí .

gm2008
fuente
3
¡Esto no está en la versión 4.0.0!
googamanga
1

Obtuve una solución en Python, modifiqué la clase webdriver basada en la clase PersistenBrowser que encontré.

https://github.com/axelPalmerin/personal/commit/fabddb38a39f378aa113b0cb8d33391d5f91dca5

reemplace el módulo webdriver /usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py

Ej. usar:

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 Axel
fuente
-1

Esto es bastante fácil con el selenium-webdrivercliente JavaScript :

Primero, asegúrese de tener un servidor WebDriver en ejecución. Por ejemplo, descargue ChromeDriver y luego ejecútelo chromedriver --port=9515.

En segundo lugar, cree el controlador así :

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

Aquí tienes un ejemplo completo:

var webdriver = require ('selenium-webdriver');

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();
Dan Dascalescu
fuente
4
No utiliza la sesión de navegador EXISTENTE. Crea una nueva sesión de chromeriver y abre una nueva ventana del navegador. Y getAllWindowHandles () no mostrará el identificador de la ventana del navegador anterior.
Dzenly
Actualización: hay seleniumhq.github.io/selenium/docs/api/javascript/module/… que permite conectarse a la ventana del navegador abierta existente.
Dzenly