class Rubium::Browser
Constants
- MAX_CONNECT_WAIT_TIME
- MAX_DEFAULT_TIMEOUT
Attributes
client[R]
devtools_url[R]
logger[R]
options[R]
pid[R]
port[R]
processed_requests_count[R]
Public Class Methods
new(options = {})
click to toggle source
# File lib/rubium/browser.rb, line 32 def initialize(options = {}) @options = options if @options[:enable_logger] @logger = Logger.new(STDOUT) @logger.progname = self.class.to_s end create_browser end
ports_pool()
click to toggle source
# File lib/rubium/browser.rb, line 21 def ports_pool @pool ||= RandomPort::Pool.new end
running_pids()
click to toggle source
# File lib/rubium/browser.rb, line 25 def running_pids @running_pids ||= [] end
Public Instance Methods
body()
click to toggle source
# File lib/rubium/browser.rb, line 97 def body response = @client.send_cmd "Runtime.evaluate", expression: 'document.documentElement.outerHTML' response.dig("result", "value") end
click(selector)
click to toggle source
# File lib/rubium/browser.rb, line 136 def click(selector) @client.send_cmd "Runtime.evaluate", expression: <<~js document.querySelector("#{selector}").click(); js end
close()
click to toggle source
# File lib/rubium/browser.rb, line 50 def close if closed? logger.info "Browser already has been closed" if options[:enable_logger] else Process.kill("HUP", @pid) self.class.running_pids.delete(@pid) self.class.ports_pool.release(@port) FileUtils.rm_rf(@data_dir) if Dir.exist?(@data_dir) @closed = true logger.info "Closed browser" if options[:enable_logger] end end
Also aliased as: destroy_driver!
closed?()
click to toggle source
# File lib/rubium/browser.rb, line 67 def closed? @closed end
current_response()
click to toggle source
# File lib/rubium/browser.rb, line 102 def current_response Nokogiri::HTML(body) end
evaluate_on_new_document(script)
click to toggle source
github.com/GoogleChrome/puppeteer/blob/master/lib/Page.js#L784 stackoverflow.com/questions/46113267/how-to-use-evaluateonnewdocument-and-exposefunction chromedevtools.github.io/devtools-protocol/tot/Page/#method-addScriptToEvaluateOnNewDocument
# File lib/rubium/browser.rb, line 157 def evaluate_on_new_document(script) @client.send_cmd "Page.addScriptToEvaluateOnNewDocument", source: script end
execute_script(script)
click to toggle source
# File lib/rubium/browser.rb, line 181 def execute_script(script) @client.send_cmd "Runtime.evaluate", expression: script end
fill_in(selector, text)
click to toggle source
# File lib/rubium/browser.rb, line 175 def fill_in(selector, text) execute_script <<~js document.querySelector("#{selector}").value = "#{text}" js end
goto(url, wait: options[:max_timeout] || MAX_DEFAULT_TIMEOUT)
click to toggle source
# File lib/rubium/browser.rb, line 71 def goto(url, wait: options[:max_timeout] || MAX_DEFAULT_TIMEOUT) logger.info "Started request: #{url}" if options[:enable_logger] if options[:restart_after] && processed_requests_count >= options[:restart_after] restart! end response = @client.send_cmd "Page.navigate", url: url # By default, after Page.navigate we should wait till page will load completely # using Page.loadEventFired. But on some websites with Ajax navigation, Page.loadEventFired # will stuck forever. In this case you can provide `wait: false` option to skip waiting. if wait != false # https://chromedevtools.github.io/devtools-protocol/tot/Page#event-frameStoppedLoading Timeout.timeout(wait) do @client.wait_for do |event_name, event_params| event_name == "Page.frameStoppedLoading" && event_params["frameId"] == response["frameId"] end end end @processed_requests_count += 1 logger.info "Finished request: #{url}" if options[:enable_logger] end
Also aliased as: visit
has_css?(selector, wait: 0)
click to toggle source
# File lib/rubium/browser.rb, line 116 def has_css?(selector, wait: 0) timer = 0 until current_response.at_css(selector) return false if timer >= wait timer += 0.2 and sleep 0.2 end true end
has_text?(text, wait: 0)
click to toggle source
# File lib/rubium/browser.rb, line 126 def has_text?(text, wait: 0) timer = 0 until body&.include?(text) return false if timer >= wait timer += 0.2 and sleep 0.2 end true end
has_xpath?(path, wait: 0)
click to toggle source
# File lib/rubium/browser.rb, line 106 def has_xpath?(path, wait: 0) timer = 0 until current_response.at_xpath(path) return false if timer >= wait timer += 0.2 and sleep 0.2 end true end
restart!()
click to toggle source
# File lib/rubium/browser.rb, line 43 def restart! logger.info "Restarting..." if options[:enable_logger] close create_browser end
send_key_on(selector, key)
click to toggle source
github.com/cyrus-and/chrome-remote-interface/issues/226#issuecomment-320247756 stackoverflow.com/a/18937620
# File lib/rubium/browser.rb, line 144 def send_key_on(selector, key) @client.send_cmd "Runtime.evaluate", expression: <<~js document.querySelector("#{selector}").dispatchEvent( new KeyboardEvent("keydown", { bubbles: true, cancelable: true, keyCode: #{key} }) ); js end
Private Instance Methods
convert_proxy(proxy_string)
click to toggle source
# File lib/rubium/browser.rb, line 260 def convert_proxy(proxy_string) ip, port, type, user, password = proxy_string.split(":") "#{type}://#{ip}:#{port}" end
create_browser()
click to toggle source
# File lib/rubium/browser.rb, line 187 def create_browser @processed_requests_count = 0 @port = options[:debugging_port] || self.class.ports_pool.acquire @data_dir = "/tmp/rubium_profile_#{SecureRandom.hex}" chrome_path = Rubium.configuration.chrome_path || Cliver.detect("chromium-browser") || Cliver.detect("google-chrome") raise ConfigurationError, "Can't find chrome executable" unless chrome_path command = %W( #{chrome_path} about:blank --remote-debugging-port=#{@port} --user-data-dir=#{@data_dir} ) + DEFAULT_PUPPETEER_ARGS command << "--headless" if ENV["HEADLESS"] != "false" && options[:headless] != false command << "--window-size=#{options[:window_size].join(',')}" if options[:window_size] if options[:user_agent] user_agent = options[:user_agent].respond_to?(:call) ? options[:user_agent].call : options[:user_agent] command << "--user-agent=#{user_agent}" end if options[:proxy_server] proxy_server = options[:proxy_server].respond_to?(:call) ? options[:proxy_server].call : options[:proxy_server] proxy_server = convert_proxy(proxy_server) unless proxy_server.include?("://") command << "--proxy-server=#{proxy_server}" end @pid = spawn(*command, [:out, :err] => "/dev/null") self.class.running_pids << @pid @closed = false counter = 0 begin counter += 0.2 and sleep 0.2 @client = ChromeRemote.client(port: @port) rescue Errno::ECONNREFUSED => e counter < MAX_CONNECT_WAIT_TIME ? retry : raise(e) end @devtools_url = "http://localhost:#{@port}/" # https://github.com/GoogleChrome/puppeteer/blob/master/lib/Page.js @client.send_cmd "Target.setAutoAttach", autoAttach: true, waitForDebuggerOnStart: false @client.send_cmd "Network.enable" @client.send_cmd "Page.enable" evaluate_on_new_document(options[:extension_code]) if options[:extension_code] set_cookies(options[:cookies]) if options[:cookies] if options[:urls_blacklist] || options[:disable_images] urls = [] if options[:urls_blacklist] urls += options[:urls_blacklist] end if options[:disable_images] urls += %w(jpg jpeg png gif swf svg tif).map { |ext| ["*.#{ext}", "*.#{ext}?*"] }.flatten urls << "data:image*" end @client.send_cmd "Network.setBlockedURLs", urls: urls end logger.info "Opened browser" if options[:enable_logger] end