【问题标题】:How to find an element by image如何通过图像查找元素
【发布时间】:2019-06-19 10:02:39
【问题描述】:

我们知道selenium 支持多个定位器策略来查找网页上的元素。

但我的要求不同,我有一些站点,其中 selenium 支持的任何定位器都不足以找到唯一的元素。

由于 selenium 提供了创建自己的自定义定位器策略来查找元素的便利,我正在尝试创建 image 定位器,它可以使用 base64 @ 987654324@的子图为appium做的。

图像定位器的积分:

  1. 使用 URL 启动浏览器
  2. 截取页面截图
  3. 从截图中检测子图的x,y位置
  4. 使用页面中的 xy 位置查找元素

为了完成这项任务,我正在创建自定义Image 定位器,如下所示:

public class ByImage extends By {

    String imageBase64String

    /**
     * @param imageBase64String
     */
    public ByImage(String imageBase64String) {
        this.imageBase64String = imageBase64String
    }

    @Override
    public List<WebElement> findElement(SearchContext context) {
        List<WebElement> els = findElements(context)
        if (els) {
            return els.get(0)
        }
        throw new NoSuchElementException("Element not found")
    }

    @Override
    public List<WebElement> findElements(SearchContext context) {
       //Get current screenshot
        byte[] screenshotByte = ((TakesScreenshot)context).getScreenshotAs(OutputType.BYTES))
        byte[] subImgToFindByte = DatatypeConverter.parseBase64Binary(imageBase64String)
        //Convert buffred image to get height and width of subimage
        BufferedImage bufferedSubImgToFind = ImageIO.read(new ByteArrayInputStream(subImgToFindByte ));

        //Here I need a mechanism to get coordinates of sub image from screenshot
        //Suppose I able to find x, y
        double x
        double y

        //Now find element using coordinates
        //Now calculate center point
        int centerX = int(x + (bufferedSubImgToFind.getWidth() / 2))
        int centerY = int(y + (bufferedSubImgToFind.getHeight() / 2))

        JavascriptExecutor js = ((JavascriptExecutor)context)

        return js.executeScript("return document.elementsFromPoint(arguments[0], arguments[1]);", centerX, centerY)
      }   
  }

现在测试用例如下:

WebDriver driver = new ChromeDriver()
driver.get("<URL>")
WebElement elementByImage = driver.findElement(new ByImage("<Base64 String of the subimage>"))

除了一个更好的库来检测subimageimage 的精确坐标以使用坐标查找元素之外,我能够实现一切。

谁能建议我更好的方法来完成这项任务?

【问题讨论】:

  • 在图片标签的src 中找不到图片名称或类似信息?请发布一些相同的 HTML,以便我们查看。您知道图像的大小或有关它的任何信息吗?如果您可以在页面的较大屏幕截图中找到它,那么您必须了解它的一些详细信息。你知道什么?
  • @JeffC 我只是想创建一个自定义定位器,它能够使用图像 base64 字符串查找元素,在这种情况下,我们只需要使用任何工具捕获特定元素的屏幕截图并转换将其转换为 base64 字符串并将其传递给此自定义定位器,在此自定义定位器中,它会截取当前可见页面的屏幕截图,并尝试使用诸如 OpenCV 之类的库从屏幕截图中找到该子图像。
  • 现在如果用户从大屏幕截取屏幕截图并尝试在小屏幕上找到它,大小将很重要,在这种情况下,我们可以简单地 scale 带有页面当前屏幕截图的子图像,以便我们可以很容易地得到正确的坐标。这是我试图实现的基于图像的通用解决方案,这就是为什么我们在这里不需要任何HTML .. 谢谢.. :)
  • @JeffC 为了更好地理解,请查看appiumpro.com/editions/32。这是 appium 教程,其中 appium 正在做我想做的事情

标签: javascript java selenium selenium-webdriver groovy


【解决方案1】:

您可以选择不同的选项,例如:

  1. 您可以使用Java Bindings for OpenCV 来查找主屏幕截图中的子图像,查看Template Matching 文章以获得全面的解释和代码sn-ps。
  2. Project Sikuli 提供了一些简单的图像识别/交互API
  3. SeeTest Automation 为图像模板提供图像识别和Object Repository 模式实现

【讨论】:

    【解决方案2】:

    正如@Dmitri 建议的那样,我将使用Java Bindings for OpenCV

    download appropriate OpenCV 并将其提取到classpath 并尝试获取坐标为:

    import org.opencv.core.Core;
    import org.opencv.core.Core.MinMaxLocResult;
    import org.opencv.core.CvType;
    import org.opencv.core.Mat;
    import org.opencv.core.MatOfByte;
    import org.opencv.core.Point;
    import org.opencv.imgcodecs.Imgcodecs;
    import org.opencv.imgproc.Imgproc;
    
    byte[] screenshotByte = ((TakesScreenshot)context).getScreenshotAs(OutputType.BYTES))
    byte[] subImgToFindByte = DatatypeConverter.parseBase64Binary(imageBase64String)
    
    System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
    Mat source = Imgcodecs.imdecode(new MatOfByte(screenshotByte), Imgcodecs.IMREAD_UNCHANGED);
    Mat template = Imgcodecs.imdecode(new MatOfByte(subImgToFindByte), Imgcodecs.IMREAD_UNCHANGED);
    
    int result_cols = source.cols() - template.cols() + 1;
    int result_rows = source.rows() - template.rows() + 1;
    Mat outputImage = new Mat(result_rows, result_cols, CvType.CV_32FC1);
    
    // Template matching method
    Imgproc.matchTemplate(source, template, outputImage, Imgproc.TM_SQDIFF_NORMED);
    
    MinMaxLocResult mmr = Core.minMaxLoc(outputImage);
    // Now get the point
    Point point = mmr.minLoc;
    double x = point.x;
    double y = point.y;
    
    //Now get the find the element using x, y after calculating center point.
    int centerX = int(x + (bufferedSubImgToFind.getWidth() / 2));
    int centerY = int(y + (bufferedSubImgToFind.getHeight() / 2));
    
    WebElement el = js.executeScript("return document.elementFromPoint(arguments[0], arguments[1]);", centerX, centerY);
    

    希望对大家有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-10-06
      • 2020-05-03
      • 2016-11-25
      • 2013-08-07
      • 1970-01-01
      • 2021-05-07
      • 2019-10-11
      相关资源
      最近更新 更多