Appium appium 中怎么以中文 text 定位?

June · 2015年02月25日 · 最后由 洛凉 回复于 2018年11月26日 · 4373 次阅读

想用 xpth 中的中文文本来定位元素。但好像不支持中文。
比如:

driver.findElementByXPath("//android.widget.TextView[@text='中文']").click();

还有 scrollTo() 等,好像都不支持中文

driver.scrollTo("中文");

英文都能找到,但中文就不行。有解决方法吗?

共收到 28 条回复 时间 点赞

你的代码是直接复制粘贴上来的吗?第一个代码的 xpath 有错,@text=‘中文'第一个引号是中文引号。

Appium 是支持中文的定位和输入的,参考下 Python 的写法

driver.find_element_by_xpath('//android.widget.TextView[contains(@text, "北京")]').click()
June #3 · 2015年02月25日 Author

@chenhengjie123 这个我写错了。代码里的符号是正确的。

June #4 · 2015年02月25日 Author

@seasoncool2011
python 符号写法和 Java 的是反的?
java 的写法好像是这样的。

driver.find_element_by_xpath("//android.widget.TextView[contains(@text, '北京')]").click()
匿名 #5 · 2015年02月25日

这个定位不到应该不是中文的问题,另外 python 的单双引号可以互换,只要成对出现就可以了

June #6 · 2015年02月25日 Author

@link1220 英文就没有问题,就中文不行。
不知道 java 可否互换单双引号。

driver.find_element_by_xpath("//*[contains(text(), '北京')]").click()
试试?

June #8 · 2015年02月25日 Author

@weamylady 这个没试过,试过这个

driver.findElementByXPath("//*[text()='中文']").click();

也不行。

你用的什么编码?是 utf-8 吗?
要支持中文应该要 utf-8 的吧。
可以肯定 appium 的 xpath 定位应该是支持中文的。

June #10 · 2015年02月25日 Author

用 windows 没有问题,MAC 不行。
@chenhengjie123 没看懂你的意思。能具体说明吗?呵呵~~

我的意思是你的 Java 文件应该使用 utf-8 编码。这样中文的编码才和 app 对应元素一致(android testview 内容默认使用 utf-8 编码)。
不过我也没使用 Java 的 client 试验过。明天试一下。

June #12 · 2015年02月25日 Author

@chenhengjie123 是 utf-8。

用 appium 的 bootstrap debug 过了, 确实 appium 1.3.5 在 android 平台下的 findElementByXPath 不支持中文

appium 用 xpath 定位的原理是先用 dump 把当前界面所有元素保存到一个 xml 文件里面,然后再读取 dump 出来的 xml 文件,最后用 xpath 去查那个 xml 文件。(xpath 本来设计就是用来做 xml 的元素定位的)
不能支持中文(严格地说是不支持所有非 ascii 字符)的原因是这个 dump 用的是 bootstrap 里面自己的 dump,而不是 uiautomator 的,这个 dump 会把所有不支持的字符替换成?,所以你用包含中文的 xpath 去找会找不到。

借个地方贴一下相关源码方便后面研究:
通过 xpath 查找元素的函数:
io.appium.android.bootstrap.utils.XMLHierarchy.java

public static ArrayList<ClassInstancePair> getClassInstancePairs(String xpathExpression)
          throws ElementNotFoundException, InvalidSelectorException, ParserConfigurationException {
    XPath xpath = XPathFactory.newInstance().newXPath();
    XPathExpression exp = null;
    try {
      exp = xpath.compile(xpathExpression);
    } catch (XPathExpressionException e) {
      throw new InvalidSelectorException(e.getMessage());
    }

    Node formattedXmlRoot;

    formattedXmlRoot = getFormattedXMLDoc();

    return getClassInstancePairs(exp, formattedXmlRoot);
  }

  public static ArrayList<ClassInstancePair> getClassInstancePairs(XPathExpression xpathExpression, Node root) throws ElementNotFoundException {

    NodeList nodes;
    try {
      nodes = (NodeList) xpathExpression.evaluate(root, XPathConstants.NODESET);
    } catch (XPathExpressionException e) {
      e.printStackTrace();
      throw new ElementNotFoundException("XMLWindowHierarchy could not be parsed: " + e.getMessage());
    }

    ArrayList<ClassInstancePair> pairs = new ArrayList<ClassInstancePair>();
    for (int i = 0; i < nodes.getLength(); i++) {
      if (nodes.item(i).getNodeType() == Node.ELEMENT_NODE) {
        try {
          pairs.add(getPairFromNode(nodes.item(i)));
        } catch (PairCreationException e) { }
      }
    }

    return pairs;
  }

替换字符的函数:
io.appium.uiautomator.core.AccessibilityNodeInfoDumper.java

private static String stripInvalidXMLChars(CharSequence cs) {
        StringBuilder ret = new StringBuilder();
        char ch;
        for (int i = 0; i < cs.length(); i++) {
            ch = cs.charAt(i);
            // code below from Html#withinStyle, this is a temporary workaround because XML
            // serializer does not support surrogates
            if (ch >= 0xD800 && ch <= 0xDFFF) {
                if (ch < 0xDC00 && i + 1 < cs.length()) {
                    char d = cs.charAt(i + 1);
                    if (d >= 0xDC00 && d <= 0xDFFF) {
                        i++;
                        ret.append("?");
                    }
                }
            } else if (ch > 0x7E || ch < ' ') {
                ret.append("?");
            } else {
                ret.append(ch);
            }
        }
        return ret.toString();
    }

其他方法(如 findElementByAccessibilityId)能找到包含中文的元素的原因是它直接使用 uiautomator 的对应方法。而 uiautomator api 并没有根据 xpath 查找元素的方法,所以 appium 是自己另外实现的。

这里替换掉字符的原因应该是基于安全性考虑( evaluate 是一个权限很高的函数,不对参数进行过滤的话很危险)。

@doctorq 我们那个项目第一个任务可以考虑修复这个问题,让 appium 支持通过含有非 ascii 字符的 xpath 来查找元素。

#13 楼 @chenhengjie123 可以啊,修改一下 stripInvalidXMLChars 应该可以解决了。

June #15 · 2015年02月26日 Author

用 windows 就能找到中文 xpath,就是在 MAC 找不到。

@jinjun0620 这么神奇?今晚回去研究研究。这是一个很有趣的问题。因为根据上面的分析应该无论哪个平台都找不到的(bootstrap 是在 android 上运行的)。

找到 windows 下可以使用的可能原因了。我分析的源码是master上的 (1.3.5解析 xpath 的部分和我分析的源码一样)...切换到1.3.4的 tag 后看到 dump file 用的是 UiDevice 的 dump 方法。
相关源码:
appium-1.3.4:
https://github.com/appium/appium/blob/v1.3.4/lib/devices/android/bootstrap/src/io/appium/android/bootstrap/utils/XMLHierarchy.java
appium-1.3.5:
https://github.com/appium/appium/blob/v1.3.5/lib/devices/android/bootstrap/src/io/appium/android/bootstrap/utils/XMLHierarchy.java

@jinjun0620 你在 windows 下用的是打包好的 exe 程序吗?windows 的 exe 最新版本的是 1.3.4.1,mac 的最新版本是 1.3.5。所以用 exe 不会出现这个问题。

github 上已经有人报过类似的 bug 了:https://github.com/appium/appium/issues/4611
官方团队目前还没有把修复这个 bug 提上 milestone。

@doctorq 我们要 fix 这个的话最好先和 appium 的 JessicaSachs 打个招呼,免得重复操作。现在貌似所有 issue 默认都是自动 assign 给他的。

#19 楼 @chenhengjie123 直接修了 发 pull request

June #22 · 2015年02月27日 Author

@chenhengjie123

我用的 appium 不是 exe 或者 app。都是用 npm 下的。
MAC 版是 1.3.5,windows 版是 1.3.4

原来是 1.3.5 版问题。呵呵~~谢谢~!

June #23 · 2015年02月27日 Author

@chenhengjie123 用 1.3.4 在 MAC 可以找到 xpath。非常感谢~~!

#15 楼 @jinjun0620 不对呀 我是 AppiumForWindows-1.3.4.1 还是找不到中文 xpath

June #25 · 2015年03月09日 Author

@misnull 你在 1.3.4 试试。

driver.findElementByName("员工编号").sendKeys(userCode);

这样写也可以啊

我是用 mac 上可以,换到 win 上执行反而不行。。😂 然后 log 报的也是乱码。。无语了。。中文 xpath@text 最后能定位到么?

driver.find_element_by_xpath("//android.view.View[contains(@text, 'xxx')]").click() 能定位到,谢谢大家,嘿嘿

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册