Selenium può interagire con una sessione del browser esistente?


103

Qualcuno sa se Selenium (preferibilmente WebDriver) è in grado di comunicare e agire tramite un browser già in esecuzione prima di avviare un client Selenium?

Voglio dire se Selenium è in grado di comunicare con un browser senza utilizzare il Selenium Server (con potrebbe essere un Internet Explorer lanciato manualmente per esempio).

Risposte:


35

Questa è una richiesta di funzionalità piuttosto vecchia: Consenti al webdriver di collegarsi a un browser in esecuzione . Quindi non è ufficialmente supportato.

Tuttavia, esiste un codice funzionante che afferma di supportare questo: https://web.archive.org/web/20171214043703/http://tarunlalwani.com/post/reusing-existing-browser-session-selenium-java/ .


Grazie mille perché in quel link ho trovato una classe che permette di farlo, ma sfortunatamente non posso usare quella soluzione con IE (solo con Firefox). Lancio un normale IEDriver e comunico con esso da altri processi utilizzando un middleware. Se hai un'idea del perché la classe non funziona su IE, lo apprezzerei. Grazie.
Angel Romero

Robert, è il 2018 ora. Potresti aggiornare la tua risposta?
MasterJoe

Nel caso qualcuno ne avesse bisogno, ho provato e testato del codice Java per fare in modo che il selenio utilizzi una sessione del browser esistente: stackoverflow.com/a/51145789/6648326 .
MasterJoe

54

Questa è una risposta duplicata ** Riconnettiti a un driver in python selenium ** Questo è applicabile su tutti i driver e per Java API.

  1. aprire un driver
driver = webdriver.Firefox()  #python
  1. estrarre in session_id e _url dall'oggetto driver.
url = driver.command_executor._url       #"http://127.0.0.1:60622/hub"
session_id = driver.session_id            #'4e167f26-dc1d-4f51-a207-f761eaf73c31'
  1. Usa questi due parametri per connetterti al tuo driver.
driver = webdriver.Remote(command_executor=url,desired_capabilities={})
driver.close()   # this prevents the dummy browser
driver.session_id = session_id

E sei di nuovo connesso al tuo autista.

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

1
Questo e 'esattamente quello che stavo cercando. Grazie.
milso

6
Per me funziona, tranne per il fatto che un browser fittizio duplicato si solleva ogni volta.
Pavel Vlasov

Ricevo anche la finestra fittizia, non è un grosso problema, ma durante il debug è fastidioso. Qualche idea su come sbarazzarsi di?
Steve Gon

1
+1. Funziona per il mio scopo di evitare login di autenticazione a 2 fattori, tuttavia sono presenti browser fittizi duplicati. Posso vivere con quello.
Sam

1
selenium.common.exceptions.SessionNotCreatedException: Message: Session is already started
Cerin,

23

Questo snippet consente di riutilizzare correttamente l'istanza del browser esistente evitando di sollevare il browser duplicato. Trovato sul blog di 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/')

2
C'è un modo per trovare l'ID di sessione esistente e l'URL dell'esecutore tramite l'automazione? Nel mio caso, un'altra applicazione ha aperto una sessione del browser e voglio usarla. Puoi consigliarmi, come trovare l'ID di sessione del browser di questo?
Sun Shine

Probabilmente puoi scaricare l'URL executor_command e l'id di sessione in un file all'avvio dello script e leggerlo dal file quando vuoi agganciare di nuovo la sessione del browser.
SK Venkat

@SKVenkat come posso ottenere l'ID di sessione della finestra di Chrome, l'ho aperto usando pywinauto e ora voglio eseguire selenuim su di esso, esiste un modo Python per ottenere l'ID di sessione della scheda Chrome
Tayyab Nasir

@TayyabNasir, guarda la risposta sopra. La quinta riga che è stata commentata # session_id = driver.session_idè il modo in cui puoi recuperare l'ID di sessione di una finestra di Chrome utilizzando python selenium api. Immagino che ogni scheda in una sessione di Chrome non abbia un ID univoco.
SK Venkat,

3
@SK Voglio l'ID della sessione della finestra di Chrome che ho aperto manualmente, non ho aperto quella finestra usando il selenio
Tayyab Nasir

12

È possibile. Ma devi hackerarlo un po ', c'è un codice Quello che devi fare è eseguire un server autonomo e "patchare" 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;
        }
    }
}

4
Sulla base di questa eccellente soluzione, ho scritto un post sul blog completo in cui ho discusso su come connettersi a un'istanza del browser già aperta di Chrome. Il codice sorgente completo è anche allegato a quel post del blog. binaryclips.com/2015/08/25/…
joinsaad

4

Sembra che questa funzione non sia ufficialmente supportata dal selenio. Tuttavia, Tarun Lalwani ha creato codice Java funzionante per fornire la funzionalità. Fare riferimento - http://tarunlalwani.com/post/reusing-existing-browser-session-selenium-java/

Ecco il codice di esempio funzionante, copiato dal link sopra:

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

Il tuo test deve avere un RemoteWebDriver creato da una sessione del browser esistente. Per creare quel Driver, è sufficiente conoscere le "informazioni di sessione", cioè l'indirizzo del server (locale nel nostro caso) dove è in esecuzione il browser e l'id della sessione del browser. Per ottenere questi dettagli, possiamo creare una sessione del browser con selenio, aprire la pagina desiderata e infine eseguire lo script di test vero e proprio.

Non so se esiste un modo per ottenere informazioni sulla sessione per una sessione che non è stata creata dal selenio.

Ecco un esempio di informazioni sulla sessione:

Indirizzo del server remoto: http: // localhost: 24266 . Il numero di porta è diverso per ogni sessione. ID sessione: 534c7b561aacdd6dc319f60fed27d9d6.


"Non so se esiste un modo per ottenere informazioni sulla sessione per una sessione che non è stata creata dal selenio." in realtà è un problema che ho provato già da un paio di giorni ... nessun successo ancora
slesh

@slesh - Ti suggerisco di creare una nuova domanda per questo e magari offrire 100 dei tuoi punti se non riceve abbastanza attenzione.
MasterJoe

Grazie per il riferimento al lavoro di Tarun Lalwani. Tra la sua pagina e la tua risposta, sono riuscito a capirlo. Le importazioni sarebbero state carine, così come i commenti che spiegavano lo scopo di alcune dichiarazioni. Ma tutto sommato molto utile.
Tihamer

4

Ispirato dalla risposta di Eric, ecco la mia soluzione a questo problema per il selenio 3.7.0. Rispetto alla soluzione su http://tarunlalwani.com/post/reusing-existing-browser-session-selenium/ , il vantaggio è che non ci sarà una finestra del browser vuota ogni volta che mi collego alla sessione esistente.

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

Per 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))

3

Tutte le soluzioni fino ad ora erano prive di alcune funzionalità. Ecco la mia soluzione:

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

Quale funzionalità aggiunge (che mancano alle altre)?
jalanb

1
Internamente, solo il metodo startSession (...) inizializzerà l'oggetto capacità. L'oggetto capacità è richiesto per molti metodi come takeScreenshot, executeScript e altri. Ma passando attraverso startSession dovrai creare una nuova creazione di sessione. Questo sovraccarico salta la creazione di una nuova sessione ma porta comunque all'inizializzazione degli oggetti delle capacità.
Yanir

amico, non confrontare le stringhe con ==
Norill Tempest il

3

Soluzione Javascript:

Mi sono collegato con successo alla sessione del browser esistente utilizzando questa funzione

webdriver.WebDriver.attachToSession(executor, session_id);

La documentazione può essere trovata qui .


3
Questo non è nella versione 4.0.0!
googamanga

1

Ho ottenuto una soluzione in python, ho modificato la classe webdriver in basso sulla classe PersistenBrowser che ho trovato.

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

sostituire il modulo webdriver /usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py

Ej. usare:

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


-1

Questo è abbastanza facile usando il selenium-webdriverclient JavaScript :

Innanzitutto, assicurati di avere un server WebDriver in esecuzione. Ad esempio, scarica ChromeDriver , quindi esegui chromedriver --port=9515.

Secondo, crea il driver in questo modo :

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

Ecco un esempio 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();

4
Non utilizza la sessione del browser ESISTENTE. Crea una nuova sessione di chromedriver e apre una nuova finestra del browser. E getAllWindowHandles () non mostrerà l'handle della vecchia finestra del browser.
Dzenly

Aggiornamento: c'è seleniumhq.github.io/selenium/docs/api/javascript/module/… che permette di connettersi alla finestra del browser aperta esistente.
Dzenly
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.