【问题标题】:Can Selenium interact with an existing browser session?Selenium 可以与现有的浏览器会话交互吗?
【发布时间】:2012-01-10 19:12:45
【问题描述】:

是否有人知道 Selenium(最好是 WebDriver)是否能够与在启动 Selenium 客户端之前已经运行的浏览器进行通信并通过该浏览器进行操作?

我的意思是如果 Selenium 能够在不使用 Selenium 服务器的情况下与浏览器通信(例如,可以是手动启动的 Internet Explorer)。

【问题讨论】:

    标签: selenium selenium-webdriver webdriver communication


    【解决方案1】:

    这是一个相当古老的功能请求:Allow webdriver to attach to a running browser。所以官方不支持。

    但是,有一些工作代码声称支持这一点:https://web.archive.org/web/20171214043703/http://tarunlalwani.com/post/reusing-existing-browser-session-selenium-java/

    【讨论】:

    • 非常感谢,因为在那个链接中我找到了一个允许这样做的类,但不幸的是我不能在 IE 中使用该解决方案(仅适用于 Firefox)。我将启动一个常规的 IEDriver 并使用中间件与其他进程进行通信。如果您知道为什么该课程不适用于 IE,我将不胜感激。谢谢。
    • 罗伯特,现在是 2018 年。你能更新你的答案吗?
    • 如果有人需要,我已经尝试并测试了一些 Java 代码以使 selenium 使用现有的浏览器会话 - stackoverflow.com/a/51145789/6648326
    • @AngelRomero 如何选择一个新的答案。自从提出这个问题以来,已经开发了很多功能。
    【解决方案2】:

    这是可能的。但是你必须稍微破解一下,有一个代码 你所要做的就是运行独立服务器并“修补”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;
            }
        }
    }
    

    【讨论】:

    • 基于这个出色的解决方案,我写了一篇完整的博文,其中讨论了如何连接到已经打开的 chrome 浏览器实例。完整的源代码也附在该博客文章中。 binaryclips.com/2015/08/25/…
    【解决方案3】:

    我正在使用 Rails + Cucumber + Selenium Webdriver + PhantomJS,并且我一直在使用 Selenium Webdriver 的猴子补丁版本,它可以在测试运行之间保持 PhantomJS 浏览器打开。见这篇博文:http://blog.sharetribe.com/2014/04/07/faster-cucumber-startup-keep-phantomjs-browser-open-between-tests/

    另请参阅我对这篇文章的回答:How do I execute a command on already opened browser from a ruby file

    【讨论】:

      【解决方案4】:

      这很容易使用 JavaScript selenium-webdriver 客户端:

      首先,确保您有一个正在运行的 WebDriver 服务器。例如download ChromeDriver,然后运行chromedriver --port=9515

      二、创建驱动like this

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

      这是一个完整的例子:

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

      【讨论】:

      • 它不使用现有的浏览器会话。它会创建一个新的 chromedriver 会话并打开一个新的浏览器窗口。 getAllWindowHandles() 不会显示旧浏览器窗口的句柄。
      • 更新:有seleniumhq.github.io/selenium/docs/api/javascript/module/…允许连接到现有打开的浏览器窗口。
      【解决方案5】:

      这是一个重复的答案 **重新连接到 python selenium 中的驱动程序** 这适用于所有驱动程序和 java api。

      1. 打开驱动程序
      driver = webdriver.Firefox()  #python
      
      1. 从驱动程序对象中提取到 session_id 和 _url。
      url = driver.command_executor._url       #"http://127.0.0.1:60622/hub"
      session_id = driver.session_id            #'4e167f26-dc1d-4f51-a207-f761eaf73c31'
      
      1. 使用这两个参数连接到您的驱动程序。
      driver = webdriver.Remote(command_executor=url,desired_capabilities={})
      driver.close()   # this prevents the dummy browser
      driver.session_id = session_id
      

      你又连接到你的驱动程序了。

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

      【讨论】:

      • 它对我有用,除了每次都会提升一个重复的虚拟浏览器。
      • 我也得到了虚拟窗口,这没什么大不了的,但是在调试过程中很烦人。关于如何摆脱的任何想法?
      • +1。用于避免 2 因素身份验证登录的目的,但是存在重复的虚拟浏览器。我可以忍受。
      • 如果您需要关闭虚拟浏览器窗口,只需在更新会话 ID 之前调用 driver.close()
      • selenium.common.exceptions.SessionNotCreatedException: Message: Session is already started
      【解决方案6】:

      到目前为止,所有解决方案都缺乏某些功能。 这是我的解决方案:

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

      【讨论】:

      • 这增加了什么功能(其他的都没有)?
      • 在内部,只有 startSession(...) 方法将初始化功能对象。许多方法都需要功能对象,例如 takeScreenshot、executeScript 等。但是通过 startSession 你将不得不创建一个新的会话创建。此重载会跳过新会话的创建,但仍会导致功能对象初始化。
      • 老兄,不要用 == 比较字符串
      【解决方案7】:

      Javascript 解决方案:

      我已使用此功能成功附加到现有浏览器会话

      webdriver.WebDriver.attachToSession(executor, session_id);
      

      可以在here找到文档。

      【讨论】:

      • 4.0.0版本没有这个!
      【解决方案8】:

      我在 python 中得到了一个解决方案,我修改了基于我找到的 PersistenBrowser 类的 webdriver 类。

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

      替换webdriver模块/usr/local/lib/python2.7/dist-packages/selenium/webdriver/remote/webdriver.py

      Ej。使用:

      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
      

      【讨论】:

        【解决方案9】:

        受 Eric 回答的启发,这是我针对 selenium 3.7.0 解决此问题的方法。与http://tarunlalwani.com/post/reusing-existing-browser-session-selenium/的解决方案相比,优点是每次连接现有会话时不会出现空白浏览器窗口。

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

        使用它:

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

        【讨论】:

          【解决方案10】:

          这个 sn-p 成功地允许重用现有的浏览器实例,同时避免引发重复的浏览器。在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/')
          

          【讨论】:

          • 有没有办法通过自动化找到现有的 session id 和 executor URL?就我而言,另一个应用程序打开了一个浏览器会话,我想使用它。您能否推荐一下,如何找到它的浏览器会话 ID?
          • 可能你可以在脚本启动时将 executor_command url & session id 转储到一个文件中,并在你想再次挂钩浏览器会话时从文件中读取它。
          • @SKVenkat 如何获取 chrome 窗口的会话 id,我使用 pywinauto 打开它,现在想在其上运行 selenuim,是否有 python 方法来获取 chrome 选项卡的会话 id
          • @TayyabNasir,请注意上述答案。被注释掉的第五行 # session_id = driver.session_id 是您可以使用 python selenium api 检索 chrome 窗口的会话 ID 的方式。我猜想 chrome 会话中的每个选项卡都没有唯一的 ID。
          • @S.K.我想要手动打开的 chrome 窗口的会话 ID,我没有使用 selenium 打开那个窗口
          【解决方案11】:

          似乎 selenium 并未正式支持此功能。但是,Tarun Lalwani 已经创建了工作 Java 代码来提供该功能。参考-http://tarunlalwani.com/post/reusing-existing-browser-session-selenium-java/

          这是工作示例代码,从上面的链接复制:

          import org.openqa.selenium.chrome.ChromeDriver;
          import org.openqa.selenium.remote.*;
          import org.openqa.selenium.remote.http.W3CHttpCommandCodec;
          import org.openqa.selenium.remote.http.W3CHttpResponseCodec;
          
          import java.io.IOException;
          import java.lang.reflect.Field;
          import java.net.URL;
          import java.util.Collections;
          
          public class TestClass {
              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");
              }
          }
          

          您的测试需要从现有浏览器会话中创建 RemoteWebDriver。要创建该驱动程序,您只需要知道“会话信息”,即浏览器正在运行的服务器(在我们的例子中是本地)的地址和浏览器会话 ID。要获取这些详细信息,我们可以使用 selenium 创建一个浏览器会话,打开所需的页面,然后最后运行实际的测试脚本。

          我不知道是否有办法获取不是由 selenium 创建的会话的会话信息。

          以下是会话信息的示例:

          远程服务器地址:http://localhost:24266。每个会话的端口号都不同。 会话 ID:534c7b561aacdd6dc319f60fed27d9d6。

          【讨论】:

          • “我不知道是否有办法获取不是由 selenium 创建的会话的会话信息。”这实际上是我已经尝试了几天的问题......还没有成功
          • @slesh - 我建议你为此创建一个新问题,如果没有得到足够的关注,可能会提供 100 分。
          • 感谢您参考 Tarun Lalwani 的作品。在他的页面和您的答案之间,我能够弄清楚。导入会很好,以及解释某些语句的目的的 cmets。但总而言之,非常有帮助。
          【解决方案12】:

          使用 Python 编程语言的解决方案。

          from selenium import webdriver
          from selenium.webdriver.remote.webdriver import WebDriver
          from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
          
          
          
          executor_url = "http://localhost:4444/wd/hub"
          # Create a desired capabilities object as a starting point.
          capabilities = DesiredCapabilities.FIREFOX.copy()
          capabilities['platform'] = "WINDOWS"
          capabilities['version'] = "10"
          
          # ------------------------ STEP 1 --------------------------------------------------
          
          
          # driver1 = webdriver.Firefox()
          driver1 = webdriver.Remote(command_executor=executor_url, desired_capabilities=capabilities)
          driver1.get('http://google.com/')
          url = driver1.command_executor._url       
          print(driver1.command_executor._url)
          print(driver1.session_id)
          print(driver1.title)
          
          # Serialize the session id in a file
          session_id = driver1.session_id
          
          # ------------------ END OF STEP 1 --------------------------------------------------
          
          # Pass the session id from step 1 to step 2
          
          # ------------------------ STEP 2 --------------------------------------------------
          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
          
              temp_driver = webdriver.Remote(command_executor=executor_url)
              # Replace the patched function with original function
              WebDriver.execute = original_execute
              return temp_driver
          
          # read the session id from the file
          driver2 = attach_to_session(executor_url, existing_session_id)
          driver2.get('http://msn.com/')
          
          print(driver2.command_executor._url)
          print(driver2.session_id)
          
          print(driver2.title)
          driver2.close()
          # ------------------ END OF STEP 2 --------------------------------------------------
          

          【讨论】:

            【解决方案13】:

            来自here,如果手动打开浏览器,则可以使用远程调试:

            1. 用 chrome 启动

              chrome --remote-debugging-port=9222
              

            或使用可选配置文件

            chrome.exe --remote-debugging-port=9222 --user-data-dir="C:\selenum\ChromeProfile"
            
            1. 然后: 爪哇:
            import org.openqa.selenium.WebDriver;
            import org.openqa.selenium.chrome.ChromeDriver;
            import org.openqa.selenium.chrome.ChromeOptions;
                
            //Change chrome driver path accordingly
            System.setProperty("webdriver.chrome.driver", "C:\\selenium\\chromedriver.exe");
            ChromeOptions options = new ChromeOptions();
            options.setExperimentalOption("debuggerAddress", "127.0.0.1:9222");
            WebDriver driver = new ChromeDriver(options);
            System.out.println(driver.getTitle());
            

            Python:

            from selenium import webdriver
            from selenium.webdriver.chrome.options import Options
               
            chrome_options = Options()
            chrome_options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
            #Change chrome driver path accordingly
            chrome_driver = "C:\chromedriver.exe"
            driver = webdriver.Chrome(chrome_driver, chrome_options=chrome_options)
            print driver.title
            

            【讨论】:

              【解决方案14】:

              使用 Chrome 的内置远程调试。在远程调试端口打开的情况下启动 Chrome。我在 OS X 上做了这个:

              sudo nohup /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 &
              

              告诉 Selenium 使用远程调试端口:

              from selenium import webdriver
              
              options = webdriver.ChromeOptions()
              options.add_argument('--remote-debugging-port=9222')
              driver = webdriver.Chrome("./chromedriver", chrome_options=options)
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2019-12-15
                • 2014-09-13
                • 1970-01-01
                • 2014-12-24
                • 1970-01-01
                • 2016-08-07
                相关资源
                最近更新 更多