Selenium peut-il interagir avec une session de navigateur existante?

103

Quelqu'un sait-il si Selenium (WebDriver de préférence) est capable de communiquer et d'agir via un navigateur qui est déjà en cours d'exécution avant de lancer un client Selenium?

Je veux dire si Selenium est capable de communiquer avec un navigateur sans utiliser le serveur Selenium (avec peut-être un Internet Explorer lancé manuellement par exemple).

Ange romero
la source

Réponses:

35

Il s'agit d'une demande de fonctionnalité assez ancienne: autorisez Webdriver à se connecter à un navigateur en cours d'exécution . Donc, ce n'est officiellement pas pris en charge.

Cependant, il existe un code de travail qui prétend prendre en charge cela: https://web.archive.org/web/20171214043703/http://tarunlalwani.com/post/reusing-existing-browser-session-selenium-java/ .

Robert Munteanu
la source
Merci beaucoup car dans ce lien j'ai trouvé une classe qui permet de le faire, mais malheureusement je ne peux pas utiliser cette solution avec IE (uniquement avec Firefox). Je vais lancer un IEDriver normal et communiquer avec lui à partir d'autres processus en utilisant un middleware. Si vous avez une idée de pourquoi la classe ne travaille pas sur IE, je l'apprécierais. Merci.
Angel Romero
Robert, c'est 2018 maintenant. Pouvez-vous mettre à jour votre réponse?
MasterJoe
Au cas où quelqu'un en aurait besoin, j'ai essayé et testé du code Java pour que le sélénium utilise une session de navigateur existante - stackoverflow.com/a/51145789/6648326 .
MasterJoe
54

Ceci est une réponse en double ** Reconnectez-vous à un pilote en python sélénium ** Ceci est applicable sur tous les pilotes et pour java api.

  1. ouvrir un pilote
driver = webdriver.Firefox()  #python
  1. extraire vers session_id et _url depuis l'objet pilote.
url = driver.command_executor._url       #"http://127.0.0.1:60622/hub"
session_id = driver.session_id            #'4e167f26-dc1d-4f51-a207-f761eaf73c31'
  1. Utilisez ces deux paramètres pour vous connecter à votre pilote.
driver = webdriver.Remote(command_executor=url,desired_capabilities={})
driver.close()   # this prevents the dummy browser
driver.session_id = session_id

Et vous êtes à nouveau connecté à votre pilote.

driver.get("http://www.mrsmart.in")
Manoj Sahu
la source
1
Ceci est exactement ce que je cherchais. Merci.
milso
6
Cela fonctionne pour moi, sauf qu'un navigateur factice en double se lève à chaque fois.
Pavel Vlasov
Je reçois aussi la fenêtre factice, ce n'est pas si grave, mais pendant le débogage, c'est ennuyeux. Des idées pour s'en débarrasser?
Steve Gon
1
+1. Fonctionne dans mon but d'éviter les connexions d'authentification à 2 facteurs, mais des navigateurs factices en double sont présents. Je peux vivre avec ça.
Sam
1
selenium.common.exceptions.SessionNotCreatedException: Message: Session is already started
Cerin
23

Cet extrait de code permet avec succès de réutiliser l'instance de navigateur existante tout en évitant d'élever le navigateur en double. Trouvé sur le 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
la source
2
Existe-t-il un moyen de trouver l'ID de session et l'URL de l'exécuteur existants grâce à l'automatisation? Dans mon cas, une autre application a ouvert une session de navigateur et je souhaite l'utiliser. Pouvez-vous s'il vous plaît recommander, comment trouver l'identifiant de session du navigateur de cela?
Sun Shine
Vous pouvez probablement vider l'url de l'exécuteur_command et l'identifiant de session dans un fichier lorsque le script démarre et le lire à partir du fichier lorsque vous souhaitez à nouveau raccorder la session du navigateur.
SK Venkat
@SKVenkat comment puis-je obtenir l'identifiant de session de la fenêtre chrome, je l'ai ouvert en utilisant pywinauto et je veux maintenant exécuter selenuim dessus, y a-t-il un moyen python d'obtenir l'identifiant de session de l'onglet chrome
Tayyab Nasir
@TayyabNasir, veuillez regarder la réponse ci-dessus. La cinquième ligne qui a été commentée # session_id = driver.session_idest la façon dont vous pouvez récupérer l'identifiant de session d'une fenêtre chrome en utilisant l'api python selenium. Je suppose que chaque onglet d'une session Chrome n'a pas d'identifiant unique.
SK Venkat le
3
@SK Je veux l'identifiant de session de la fenêtre chrome que j'ai ouverte manuellement, je n'ai pas ouvert cette fenêtre en utilisant du sélénium
Tayyab Nasir
12

C'est possible. Mais vous devez le pirater un peu, il y a un code Ce que vous devez faire est d'exécuter un serveur autonome et de "patcher" 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
la source
4
Sur la base de cette excellente solution, j'ai écrit un article de blog complet dans lequel j'ai expliqué comment me connecter à une instance de navigateur déjà ouverte de chrome. Le code source complet est également joint à ce billet de blog. binaryclips.com/2015/08/25/…
joinsaad
4

Il semble que cette fonctionnalité n'est pas officiellement prise en charge par le sélénium. Mais, Tarun Lalwani a créé du code Java fonctionnel pour fournir cette fonctionnalité. Reportez-vous - http://tarunlalwani.com/post/reusing-existing-browser-session-selenium-java/

Voici l'exemple de code de travail, copié à partir du lien ci-dessus:

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

Votre test doit avoir un RemoteWebDriver créé à partir d'une session de navigateur existante. Pour créer ce pilote, il vous suffit de connaître les "informations de session", c'est-à-dire l'adresse du serveur (local dans notre cas) sur lequel le navigateur s'exécute et l'identifiant de session du navigateur. Pour obtenir ces détails, nous pouvons créer une session de navigateur avec du sélénium, ouvrir la page souhaitée, puis enfin exécuter le script de test réel.

Je ne sais pas s'il existe un moyen d'obtenir des informations de session pour une session qui n'a pas été créée par le sélénium.

Voici un exemple d'informations sur la session:

Adresse du serveur distant: http: // localhost: 24266 . Le numéro de port est différent pour chaque session. Id de session: 534c7b561aacdd6dc319f60fed27d9d6.

MaîtreJoe
la source
"Je ne sais pas s'il existe un moyen d'obtenir des informations de session pour une session qui n'a pas été créée par le sélénium." c'est en fait un problème que j'essaye depuis quelques jours déjà ... pas encore de succès
slesh
@slesh - Je vous suggère de créer une nouvelle question pour cela et peut-être d'offrir 100 de vos points si cela ne retient pas suffisamment l'attention.
MasterJoe
Merci pour la référence au travail de Tarun Lalwani. Entre sa page et votre réponse, j'ai pu le comprendre. Les importations auraient été bien, ainsi que des commentaires expliquant le but de certaines des déclarations. Mais dans l'ensemble, très utile.
Tihamer
4

Inspiré par la réponse d'Eric, voici ma solution à ce problème pour le sélénium 3.7.0. Par rapport à la solution sur http://tarunlalwani.com/post/reusing-existing-browser-session-selenium/ , l'avantage est qu'il n'y aura pas de fenêtre de navigateur vide à chaque fois que je me connecte à la session existante.

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

Pour l'utiliser:

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))
Grosse citrouille
la source
3

Jusqu'à présent, toutes les solutions manquaient de certaines fonctionnalités. Voici ma solution:

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
la source
Quelle fonctionnalité cela ajoute-t-il (que les autres manquent)?
jalanb
1
En interne, seule la méthode startSession (...) initialisera l'objet de capacités. L'objet de capacités est requis pour de nombreuses méthodes telles que takeScreenshot, executeScript, etc. Mais en passant par startSession, vous devrez créer une nouvelle création de session. Cette surcharge ignore la création d'une nouvelle session mais conduit toujours à l'initialisation des objets de capacités.
Yanir
mec, ne compare pas les chaînes avec ==
Norill Tempest
3

Solution Javascript:

J'ai réussi à me connecter à une session de navigateur existante à l'aide de cette fonction

webdriver.WebDriver.attachToSession(executor, session_id);

La documentation peut être trouvée ici .

gm2008
la source
3
Ce n'est pas dans la version 4.0.0!
googamanga
1

J'ai eu une solution en python, j'ai modifié la classe webdriver basée sur la classe PersistenBrowser que j'ai trouvée.

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

remplacez le module webdriver /usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py

Ej. utiliser:

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
la source
0

J'utilise Rails + Cucumber + Selenium Webdriver + PhantomJS, et j'utilise une version monkey-patch de Selenium Webdriver, qui maintient le navigateur PhantomJS ouvert entre les tests. Voir ce billet de blog: http://blog.sharetribe.com/2014/04/07/faster-cucumber-startup-keep-phantomjs-browser-open-between-tests/

Voir aussi ma réponse à ce post: Comment exécuter une commande sur un navigateur déjà ouvert à partir d'un fichier ruby

rap1ds
la source
-1

C'est assez simple en utilisant le selenium-webdriverclient JavaScript :

Tout d'abord, assurez-vous qu'un serveur WebDriver est en cours d'exécution. Par exemple, téléchargez ChromeDriver , puis exécutez chromedriver --port=9515.

Deuxièmement, créez le pilote comme ceci :

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

Voici un exemple complet:

var webdriver = require ('sélénium-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
la source
4
Il n'utilise pas de session de navigateur EXISTANTE. Il crée une nouvelle session chromedriver et ouvre une nouvelle fenêtre de navigateur. Et getAllWindowHandles () n'affichera pas le handle de votre ancienne fenêtre de navigateur.
Dzenly
Mise à jour: Il y a seleniumhq.github.io/selenium/docs/api/javascript/module/… qui permet de se connecter à la fenêtre de navigateur ouverte existante.
Dzenly