如何重新连接Web Driver使用Selenium打开的浏览器?

发布于 2025-02-06 06:07:16 字数 220 浏览 4 评论 0原文

由于某些未知原因,我的浏览器非常缓慢地打开远程服务器的测试页面。因此,我想在退出脚本后是否可以重新连接到浏览器,但不要执行webdriver.quit()这将使浏览器打开。它可能是挂钩或网络驱动器的手柄。 我已经查找了Selenium API文档,但没有找到任何功能。 我正在使用Chrome 62、 X64,Windows 7,Selenium 3.8.0。 是否可以解决这个问题,我将不胜感激。

For some unknown reasons, my browser open test pages of my remote server very slowly. So I am thinking if I can reconnect to the browser after quitting the script but don't execute webdriver.quit() this will leave the browser opened. It is probably kind of HOOK or webdriver handle.
I have looked up the selenium API doc but didn't find any function.
I'm using Chrome 62, x64, windows 7, selenium 3.8.0.
I'll be very appreciated whether the question can be solved or not.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

凉城凉梦凉人心 2025-02-13 06:07:16

,这实际上很容易做到。

硒< - > WebDriver会话由连接URL和Session_ID表示,您只需重新连接与现有的连接。

免责声明 - 该方法是使用硒内部属性(某种程度上的“私人”),这可能会改变新版本;您最好不要将其用于生产代码;最好不要与远程SE(您的轮毂或Browserstack/Sauce Labs这样的提供商)使用,因为在末尾解释了警告/资源引流。

启动WebDriver实例时,您需要获取之前提到的属性;示例:

from selenium import webdriver

driver = webdriver.Chrome()
driver.get('https://www.google.com/')

# now Google is opened, the browser is fully functional; print the two properties
# command_executor._url (it's "private", not for a direct usage), and session_id

print(f'driver.command_executor._url: {driver.command_executor._url}')
print(f'driver.session_id: {driver.session_id}')

随着现在已知的这两个属性,另一个实例可以连接。 “技巧”是启动远程驱动程序,并提供上面的_url - 因此,它将连接到运行的硒过程:

driver2 = webdriver.Remote(command_executor=the_known_url)  
# when the started selenium is a local one, the url is in the form 'http://127.0.0.1:62526'

当运行时,您会看到打开一个新的浏览器窗口。
这是因为在启动驱动程序后,Selenium库会自动为其启动一个新的会话 - 现在您有1个带有2个会话的Web驱动程序进程(浏览器实例)。

如果导航到URL,您会发现它是在新的浏览器实例上执行的,而不是上一个开始剩下的一个 - 这不是所需的行为。
在这一点上,需要完成两件事 - a)关闭当前的SE会话(“新的”),b)将此实例切换到上一个会话:

if driver2.session_id != the_known_session_id:   # this is pretty much guaranteed to be the case
    driver2.close()   # this closes the session's window - it is currently the only one, thus the session itself will be auto-killed, yet:
    driver2.quit()    # for remote connections (like ours), this deletes the session, but does not stop the SE server

# take the session that's already running
driver2.session_id = the_known_session_id

# do something with the now hijacked session:
driver.get('https://www.bing.com/')

这就是您现在已连接到以前/已经存在的会话,及其所有属性(cookie,localstorage等)。

顺便说一句,您不必在启动新远程驱动程序时提供desired_capabilities - 这些远程驱动程序是从您接管的现有会话中存储和继承的。


警告 - 运行SE过程会导致系统中的某些资源引流。

每当一个启动然后不关闭时 - 就像在代码的第一部分一样 - 它将一直留在那里,直到您手动杀死它。我的意思是 - 例如,在Windows中 - 您会看到一个“ chromedriver.exe”过程,一旦完成后,您就必须手动终止。它不能由已连接到远程硒过程的驾驶员关闭。
原因 - 每当您启动本地浏览器实例,然后调用其quit()方法,它中有2个部分 - 第一个是从Selenium实例中删除会话(在那里的第二个代码),另一个是停止本地服务(Chrome/Geckodriver) - 通常可以正常工作。

问题是,对于远程会话,第二部分丢失了 - 您的本地计算机无法控制远程过程,这就是该遥控器的工作。因此,第二部分实际上是PASS Python语句 - no -op。

如果您在远程集线器上启动了太多的硒服务,并且对此没有控制 - 这将导致该服务器的资源引流。像Browserstack这样的云提供商对此采取了措施 - 他们正在关闭服务,而过去60年代没有活动等,这是您不想做的。

至于本地SE服务 - 只是不要忘记偶尔从您忘记的孤儿硒驱动器中清理操作系统:)

Yes, that's actually quite easy to do.

A selenium <-> webdriver session is represented by a connection url and session_id, you just reconnect to an existing one.

Disclaimer - the approach is using selenium internal properties ("private", in a way), which may change in new releases; you'd better not use it for production code; it's better not to be used against remote SE (yours hub, or provider like BrowserStack/Sauce Labs), because of a caveat/resource drainage explained at the end.

When a webdriver instance is initiated, you need to get the before-mentioned properties; sample:

from selenium import webdriver

driver = webdriver.Chrome()
driver.get('https://www.google.com/')

# now Google is opened, the browser is fully functional; print the two properties
# command_executor._url (it's "private", not for a direct usage), and session_id

print(f'driver.command_executor._url: {driver.command_executor._url}')
print(f'driver.session_id: {driver.session_id}')

With those two properties now known, another instance can connect; the "trick" is to initiate a Remote driver, and provide the _url above - thus it will connect to that running selenium process:

driver2 = webdriver.Remote(command_executor=the_known_url)  
# when the started selenium is a local one, the url is in the form 'http://127.0.0.1:62526'

When that is ran, you'll see a new browser window being opened.
That's because upon initiating the driver, the selenium library automatically starts a new session for it - and now you have 1 webdriver process with 2 sessions (browsers instances).

If you navigate to an url, you'll see it is executed on that new browser instance, not the one that's left from the previous start - which is not the desired behavior.
At this point, two things need to be done - a) close the current SE session ("the new one"), and b) switch this instance to the previous session:

if driver2.session_id != the_known_session_id:   # this is pretty much guaranteed to be the case
    driver2.close()   # this closes the session's window - it is currently the only one, thus the session itself will be auto-killed, yet:
    driver2.quit()    # for remote connections (like ours), this deletes the session, but does not stop the SE server

# take the session that's already running
driver2.session_id = the_known_session_id

# do something with the now hijacked session:
driver.get('https://www.bing.com/')

And, that is it - you're now connected to the previous/already existing session, with all its properties (cookies, LocalStorage, etc).

By the way, you do not have to provide desired_capabilities when initiating the new remote driver - those are stored and inherited from the existing session you took over.


Caveat - having a SE process running can lead to some resource drainage in the system.

Whenever one is started and then not closed - like in the first piece of the code - it will stay there until you manually kill it. By this I mean - in Windows for example - you'll see a "chromedriver.exe" process, that you have to terminate manually once you are done with it. It cannot be closed by a driver that has connected to it as to a remote selenium process.
The reason - whenever you initiate a local browser instance, and then call its quit() method, it has 2 parts in it - the first one is to delete the session from the Selenium instance (what's done in the second code piece up there), and the other is to stop the local service (the chrome/geckodriver) - which generally works ok.

The thing is, for Remote sessions the second piece is missing - your local machine cannot control a remote process, that's the work of that remote's hub. So that 2nd part is literally a pass python statement - a no-op.

If you start too many selenium services on a remote hub, and don't have a control over it - that'll lead to resource drainage from that server. Cloud providers like BrowserStack take measures against this - they are closing services with no activity for the last 60s, etc, yet - this is something you don't want to do.

And as for local SE services - just don't forget to occasionally clean up the OS from orphaned selenium drivers you forgot about :)

握住你手 2025-02-13 06:07:16

no ,退出脚本后,您无法重新连接到上一个 Web浏览会话。即使您能够提取会话IDcookies和其他会话属性,将这些属性作为 hook 传递到 Web Driver

一种清洁的方法是调用webdriver.quit(),然后跨越新的浏览上下文


深度潜水

已经进行了许多讨论,并尝试将 web driver 重新连接到现有的运行浏览上下文。在讨论中允许webdriver连接到跑步的浏览器 Simon Stewart [Creator Webdriver]清楚地提到了:

  • 重新连接到现有浏览上下文是浏览器特定功能,因此无法以通用的方式实现。
  • 使用 Internet-explorer ,可以在操作系统中的打开窗口上迭代并找到适合的IE过程。
  • firefox google-chrome 需要以特定模式和配置启动,这实际上意味着
    在技​​术上不可能连接到运行实例。

tl; dr

webdriver.firefox.useexisting未实现

No, you can't reconnect to the previous Web Browsing Session after you quit the script. Even if you are able to extract the Session ID, Cookies and other session attributes from the previous Browsing Context still you won't be able to pass those attributes as a HOOK to the WebDriver.

A cleaner way would be to call webdriver.quit() and then span a new Browsing Context.


Deep Dive

There had been a lot of discussions and attempts around to reconnect WebDriver to an existing running Browsing Context. In the discussion Allow webdriver to attach to a running browser Simon Stewart [Creator WebDriver] clearly mentioned:

  • Reconnecting to an existing Browsing Context is a browser specific feature, hence can't be implemented in a generic way.
  • With , it's possible to iterate over the open windows in the OS and find the right IE process to attach to.
  • and needs to be started in a specific mode and configuration, which effectively means that just
    attaching to a running instance isn't technically possible.

tl; dr

webdriver.firefox.useExisting not implemented

友谊不毕业 2025-02-13 06:07:16

对于Chromium,您可以获取浏览器的运行过程列表,请检查此过程中的命令行,然后搜索“ - remote-debugging-port =”提取portnumber并将其用于WebDriver的初始化。

将驱动程序设置为

ChromiumOptions.DebuggerAddress = "127.0.0.1:" & remotePort

无需任何其他选项设置的启动

driver As IWebDriver = New EdgeDriver(driverService, ChromiumOptions, CommandTimeout)

,即VB.NET中对我有用的零件,但我认为也可以用其他语言翻译和使用它。

并尽其所用。您可以在页面打开的点上继续,或者创建一个知道的窗口并关闭旧的窗口,如果您喜欢一种新的开始,但是先前的Coockies和Cache。

您可能想杀死旧的“ webdriver.exe”进程,因为在每个重新连接中,它都会产生一个知识。

这样您就可以模拟真实的用户行为,因为普通用户不会始终私有浏览。他们回到您的网站上,上面有旧的cookie和Cache中的数据。因此,您可以优化网站以正确处理旧的缓存资产,并找到用户不需要强制重新加载网站或每次更新时都必须清洁缓存的方法。

for Chromium you can get a list of running processes of the browser, inspect the commandline from this process and search for "--remote-debugging-port=" extract that portnumber and use it in initialisation of the webdriver.

set the DriverOptions to

ChromiumOptions.DebuggerAddress = "127.0.0.1:" & remotePort

without any other options set initiate

driver As IWebDriver = New EdgeDriver(driverService, ChromiumOptions, CommandTimeout)

that the part in VB.net that works for me, but i think it is possbible to translate and use it in other languages too.

and use the driver as usally. you can continue at the point where the page was left open, or create a knew window and close the old one if you like a kind of fresh start, but with the previous coockies and cache.

you may want to kill old "webdriver.exe" processes, because on every reconnet it will spawn a knew one.

in this way you simulate real user behavior because normal users don't browse always in private. they came back to your site with old cookies and data in cache. so you can optimize your site to deal properly with old cached assets and find ways where the user doesn't need to force reload the site or have to clean the cache every time the site gets an update.

燃情 2025-02-13 06:07:16

我也不知道这是不可能的,所以我也做到了。

这是我的Java版本。

public RemoteWebDriver getWebDriver(String sessionId, Capabilities capabilities, URL seleniumGridUrl) {
    return new RemoteWebDriver(seleniumGridUrl, capabilities) {
        {
            setSessionId(sessionId);
        }
    };
}

多亏了其他人,他们的回答是有可能的。如果可以在Python中进行,那么肯定可以在Java中进行。

I also didn't know it was impossible, so I did it too.

Here is my Java version.

public RemoteWebDriver getWebDriver(String sessionId, Capabilities capabilities, URL seleniumGridUrl) {
    return new RemoteWebDriver(seleniumGridUrl, capabilities) {
        {
            setSessionId(sessionId);
        }
    };
}

Thanks to other people showing it was possible in their answers. If it's possible in Python, it must be possible in Java for sure.

甜柠檬 2025-02-13 06:07:16

好的,在混合了这里共享的各种解决方案并进行调整之后,我现在可以按照以下操作。如果存在,脚本将使用以前左打开的Chrome窗口 - 如果需要,远程连接可以完美地杀死浏览器,并且代码功能正常。

而无需将其写入hte上一次会话时将其写入文件...

我希望有一种方法可以自动化session_id和url以进行上一次活动会话,

from selenium import webdriver
from selenium.webdriver.remote.webdriver import WebDriver

#Set manually - read/write from a file for automation
session_id =  "e0137cd71ab49b111f0151c756625d31"
executor_url = "http://localhost:50491"

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
 

remote_session = 0

#Try to connect to the last opened session - if failing open new window
try:
    driver = attach_to_session(executor_url,session_id)
    driver.current_url
    print(" Driver has an active window we have connected to it and running here now : ")
    print(" Chrome session ID ",session_id)
    print(" executor_url",executor_url)

except Exception:
    print("No Driver window open - make a new one")
    driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()),options=myoptions)
    session_id = driver.session_id 
    executor_url = driver.command_executor._url

OK after mixing various solutions shared on here and tweaking I have this working now as below. Script will use previously left open chrome window if present - the remote connection is perfectly able to kill the browser if needed and code functions just fine.

I would love a way to automate the getting of session_id and url for previous active session without having to write them out to a file during hte previous session for pick up...

This is my first post on here so apologies for breaking any norms

from selenium import webdriver
from selenium.webdriver.remote.webdriver import WebDriver

#Set manually - read/write from a file for automation
session_id =  "e0137cd71ab49b111f0151c756625d31"
executor_url = "http://localhost:50491"

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
 

remote_session = 0

#Try to connect to the last opened session - if failing open new window
try:
    driver = attach_to_session(executor_url,session_id)
    driver.current_url
    print(" Driver has an active window we have connected to it and running here now : ")
    print(" Chrome session ID ",session_id)
    print(" executor_url",executor_url)

except Exception:
    print("No Driver window open - make a new one")
    driver = webdriver.Chrome(service=Service(ChromeDriverManager().install()),options=myoptions)
    session_id = driver.session_id 
    executor_url = driver.command_executor._url
沧桑㈠ 2025-02-13 06:07:16

这是2023年对我有用的东西。python代码:

from selenium import webdrive

# driver = webdriver.Firefox()
# executor_url = driver.command_executor._url
# session_id = driver.session_id
# print(executor_url)
# print(session_id)


class SessionRemote(webdriver.Remote):
    def start_session(self, desired_capabilities, browser_profile=None):
        # Skip the NEW_SESSION command issued by the original driver
        # and set only some required attributes
        self.w3c = True


driver = SessionRemote(command_executor='your.executor.url', desired_capabilities=None)
driver.session_id = 'your-session-id'

我在 this 重复的线程和类定义在Stackoverflow上的另一个线程上,我现在找不到。取消注释前5行并评论最后2行。然后进行相反的操作,然后添加您的 url sessionId

编辑:仅当您在单独的终端中运行第一部分时才有效。您可以在另一个终端中运行第二部分,并根据需要将其重复多次。

Here is something that works for me in 2023. Python code:

from selenium import webdrive

# driver = webdriver.Firefox()
# executor_url = driver.command_executor._url
# session_id = driver.session_id
# print(executor_url)
# print(session_id)


class SessionRemote(webdriver.Remote):
    def start_session(self, desired_capabilities, browser_profile=None):
        # Skip the NEW_SESSION command issued by the original driver
        # and set only some required attributes
        self.w3c = True


driver = SessionRemote(command_executor='your.executor.url', desired_capabilities=None)
driver.session_id = 'your-session-id'

I found a partial answer on this duplicate thread and the class definition on another thread on stackoverflow which I cannot find right now. Uncomment the first 5 lines and comment the last 2. Then do the opposite and add your URL and sessionID.

EDIT: It only works if you run the first part in a separate terminal. You can run the second part in another terminal and reuse it as many times as you want.

安人多梦 2025-02-13 06:07:16

多亏了原始海报开始此主题,这对我来说是非常有用的讨论,可以为我的Python技能提供特定的用例。

我尝试了 Todor's 上面发布的方法,并且在以下设置方面未成功
OS = Mac OS Ventura; python = 3.10.4; se = 4.12.0

但是,我从 shabad 这很有帮助。

这是我针对上述设置的代码,这些代码更优雅地

苹果脚本,以启动具有特定配置文件python代码的新会话

do shell script "open -a /Applications/Microsoft\\ Edge.app --args --remote-debugging-port=9222 --user-data-dir='~/seEdge'"

以附加到本会话

# Python code to attach to a specific browser session
from selenium import webdriver
import time # used to add delay in the automation script

# set option to make execution of automation scripts
edge_options = webdriver.EdgeOptions()
edge_options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
driver = webdriver.Edge(options=edge_options)

Thanks to the original poster to start this topic, and this has been very helpful discussion for me to pick up on my Python skills for a specific use-case.

I tried Todor's method posted above and was not successful with the following setup
OS = Mac OS Ventura; Python = 3.10.4; SE = 4.12.0

However, I found this method from Shabad that was helpful.

Here is my code for the above settings that worked more elegantly

Apple Script to launch a new session with a specific profile

do shell script "open -a /Applications/Microsoft\\ Edge.app --args --remote-debugging-port=9222 --user-data-dir='~/seEdge'"

Python code to attach to this session

# Python code to attach to a specific browser session
from selenium import webdriver
import time # used to add delay in the automation script

# set option to make execution of automation scripts
edge_options = webdriver.EdgeOptions()
edge_options.add_experimental_option("debuggerAddress", "127.0.0.1:9222")
driver = webdriver.Edge(options=edge_options)
触ぅ动初心 2025-02-13 06:07:16

大约一个月前(2024年10月上旬),我找到了这个线程,并根据Stiky的代码建立了自己的POC,只是为了确认其有效。不幸的是,从那以后,破坏该方法的Selenium Python代码库发生了更改。

  1. command_executor不再具有._URL,但是可以在新类中找到[executor] ._ client_config._remote_server_addr,
  2. 但是,最终根本不调用driver.quit()并不会使浏览器运行以将其重新连接到.. .. 。
  3. ​效果。

将此功能作为官方功能真是太好了,但是我可能被迫将我的特定项目求助,以保持长期寿命的“所有者”流程将网络驱动器持续使用,但我需要它多久。

About a month ago (early Oct 2024) I found this thread and built my own POC based on stiky's code just to confirm it worked. Unfortunately, since then there have been changes to the Selenium Python codebase that break that method.

  1. The command_executor no longer has ._url, but it can be found in a new class as [executor]._client_config._remote_server_addr
  2. However, simply not calling driver.quit() at the end does not leave the browser running to be reconnected to... I'll do some looking around to see if I can understand what changed and if there's a way around it...
  3. But, it begs the question of long-term viability, if the method relies on "private" variables and side-effects.

It would be nice to have this capability as an official feature, but I may be forced to rearchitect my particular project around keeping a long-lived "owner" process holding the webdriver for however long I need it.

扮仙女 2025-02-13 06:07:16

不介绍为什么您认为离开开放式浏览器窗口会解决慢速的问题,您真的不需要手柄来做到这一点。如您所提到的那样,只需继续运行测试而不关闭会话或不调用driver.quit()而无需调用即可。这里的问题是自己的跑步者的框架吗?喜欢黄瓜吗?

无论如何,您必须具有一些“设置”和“清理”代码。因此,您需要做的是在“清理”阶段确保浏览器恢复其初始状态。这意味着:

  • 显示空白页的
  • cookie已在会话中删除

Without getting into why do you think that leaving an open browser windows will solve the problem of being slow, you don't really need a handle to do that. Just keep running the tests without closing the session or, in other words, without calling driver.quit() as you have mentioned yourself. The question here though framework that comes with its own runner? Like Cucumber?

In any case, you must have some "setup" and "cleanup" code. So what you need to do is to ensure during the "cleanup" phase that the browser is back to its initial state. That means:

  • Blank page is displayed
  • Cookies are erased for the session
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文