如何按图片查找元素

时间:2019-06-19 10:02:39

标签: javascript java selenium selenium-webdriver groovy

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

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

由于硒为创建自己的自定义定位器策略以查找元素提供了便利,因此我正在尝试创建 image 定位器,该定位器可以使用 base64 找到元素{1}}就像appium一样。

图像定位器的要点:

  1. 使用URL启动浏览器
  2. 捕获页面的屏幕截图
  3. 从屏幕快照中检测子图像的 x y 位置
  4. 使用页面中的 x y 位置查找元素

要完成此任务,我将创建自定义String定位器,如下所示:

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>")) 检测subimage的确切坐标以使用坐标查找元素的更好的库之外,我能够实现所有功能。

有人可以建议我一种更好的方法来完成此任务吗?

3 个答案:

答案 0 :(得分:2)

您可以选择其他选项,例如:

  1. 您可以使用Java Bindings for OpenCV来在主屏幕快照中查找子图像,请查看Template Matching文章以获取全面的说明和代码段。
  2. Project Sikuli提供了一些用于图像识别/交互的简单API
  3. SeeTest Automation为图像模板提供图像识别和Object Repository模式实现

答案 1 :(得分:0)

为不熟悉Java绑定和OpenCV的用户添加另一个选项:Selenium IDE++包含内置的图像识别命令:

  • XClick (image)
  • XMove (image)
  • 和OCR支持:XClick (ocr=text)

有关更多详细信息,请参见UI testing页。您可以通过command line从Java调用它。

答案 2 :(得分:-1)

按照@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);

希望对所有人都有帮助。