Czy Selenium może współdziałać z istniejącą sesją przeglądarki?

106

Czy ktoś wie, czy Selenium (najlepiej WebDriver) jest w stanie komunikować się i działać za pośrednictwem przeglądarki, która jest już uruchomiona przed uruchomieniem Klienta Selenium?

Chodzi mi o to, czy Selenium jest w stanie komunikować się z przeglądarką bez korzystania z Selenium Server (na przykład może to być Internet Explorer uruchamiany ręcznie).

Angel Romero
źródło

Odpowiedzi:

35

To jest dość stara prośba o funkcję: Zezwól webdriverowi na dołączenie do działającej przeglądarki . Więc oficjalnie nie jest obsługiwany.

Istnieje jednak działający kod, który twierdzi, że to obsługuje: https://web.archive.org/web/20171214043703/http://tarunlalwani.com/post/reusing-existing-browser-session-selenium-java/ .

Robert Munteanu
źródło
Bardzo dziękuję, ponieważ w tym linku znalazłem klasę, która to umożliwia, ale niestety nie mogę korzystać z tego rozwiązania w IE (tylko z Firefox). Mam zamiar uruchomić zwykły IEDriver i komunikować się z nim z innych procesów za pomocą oprogramowania pośredniego. Jeśli masz pomysł, dlaczego klasa nie działa w IE, byłbym wdzięczny. Dziękuję Ci.
Angel Romero
Robert, teraz jest 2018. Czy mógłbyś zaktualizować swoją odpowiedź?
MasterJoe
Na wypadek, gdyby ktoś tego potrzebował, wypróbowałem i przetestowałem kod Java, aby selen używał istniejącej sesji przeglądarki - stackoverflow.com/a/51145789/6648326 .
MasterJoe,
57

To jest zduplikowana odpowiedź ** Połącz ponownie ze sterownikiem w języku Python selenium ** Dotyczy wszystkich sterowników i interfejsu Java API.

  1. otwórz sterownik
driver = webdriver.Firefox()  #python
  1. wypakuj do session_id i _url z obiektu sterownika.
url = driver.command_executor._url       #"http://127.0.0.1:60622/hub"
session_id = driver.session_id            #'4e167f26-dc1d-4f51-a207-f761eaf73c31'
  1. Użyj tych dwóch parametrów, aby połączyć się ze sterownikiem.
driver = webdriver.Remote(command_executor=url,desired_capabilities={})
driver.close()   # this prevents the dummy browser
driver.session_id = session_id

I jesteś ponownie połączony ze swoim sterownikiem.

driver.get("http://www.mrsmart.in")
Manoj Sahu
źródło
2
To jest dokładnie to, czego szukałem. Dzięki.
milso
6
U mnie działa, z wyjątkiem tego, że za każdym razem podnosi się zduplikowana przeglądarka.
Pavel Vlasov
Otrzymuję również atrapy okna, to nie jest taka wielka sprawa, ale podczas debugowania jest denerwujące. Jakieś pomysły, jak się go pozbyć?
Steve Gon
1
+1. Działa w moim celu, aby uniknąć uwierzytelniania dwuetapowego, ale obecne są zduplikowane fałszywe przeglądarki. Moge z tym zyc.
Sam,
1
selenium.common.exceptions.SessionNotCreatedException: Message: Session is already started
Cerin
23

Ten fragment kodu z powodzeniem pozwala na ponowne wykorzystanie istniejącej instancji przeglądarki, unikając jednocześnie podnoszenia zduplikowanej przeglądarki. Znalezione na blogu 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
źródło
2
Czy istnieje sposób na znalezienie istniejącego identyfikatora sesji i adresu URL modułu wykonawczego poprzez automatyzację? W moim przypadku inna aplikacja otworzyła sesję przeglądarki i chcę tego użyć. Czy możesz polecić, jak znaleźć identyfikator sesji przeglądarki?
Sun Shine
Prawdopodobnie możesz zrzucić adres URL executor_command i identyfikator sesji do pliku podczas uruchamiania skryptu i odczytać je z pliku, gdy chcesz ponownie podłączyć sesję przeglądarki.
SK Venkat
@SKVenkat jak mogę uzyskać identyfikator sesji okna chrome, otworzyłem je za pomocą pywinauto i teraz chcę uruchomić na nim selenuim, czy istnieje sposób w Pythonie na uzyskanie identyfikatora sesji karty chrome
Tayyab Nasir
@TayyabNasir, zwróć uwagę na powyższą odpowiedź. Piąta linia, która została zakomentowana, # session_id = driver.session_idto sposób, w jaki możesz pobrać identyfikator sesji okna chrome przy użyciu interfejsu Python Selenium API. Wydaje mi się, że każda karta w sesji chrome nie ma unikalnego identyfikatora.
SK Venkat
5
@SK Chcę mieć identyfikator sesji chromowanego okna, które otworzyłem ręcznie, nie otworzyłem tego okna przy użyciu selenu
Tayyab Nasir
12

To jest możliwe. Ale musisz trochę to zhakować, jest kod Co musisz zrobić to uruchomić samodzielny serwer i "załatać" 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
źródło
5
Opierając się na tym doskonałym rozwiązaniu, napisałem kompletny wpis na blogu, w którym omówiłem, jak połączyć się z już otwartą instancją przeglądarki Chrome. Pełny kod źródłowy jest również dołączony do tego posta na blogu. binaryclips.com/2015/08/25/…
dołącza do
4

Wygląda na to, że selen oficjalnie nie wspiera tej funkcji. Ale Tarun Lalwani stworzył działający kod Java, aby zapewnić tę funkcję. Skorzystaj - http://tarunlalwani.com/post/reusing-existing-browser-session-selenium-java/

Oto działający przykładowy kod skopiowany z powyższego linku:

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

Twój test musi mieć RemoteWebDriver utworzony z istniejącej sesji przeglądarki. Aby utworzyć ten sterownik, wystarczy znać „informacje o sesji”, tj. Adres serwera (w naszym przypadku lokalny), na którym działa przeglądarka i identyfikator sesji przeglądarki. Aby uzyskać te szczegóły, możemy utworzyć jedną sesję przeglądarki z selenem, otworzyć żądaną stronę, a na koniec uruchomić rzeczywisty skrypt testowy.

Nie wiem, czy istnieje sposób na uzyskanie informacji o sesji, która nie została stworzona przez selen.

Oto przykład informacji o sesji:

Adres zdalnego serwera: http: // localhost: 24266 . Numer portu jest inny dla każdej sesji. Identyfikator sesji: 534c7b561aacdd6dc319f60fed27d9d6.

MasterJoe
źródło
„Nie wiem, czy istnieje sposób na uzyskanie informacji o sesji, która nie została stworzona przez selen”. to właściwie problem, który próbuję już od kilku dni ... jeszcze bez sukcesu
slesh
@slesh - proponuję, abyś stworzył nowe pytanie i być może zaoferowałbyś 100 punktów, jeśli nie wzbudzi ono wystarczającej uwagi.
MasterJoe
Dziękuję za odniesienie do pracy Taruna Lalwaniego. Pomiędzy jego stroną a twoją odpowiedzią udało mi się to rozgryźć. Import byłby miły, podobnie jak komentarze wyjaśniające cel niektórych stwierdzeń. Ale w sumie bardzo pomocne.
Tihamer
4

Zainspirowany odpowiedzią Erica, oto moje rozwiązanie tego problemu dla selenu 3.7.0. W porównaniu z rozwiązaniem dostępnym pod adresem http://tarunlalwani.com/post/reusing-existing-browser-session-selenium/ , zaletą jest to, że za każdym razem, gdy łączę się z istniejącą sesją, nie będzie pustego okna przeglądarki.

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

Aby z niego skorzystać:

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))
Duża dynia
źródło
3

Wszystkie dotychczasowe rozwiązania nie posiadały określonej funkcjonalności. Oto moje rozwiązanie:

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
źródło
Jaką funkcjonalność to dodaje (której brakuje innym)?
jalanb
1
Wewnętrznie tylko metoda startSession (...) zainicjuje obiekt możliwości. Obiekt możliwości jest wymagany dla wielu metod, takich jak takeScreenshot, executeScript i nie tylko. Ale przechodząc przez startSession będziesz musiał stworzyć nową sesję. To przeciążenie pomija tworzenie nowej sesji, ale nadal prowadzi do inicjalizacji obiektu możliwości.
Yanir
stary, nie porównuj stringów z ==
Norill Tempest
3

Rozwiązanie JavaScript:

Udało mi się dołączyć do istniejącej sesji przeglądarki przy użyciu tej funkcji

webdriver.WebDriver.attachToSession(executor, session_id);

Dokumentację można znaleźć tutaj .

gm2008
źródło
3
Tego nie ma w wersji 4.0.0!
googamanga
1

Dostałem rozwiązanie w Pythonie, zmodyfikowałem klasę webdrivera bazującą na klasie PersistenBrowser, którą znalazłem.

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

wymień moduł webdriver /usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py

Ej. używać:

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
źródło
0

Używam Rails + Cucumber + Selenium Webdriver + PhantomJS i używam poprawionej przez małpę wersji Selenium Webdriver, która utrzymuje otwartą przeglądarkę PhantomJS między testami. Zobacz ten wpis na blogu: http://blog.sharetribe.com/2014/04/07/faster-cucumber-startup-keep-phantomjs-browser-open-between-tests/

Zobacz także moją odpowiedź na ten post: Jak wykonać polecenie w już otwartej przeglądarce z pliku ruby

rap1ds
źródło
-1

Przy użyciu selenium-webdriverklienta JavaScript jest to całkiem proste :

Najpierw upewnij się, że masz uruchomiony serwer WebDriver. Na przykład pobierz ChromeDriver , a następnie uruchom chromedriver --port=9515.

Po drugie, utwórz sterownik w ten sposób :

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

Oto pełny przykład:

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
źródło
4
Nie używa ISTNIEJĄCEJ sesji przeglądarki. Tworzy nową sesję chromedriver i otwiera nowe okno przeglądarki. I getAllWindowHandles () nie pokaże uchwytu twojego starego okna przeglądarki.
Dzenly
Aktualizacja: Jest seleniumhq.github.io/selenium/docs/api/javascript/module/… Który pozwala na połączenie się z istniejącym otwartym oknem przeglądarki.
Dzenly,