首页 > 解决方案 > ruby selenium execute_script Net::ReadTimeout

问题描述

我正在使用我的脚本(不是测试)中的 Capybara::Selenium 从外部站点获取一些图像。页面加载正常,所有图像也都加载了,我看到了它们,但是任何执行函数的尝试page.session.driver.evaluate_script总是抛出Net::ReadTimeout: Net::ReadTimeout with #<TCPSocket:(closed)>

完整代码:

require 'capybara-webkit'
require 'selenium-webdriver'

JS_GET_IMAGE = <<~EJSGETIMAGE
  var img = document.getElementById('requestImage');
  const cvs = document.createElement('canvas');
  cvs.width = img.width;
  cvs.height = img.height;
  cvs.getContext('2d').drawImage( img, 0, 0 );
return cvs.toDataURL("image/png");
EJSGETIMAGE

session = Capybara::Session.new :selenium
page = session.visit Cfg.site.url
driver = session.driver.browser
driver.manage.timeouts.script_timeout = 5000

@img = driver.execute_async_script JS_GET_IMAGE

好的,我开始测试非常简单的脚本,但也出现了同样的错误。

page.session.driver.browser.execute_async_script("setTimeout(arguments[0], 2000)")

我也使用session = Capybara::Session.new :selenium_headless并得到了同样的错误。

非常感谢任何帮助。

标签: rubyseleniumcapybara

解决方案


capybara-webkit小事第一 -如果您使用 Selenium 驱动程序,则无需加载。

现在进入主要问题。执行 JS 时无需直接在驱动程序上调用方法,而只需调用 Capybara 方法execute_scriptevaluate_scriptevaluate_async_script. 这些evaluate_xxx方法适用于您期望返回值时,该execute_script方法适用于您不关心任何返回值时。evaluate_async_script接收回调函数作为需要调用以返回值的最后一个参数,但是您的 JS_GET_IMAGE 似乎从来没有这样做(也不需要,因为它不是异步的)所以最好只使用evaluate_script. 另一个要求evaluate_script是评估的代码必须是单个语句。为了满足这个要求,我们可以使用 IIFE。

require "capybara/dsl"

JS_GET_IMAGE = <<~EJSGETIMAGE
  (function(){
     var img = document.getElementById('requestImage');
     const cvs = document.createElement('canvas');
     cvs.width = img.width;
     cvs.height = img.height;
     cvs.getContext('2d').drawImage( img, 0, 0 );
     return cvs.toDataURL("image/png");
  })()
EJSGETIMAGE

session = Capybara::Session.new :selenium
session.visit Cfg.site.url
@img = session.evaluate_script JS_GET_IMAGE

尽管恕我直言,最好让 Capybara 找到元素并将其传递给 JS 函数,使其更加灵活并利用 Capybara 等待元素出现

require "capybara/dsl"

JS_GET_IMAGE = <<~EJSGETIMAGE
  (function(img){
     const cvs = document.createElement('canvas');
     cvs.width = img.width;
     cvs.height = img.height;
     cvs.getContext('2d').drawImage( img, 0, 0 );
     return cvs.toDataURL("image/png");
  })(arguments[0])
EJSGETIMAGE

session = Capybara::Session.new :selenium
session.visit Cfg.site.url
img_element = session.find('#requestImage')
@img = session.evaluate_script JS_GET_IMAGE, img_element

推荐阅读