ferrum icon indicating copy to clipboard operation
ferrum copied to clipboard

How can I connect to the existing Chrome and control it?

Open sonymag opened this issue 3 years ago • 17 comments

So, I need to control my Chrome, which I starts with terminal command: google-chrome --remote-debugging-port=9222

For example with Golang library, I can do it like this:

func main() {
	u := "ws://localhost:9222/devtools/page/D4ACC2FBE3AF8A9C8F0400C66E2094E0"
	rod.New().ControlURL(u).MustConnect().MustPage("https://example.com")
	time.Sleep(10 * time.Second)
}

And now I can control my browser. So, Im trying to do the same with Ferrum:

browser = Ferrum::Browser.new(
  url: "http://localhost:9222",
  # browser_path: "/usr/bin/google-chrome-stable",
  headless: true,
)
browser.goto("https://google.com")
browser.screenshot(path: "google.png")
sleep 100
browser.quit

and it`s creates a new Chrome instance with Incognito mode. But, I need to control already existing Chrome.

sonymag avatar Dec 11 '22 00:12 sonymag

So, I found some library "chrome_remote" and It works well, I can connect to Chrome(google-chrome --remote-debugging-port=9222) with this code:

require 'chrome_remote'
require 'base64'

chrome = ChromeRemote.client

# Enable events
chrome.send_cmd "Network.enable"
chrome.send_cmd "Page.enable"

# Setup handler to log network requests
chrome.on "Network.requestWillBeSent" do |params|
  puts params["request"]["url"]
end

# Navigate to github.com and wait for the page to load
chrome.send_cmd "Page.navigate", url: "https://example.com"
chrome.wait_for "Page.loadEventFired"

# Take page screenshot
response = chrome.send_cmd "Page.captureScreenshot"
File.write "screenshot.png", Base64.decode64(response["data"])

sonymag avatar Dec 11 '22 19:12 sonymag

I just checked locally, everything works just fine. Started the browser /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --headless=true started the browser browser = Ferrum::Browser.new(url: "http://localhost:9222") went to google browser.go "http://google.com".

The solution you posted is not related to Ferrum project at all honestly.

route avatar Dec 12 '22 09:12 route

I just checked locally, everything works just fine. Started the browser /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222 --headless=true started the browser browser = Ferrum::Browser.new(url: "http://localhost:9222") went to google browser.go "http://google.com".

The solution you posted is not related to Ferrum project at all honestly.

My steps =>

  1. Run Chrome with --remote-debugging-port=9222 (I want to see Chrome, so --headless=flase)
  2. Run my app.rb script: browser = Ferrum::Browser.new( url:"http://localhost:9222", headless: false, ) browser.goto("https://ya.ru") browser.quit

Now I have 2 Chrome windows, but I need only one. Screenshot from 2022-12-12 23-11-14

sonymag avatar Dec 12 '22 20:12 sonymag

On step 2 the only option you have to pass is :url. Try it

route avatar Dec 13 '22 04:12 route

On step 2 the only option you have to pass is :url. Try it

My friend, nothing has changed, I have 2 Chormes Screenshot from 2022-12-13 23-47-18

sonymag avatar Dec 13 '22 20:12 sonymag

That's because there are 2 contexts here. When you run the browser with --remote-debugging-port=9222 it spawns your Chrome profile, with the pages you visited before, but when you want Chrome to visit "ya.ru", it creates for you an incognito profile and does so obviously in another window. Even though if you know the context id of the existing opened session Chrome does not allow you to create any pages in it, throwing an error:

context_id = "3F44386AB32348B20EC60B793358267A"
browser = Ferrum::Browser.new(url: "http://localhost:9222")
browser.command("Target.createTarget", browserContextId: context_id, url: "https://google.com")
▶ {"method":"Target.createTarget","params":{"browserContextId":"3F44386AB32348B20EC60B793358267A","url":"https://google.com"},"id":2}
◀ {"id":2,"error":{"code":-32000,"message":"Failed to find browser context with id 3F44386AB32348B20EC60B793358267A"}}

Thus you will always see 2 windows.

route avatar Dec 14 '22 06:12 route

So does it mean, I can't control my Chrome profile?

По русски: Дмитрий, скажи пожалуйста, я могу подкючаться к браузеру, который запускаю с помощью терминала(--remote-debugging-port=9222). Например, в Go example, я так же запускаю браузер и могу контролировать именно эту сессию:

func main() {
	u := "ws://localhost:9222/devtools/page/D4ACC2FBE3AF8A9C8F0400C66E2094E0"
	rod.New().ControlURL(u).MustConnect().MustPage("https://example.com")
	time.Sleep(10 * time.Second)
}

Thx in advance!

sonymag avatar Dec 14 '22 14:12 sonymag

Я вот только сегодня пробовал, запускал хром (он открывал мой профиль) с флагом remote-debugging-port=9222 и мне приходили события о всех открытых табах, в них был browserContextId который я затем пытался использовать чтобы создать новый таб командой browser.command("Target.createTarget", browserContextId: context_id, url: "https://google.com") но хром ругался и не давал создать такую страницу. Если у тебя есть полный пример кода на Go я могу скомпилить и поанализировать, но я не думаю что тут дело в языке. Я если честно немного удивлен что можно.

route avatar Dec 14 '22 17:12 route

Я вот только сегодня пробовал, запускал хром (он открывал мой профиль) с флагом remote-debugging-port=9222 и мне приходили события о всех открытых табах, в них был browserContextId который я затем пытался использовать чтобы создать новый таб командой browser.command("Target.createTarget", browserContextId: context_id, url: "https://google.com") но хром ругался и не давал создать такую страницу. Если у тебя есть полный пример кода на Go я могу скомпилить и поанализировать, но я не думаю что тут дело в языке. Я если честно немного удивлен что можно.

Во первых, спасибо тебе, что идешь навстречу, это дорого стоит. Во вторых, вот пример кода на Go:

package main

import (
	"github.com/go-rod/rod"
)
func main() {
	u := "ws://127.0.0.1:9222/devtools/browser/5dc1bb0c-dae1-4d96-bfc4-9a6af8b98331"
	rod.New().ControlURL(u).MustConnect().MustPage("https://example.com")
}

Запускаем с Chrome с помощью терминала(--remote-debugging-port=9222), берем ws строку из терминала, добавляем в переменную u и запускаем код. Он подключается к текущей сессии(запущенному Chrome) и переходит на страницу: "https://example.com". Сам пример есть в док, https://go-rod.github.io/#/custom-launch

Так же есть похожий гем на ваш, chrome_remote, он тоже может подключаться к текущей сессии, которая зарущена через терминал и управлять ею, я писал выше:

require 'chrome_remote'

chrome = ChromeRemote.client(
  host: 'localhost', # optional (default: localhost). Host of the Chrome remote server
  port: 9222,        # optional (default: 9222). Port of the Chrome remote server
  new_tab: false     # optional (default: false). Whether to use the browser's current tab or a new one
)
chrome.send_cmd "Page.navigate", url: "https://github.com"

https://github.com/cavalle/chrome_remote

sonymag avatar Dec 14 '22 21:12 sonymag

Cпасибо, я тогда исследую, но это не будет быстро, скорее всего результат будет только на следующей неделе, потому что буду в дороге.

route avatar Dec 15 '22 06:12 route

Cпасибо, я тогда исследую, но это не будет быстро, скорее всего результат будет только на следующей неделе, потому что буду в дороге.

Спасибо большое!

sonymag avatar Dec 15 '22 08:12 sonymag

I needed the same functionality and have added it here: https://github.com/kburkhardt/ferrum/commit/c5373a3a50f7a7aab01a78ae189e337b5cdfe11a

This is based on the approach from the 'chrome_remote' gem.

Using the example from above, the following should work:

browser = Ferrum::Browser.new(url: "http://localhost:9222", headless: false, new_tab: false)
browser.goto("https://google.com")
browser.screenshot(path: "google.png")
sleep 100
browser.quit

Let me know if you have any questions or issues with this. I'm still learning how the ferrum gem is structured, so I might have missed something. But, great work! Looking forward to doing more with it.

kburkhardt avatar Jan 01 '23 06:01 kburkhardt

I needed the same functionality and have added it here: kburkhardt@c5373a3

This is based on the approach from the 'chrome_remote' gem.

Using the example from above, the following should work:

browser = Ferrum::Browser.new(url: "http://localhost:9222", headless: false, new_tab: false)
browser.goto("https://google.com")
browser.screenshot(path: "google.png")
sleep 100
browser.quit

Let me know if you have any questions or issues with this. I'm still learning how the ferrum gem is structured, so I might have missed something. But, great work! Looking forward to doing more with it.

When he will pull it? Maybe I should copy your code)))

sonymag avatar Jan 30 '23 23:01 sonymag

Also interested in this

carlagomesah avatar Jan 29 '24 17:01 carlagomesah

@route I tried the newest master and it's still not possible to connect to an open Chrome app with the remote port set. It always opens a new Incognito window.

options = {
  url: "http://localhost:9222",
  headless: false,
  ws_url: "ws://localhost:9222",
  ignore_default_browser_options: true,
  port: 9222,
  host: "localhost",
}
@browser = Ferrum::Browser.new(**options)

pusewicz avatar Feb 07 '24 09:02 pusewicz

Hm, so is there no way to connect to an existing browser context (say one created by Ferrum, not even the existing default context) and control it from multiple Ferrum clients? I found a related comment from a few years back but the parameters have changed and I can't get this to work for me: https://github.com/rubycdp/ferrum/issues/34#issuecomment-611589596. I haven't tried the approach from the fork above though...

ibrahima avatar Jun 14 '24 02:06 ibrahima

One thing I've been trying to understand is what the purpose of the discover method is in the Context class. I would have expected it to allow Ferrum to list and use existing Targets, but that does not seem to occur - when I try to list the targets on a Ferrum Browser instance, I don't get any existing ones until I create a new page. Maybe this is just legacy code?

https://github.com/rubycdp/ferrum/blob/443b268543da5347513dafb0ec7ccfe8b609b199/lib/ferrum/contexts.rb#L109

Been looking through https://github.com/aslushnikov/getting-started-with-cdp/blob/master/README.md#protocol-fundamentals to try to understand what's happening but I don't fully understand yet. Would appreciate any guidance on what Ferrum is intending to do here and if there is some way to attach to existing targets - I suspect not, but I am wondering why and if that was previously possible (I see mention of attachToTarget in the README).

ibrahima avatar Jun 19 '24 02:06 ibrahima