Kann Selenium mit einer vorhandenen Browsersitzung interagieren?

94

Weiß jemand, ob Selenium (vorzugsweise WebDriver) in der Lage ist, mit einem Browser zu kommunizieren und über diesen zu handeln, der bereits ausgeführt wird, bevor ein Selenium-Client gestartet wird?

Ich meine, wenn Selenium in der Lage ist, mit einem Browser zu kommunizieren, ohne den Selenium Server zu verwenden (mit könnte beispielsweise ein Internet Explorer manuell gestartet werden).

Engel Romero
quelle

Antworten:

32

Dies ist eine ziemlich alte Funktionsanforderung: Ermöglichen Sie dem Webdriver, eine Verbindung zu einem laufenden Browser herzustellen . Es wird also offiziell nicht unterstützt.

Es gibt jedoch einige Arbeitscodes, die dies unterstützen sollen: https://web.archive.org/web/20171214043703/http://tarunlalwani.com/post/reusing-existing-browser-session-selenium-java/ .

Robert Munteanu
quelle
Vielen Dank, denn in diesem Link habe ich eine Klasse gefunden, die dies ermöglicht, aber leider kann ich diese Lösung nicht mit IE (nur mit Firefox) verwenden. Ich werde einen regulären IEDriver starten und ihn mithilfe einer Middleware von anderen Prozessen aus kommunizieren. Wenn Sie eine Idee haben, warum die Klasse nicht mit IE arbeitet, würde ich es begrüßen. Danke dir.
Angel Romero
Robert, es ist jetzt 2018. Könnten Sie bitte Ihre Antwort aktualisieren?
MasterJoe
Für den Fall, dass jemand es benötigt, habe ich Java-Code getestet, damit Selen eine vorhandene Browsersitzung verwendet - stackoverflow.com/a/51145789/6648326 .
MasterJoe
48

Dies ist eine doppelte Antwort. ** Wiederverbindung mit einem Treiber in Python Selen. ** Dies gilt für alle Treiber und für Java API.

  1. Öffnen Sie einen Treiber
driver = webdriver.Firefox()  #python
  1. Aus dem Treiberobjekt in session_id und _url extrahieren.
url = driver.command_executor._url       #"http://127.0.0.1:60622/hub"
session_id = driver.session_id            #'4e167f26-dc1d-4f51-a207-f761eaf73c31'
  1. Verwenden Sie diese beiden Parameter, um eine Verbindung zu Ihrem Treiber herzustellen.
driver = webdriver.Remote(command_executor=url,desired_capabilities={})
driver.close()   # this prevents the dummy browser
driver.session_id = session_id

Und Sie sind wieder mit Ihrem Fahrer verbunden.

driver.get("http://www.mrsmart.in")
Manoj Sahu
quelle
1
Genau das habe ich gesucht. Vielen Dank.
Milso
5
Es funktioniert für mich, außer dass jedes Mal ein doppelter Dummy-Browser ausgelöst wird.
Pavel Vlasov
Ich bekomme auch das Dummy-Fenster, es ist keine so große Sache, aber beim Debuggen ist es ärgerlich. Irgendwelche Ideen, wie man sie loswird?
Steve Gon
1
+1. Funktioniert für meinen Zweck, 2-Faktor-Authentifizierungsanmeldungen zu vermeiden, es sind jedoch doppelte Dummy-Browser vorhanden. Ich kann damit leben.
Sam
Wenn Sie das Dummy-Browserfenster schließen müssen, rufen Sie einfach an, driver.close()bevor Sie die Sitzungs-ID aktualisieren.
Amr Awad
21

Mit diesem Snippet können vorhandene Browserinstanzen erfolgreich wiederverwendet werden, ohne dass der doppelte Browser ausgelöst wird. Gefunden bei Tarun Lalwanis Blog.

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
quelle
2
Gibt es eine Möglichkeit, die vorhandene Sitzungs-ID und die Executor-URL durch Automatisierung zu finden? In meinem Fall hat eine andere Anwendung eine Browsersitzung geöffnet, und ich möchte diese verwenden. Können Sie bitte empfehlen, wie Sie die Browser-Sitzungs-ID dafür finden?
Sun Shine
Wahrscheinlich können Sie die URL und die Sitzungs-ID von executor_command beim Starten des Skripts in eine Datei kopieren und aus der Datei lesen, wenn Sie die Browsersitzung erneut einbinden möchten.
SK Venkat
@SKVenkat wie kann ich Session - ID von Chrome - Fenster, ich öffnete sie pywinauto mit und wollen nun selenuim darauf laufen, gibt es eine Python - Weg Session - ID von Chrome - Tab zu erhalten
Tayyab Nasir
@TayyabNasir, bitte schauen Sie sich die obige Antwort an. In der fünften Zeile, die auskommentiert wurde, # session_id = driver.session_idkönnen Sie die Sitzungs-ID eines Chrome-Fensters mit Python Selenium API abrufen. Ich denke, dass jeder Tab in einer Chrome-Sitzung keine eindeutige ID hat.
SK Venkat
2
@SK Ich möchte die Sitzungs-ID des Chromfensters, das ich manuell geöffnet habe. Ich habe dieses Fenster nicht mit Selen geöffnet
Tayyab Nasir
12

Es ist möglich. Aber Sie müssen es ein wenig hacken, es gibt einen Code. Sie müssen einen eigenständigen Server ausführen und RemoteWebDriver "patchen"

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
quelle
4
Basierend auf dieser hervorragenden Lösung habe ich einen vollständigen Blog-Beitrag geschrieben, in dem ich erläutert habe, wie eine Verbindung zu einer bereits geöffneten Browser-Instanz von Chrome hergestellt werden kann. Der vollständige Quellcode ist auch an diesen Blog-Beitrag angehängt. binaryclips.com/2015/08/25/…
joinsaad
4

Es scheint, dass diese Funktion von Selen nicht offiziell unterstützt wird. Tarun Lalwani hat jedoch funktionierenden Java-Code erstellt, um die Funktion bereitzustellen. Verweisen - http://tarunlalwani.com/post/reusing-existing-browser-session-selenium-java/

Hier ist der Arbeitsbeispielcode, der über den obigen Link kopiert wurde:

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

Für Ihren Test muss ein RemoteWebDriver aus einer vorhandenen Browsersitzung erstellt werden. Um diesen Treiber zu erstellen, müssen Sie nur die "Sitzungsinformationen" kennen, dh die Adresse des Servers (in unserem Fall lokal), auf dem der Browser ausgeführt wird, und die Sitzungs-ID des Browsers. Um diese Details zu erhalten, können wir eine Browsersitzung mit Selen erstellen, die gewünschte Seite öffnen und schließlich das eigentliche Testskript ausführen.

Ich weiß nicht, ob es eine Möglichkeit gibt, Sitzungsinformationen für eine Sitzung abzurufen, die nicht von Selen erstellt wurde.

Hier ist ein Beispiel für Sitzungsinformationen:

Adresse des Remote-Servers: http: // localhost: 24266 . Die Portnummer ist für jede Sitzung unterschiedlich. Sitzungs-ID: 534c7b561aacdd6dc319f60fed27d9d6.

MasterJoe
quelle
"Ich weiß nicht, ob es eine Möglichkeit gibt, Sitzungsinformationen für eine Sitzung abzurufen, die nicht von Selen erstellt wurde." Es ist eigentlich ein Problem, das ich schon seit ein paar Tagen versuche ... noch kein Erfolg
Slesh
@slesh - Ich schlage vor, Sie erstellen eine neue Frage dafür und bieten möglicherweise 100 Ihrer Punkte an, wenn diese nicht genügend Beachtung finden.
MasterJoe
4

Inspiriert von Erics Antwort, hier ist meine Lösung für dieses Problem für Selen 3.7.0. Gegenüber der Lösung unter http://tarunlalwani.com/post/reusing-existing-browser-session-selenium/ besteht der Vorteil darin, dass nicht jedes Mal, wenn ich eine Verbindung zur vorhandenen Sitzung herstelle, ein leeres Browserfenster angezeigt wird.

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

Um es zu benutzen:

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))
Großer Kürbis
quelle
3

Allen bisherigen Lösungen fehlten bestimmte Funktionen. Hier ist meine Lösung:

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
quelle
Welche Funktionalität fügt dies hinzu (dass die anderen fehlen)?
Jalanb
1
Intern initialisiert nur die Methode startSession (...) das Capabilities-Objekt. Das Capabilities-Objekt wird für viele Methoden wie takeScreenshot, executeScript und mehr benötigt. Wenn Sie jedoch startSession durchlaufen, müssen Sie eine neue Sitzungserstellung erstellen. Diese Überlastung überspringt die Erstellung einer neuen Sitzung, führt jedoch weiterhin zur Initialisierung von Funktionsobjekten.
Yanir
Alter, vergleiche keine Saiten mit ==
Norill Tempest
3

Javascript-Lösung:

Ich habe mit dieser Funktion erfolgreich eine Verbindung zu einer vorhandenen Browsersitzung hergestellt

webdriver.WebDriver.attachToSession(executor, session_id);

Dokumentation finden Sie hier .

gm2008
quelle
Ja, ein funktionierendes Codebeispiel für die Verwendung eines aktuell geöffneten Fensters (das unter einem zuvor gestarteten Chromedriver ausgeführt wird) finden Sie hier (in meiner Test-Engine): github.com/Dzenly/tia/blob/master/api/selenium/sel-driver.js
Dzenly
2
Dies ist nicht in der Version 4.0.0!
Googamanga
1

Ich habe eine Lösung in Python erhalten. Ich habe die Webdriver-Klasse geändert, die auf der von mir gefundenen PersistenBrowser-Klasse basiert.

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

Ersetzen Sie das Webdriver-Modul /usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py

Ej. benutzen:

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
quelle
0

Ich verwende Rails + Cucumber + Selenium Webdriver + PhantomJS und eine mit Affen gepatchte Version von Selenium Webdriver, mit der der PhantomJS-Browser zwischen den Testläufen geöffnet bleibt. Siehe diesen Blog-Beitrag: http://blog.sharetribe.com/2014/04/07/faster-cucumber-startup-keep-phantomjs-browser-open-between-tests/

Siehe auch meine Antwort auf diesen Beitrag: Wie führe ich einen Befehl in einem bereits geöffneten Browser aus einer Ruby-Datei aus?

rap1ds
quelle
Vielen Dank für Ihre Antwort
Angel Romero
-1

Dies ist mit dem JavaScript- selenium-webdriverClient ziemlich einfach :

Stellen Sie zunächst sicher, dass ein WebDriver-Server ausgeführt wird. Laden Sie beispielsweise ChromeDriver herunter und führen Sie es aus chromedriver --port=9515.

Zweitens erstellen Sie den Treiber wie folgt :

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

Hier ist ein vollständiges Beispiel:

var webdriver = require ('Selen-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
quelle
4
Es wird keine EXISTING-Browsersitzung verwendet. Es erstellt eine neue Chromedriver-Sitzung und öffnet ein neues Browserfenster. Und getAllWindowHandles () zeigt das Handle Ihres alten Browserfensters nicht an.
Dzenly
Update: Es gibt seleniumhq.github.io/selenium/docs/api/javascript/module/…, mit dem eine Verbindung zu einem vorhandenen geöffneten Browserfenster hergestellt werden kann.
Dzenly