有没有办法在 Selenium WebDriver 中使用 JavaScript 通过 XPath 获取元素?

Is there a way to get element by XPath using JavaScript in Selenium WebDriver?

提问人:pMan 提问时间:5/15/2012 最后编辑:ggorlenpMan 更新时间:10/11/2023 访问量:631482

问:

我正在寻找类似的东西:

getElementByXpath(//html[1]/body[1]/div[1]).innerHTML

我需要使用 JS 获取元素的 innerHTML(在 Selenium WebDriver/Java 中使用它,因为 WebDriver 本身找不到它),但如何?

我可以使用 ID 属性,但并非所有元素都有 ID 属性。

javascript selenium-webdriver xpath document.evaluate google-chrome-devtools

评论

2赞 RobG 5/15/2012
顺便说一句,和 选择器是多余的,因为 DIV 必须是 BODY 的后代(直接或更深层次),并且 BODY 必须是 HTML 的子级,所以只要文档中没有其他 DIV 元素,应该可以工作(尽管我对 XPath 表达式很生疏)。DOM 等价物是 (或者可能是 )。htmlbody//DIV[1]document.getElementsByTagName('div')[1]0

答:

11赞 RobG 5/15/2012 #1

您可以使用 javascript 的 document.evaluate 在 DOM 上运行 XPath 表达式。我认为它在 IE 6 的浏览器中以一种或另一种方式得到支持。

MDN:https://developer.mozilla.org/en-US/docs/Web/API/Document/evaluate

IE 支持 selectNodes

MSDN:https://msdn.microsoft.com/en-us/library/ms754523(v=vs.85).aspx

评论

8赞 Christopher Bales 1/31/2013
我想指出的是,{document.evaluate}在IE中不起作用。
2赞 Martin Spamer 5/15/2012 #2

假设您的目标是开发和测试屏幕映射的 xpath 查询。然后,要么使用 Chrome 的开发者工具。这允许您运行 xpath 查询以显示匹配项。或者在 Firefox >9 中,您可以使用 Web 开发人员工具控制台执行相同的操作。在早期版本中,请使用 x-path-finderFirebug

711赞 yckart 1/12/2013 #3

您可以使用:document.evaluate

计算 XPath 表达式字符串并返回 如果可能,请指定类型。

它是 w3 标准化的,并完整地记录了:https://developer.mozilla.org/en-US/docs/Web/API/Document.evaluate

function getElementByXpath(path) {
  return document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
}

console.log( getElementByXpath("//html[1]/body[1]/div[1]") );
<div>foo</div>

https://gist.github.com/yckart/6351935

Mozilla开发者网络上也有一个很好的介绍:https://developer.mozilla.org/en-US/docs/Introduction_to_using_XPath_in_JavaScript#document.evaluate


替代版本,使用:XPathEvaluator

function getElementByXPath(xpath) {
  return new XPathEvaluator()
    .createExpression(xpath)
    .evaluate(document, XPathResult.FIRST_ORDERED_NODE_TYPE)
    .singleNodeValue
}

console.log( getElementByXPath("//html[1]/body[1]/div[1]") );
<div>foo/bar</div>

评论

16赞 Will Sheppard 11/16/2013
神奇的数字 9 是什么意思?最好在这里使用命名常量。
11赞 yckart 11/16/2013
@WillSheppard developer.mozilla.org/en-US/docs/......XPathResult.FIRST_ORDERED_NODE_TYPE === 9
2赞 joopmicroop 4/11/2014
我编写了一个 getElementByXPath 函数,该函数支持 IE,但目前支持一些基本的 xpath。那里还有一个函数getElementXpath,它们可以很好地协同工作以满足我的需要。gist.github.com/Joopmicroop/10471650
0赞 yckart 7/5/2014
@CiroSantilli 是的,先生,它是:w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathEvaluator-evaluate
0赞 TechDog 10/2/2015
var xpathResult = document.evaluate( xpathExpression, contextNode, namespaceResolver, resultType, result );
303赞 Dmitry Semenyuk 12/12/2013 #4

在 Chrome 开发工具中,您可以运行以下命令:

$x("some xpath")

评论

1赞 Eric 1/31/2017
我已经玩过这个,它似乎有效,但是有关于此功能的任何文档吗?我什么也没找到。
13赞 iSWORD 11/20/2019
这需要更高。Firefox也支持它。
0赞 bomben 12/1/2020
事实上。但是 Selenium 并没有找到所有适用于 .$x("")
7赞 John Yepthomi 6/11/2021
$x在脚本中不起作用,它仅适用于控制台。接受的答案适用于在浏览器上下文中运行的脚本。
30赞 Jay 6/21/2014 #5

对于类似 chrome 命令行 api $x的内容(选择多个元素),请尝试:

var xpath = function(xpathToExecute){
  var result = [];
  var nodesSnapshot = document.evaluate(xpathToExecute, document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null );
  for ( var i=0 ; i < nodesSnapshot.snapshotLength; i++ ){
    result.push( nodesSnapshot.snapshotItem(i) );
  }
  return result;
}

这个 MDN 概述有帮助: https://developer.mozilla.org/en-US/docs/Introduction_to_using_XPath_in_JavaScript

2赞 Prerit Jain 2/24/2017 #6
public class JSElementLocator {

    @Test
    public void locateElement() throws InterruptedException{
        WebDriver driver = WebDriverProducerFactory.getWebDriver("firefox");

        driver.get("https://www.google.co.in/");


        WebElement searchbox = null;

        Thread.sleep(1000);
        searchbox = (WebElement) (((JavascriptExecutor) driver).executeScript("return document.getElementById('lst-ib');", searchbox));
        searchbox.sendKeys("hello");
    }
}

确保您为其使用了正确的定位器。

评论

1赞 Gaurav Thantry 6/9/2018
嗨,Prerit,问题是根据元素的 xpath 进行选择。您提供的解决方案是按 id 选择它。 :)
2赞 Alok Patel 4/12/2019 #7
**Different way to Find Element:**

IEDriver.findElement(By.id("id"));
IEDriver.findElement(By.linkText("linkText"));
IEDriver.findElement(By.xpath("xpath"));

IEDriver.findElement(By.xpath(".//*[@id='id']"));
IEDriver.findElement(By.xpath("//button[contains(.,'button name')]"));
IEDriver.findElement(By.xpath("//a[contains(.,'text name')]"));
IEDriver.findElement(By.xpath("//label[contains(.,'label name')]"));

IEDriver.findElement(By.xpath("//*[contains(text(), 'your text')]");

Check Case Sensitive:
IEDriver.findElement(By.xpath("//*[contains(lower-case(text()),'your text')]");

For exact match: 
IEDriver.findElement(By.xpath("//button[text()='your text']");

**Find NG-Element:**

Xpath == //td[contains(@ng-show,'childsegment.AddLocation')]
CssSelector == .sprite.icon-cancel
1赞 Dharit Mehta 9/11/2019 #8
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;

System.setProperty("webdriver.chrome.driver", "path of your chrome exe");
        WebDriver driver = new ChromeDriver();
        driver.manage().window().maximize();
        driver.get("https://www.google.com");

            driver.findElement(By.xpath(".//*[@id='UserName']")).clear();
            driver.findElement(By.xpath(".//*[@id='UserName']")).sendKeys(Email);

评论

1赞 NickCoder 9/11/2019
添加描述性答案。您的描述将帮助提问者快速理解您的解决方案。
7赞 Sameera De Silva 12/18/2019 #9

直奔主题,你可以很容易地使用 xapth .使用以下代码执行此操作的确切而简单的方法。请尝试提供反馈。谢谢。

JavascriptExecutor js = (JavascriptExecutor) driver;

    //To click an element 
    WebElement element=driver.findElement(By.xpath(Xpath));
    js.executeScript(("arguments[0].click();", element);

    //To gettext

    String theTextIWant = (String) js.executeScript("return arguments[0].value;",driver.findElement(By.xpath("//input[@id='display-name']")));

延伸阅读 - https://medium.com/@smeesheady/webdriver-javascriptexecutor-interact-with-elements-and-open-and-handle-multiple-tabs-and-get-url-dcfda49bfa0f

评论

0赞 ankostis 5/8/2020
我相信 OP 没有要求像您这样的 Selenium 解决方案,对吗?
0赞 MLEN 11/1/2020
@ankostis 它在标题中提到了“Selenium webdriver”。
0赞 ankostis 11/2/2020
@mlen原来的标题当时没有这样的 - 后来被填充了。
0赞 Andrewcpu 1/28/2023
@ankostis 这是一种有趣的方式,为错误的尖刻评论道歉。
0赞 ggorlen 5/26/2023
原来的帖子正文提到了 Selenium,所以它一直是问题的一部分。标题往往会错过重要的细节,这些细节最终会从正文中提升出来以改善 SEO。
36赞 undetected Selenium 7/27/2020 #10

要使用 xpath 和 标识 WebElement,您必须使用 evaluate() 方法,该方法计算 表达式并返回结果。


document.evaluate() 文档

document.evaluate() 返回基于 XPath 表达式和其他给定参数的 XPathResult

语法为:

var xpathResult = document.evaluate(
  xpathExpression,
  contextNode,
  namespaceResolver,
  resultType,
  result
);

哪里:

  • xpathExpression:表示要计算的 XPath 的字符串。
  • contextNode:指定查询的上下文节点。常见的做法是作为上下文节点传递。document
  • namespaceResolver:将传递任何命名空间前缀的函数,并应返回表示与该前缀关联的命名空间 URI 的字符串。它将用于解析 XPath 本身中的前缀,以便它们可以与文档匹配。 对于 HTML 文档或未使用命名空间前缀时很常见。null
  • resultType:一个整数,对应于使用命名常量属性(如 XPathResult 构造函数的 )返回的结果类型 XPathResult,该属性对应于 0 到 9 之间的整数。XPathResult.ANY_TYPE
  • result:用于结果的现有 XPathResult。 是最常见的,将创建新的 XPathResultnull

示范

例如,Google 主页中的搜索框可以使用 xpath 进行唯一标识,也可以使用 控制台通过以下命令进行标识://*[@name='q']

$x("//*[@name='q']")

快照:

googlesearchbox_xpath

也可以使用 和 xpath 表达式来标识相同的元素,如下所示:document.evaluate()

document.evaluate("//*[@name='q']", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;

快照:

document_evalute_xpath

15赞 ggorlen 7/2/2021 #11

尽管许多浏览器都内置了控制台,但这里汇总了在 JavaScript 中使用 XPath 简介中有用但硬编码的片段,这些片段可在脚本中使用:$x(xPath)

快照

这提供了 xpath 结果集的一次性快照。DOM 突变后数据可能已过时。

const $x = xp => {
  const snapshot = document.evaluate(
    xp, document, null, 
    XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null
  );
  return [...Array(snapshot.snapshotLength)]
    .map((_, i) => snapshot.snapshotItem(i))
  ;
};

console.log($x('//h2[contains(., "foo")]'));
<h2>foo</h2>
<h2>foobar</h2>
<h2>bar</h2>

一阶节点

const $xOne = xp => 
  document.evaluate(
    xp, document, null,
    XPathResult.FIRST_ORDERED_NODE_TYPE, null
  ).singleNodeValue
;

console.log($xOne('//h2[contains(., "foo")]'));
<h2>foo</h2>
<h2>foobar</h2>
<h2>bar</h2>

迭 代

但请注意,如果文档在迭代之间发生变异(修改文档树),这将使迭代无效,并且属性设置为 ,并且会引发异常。invalidIteratorStateXPathResulttrueNS_ERROR_DOM_INVALID_STATE_ERR

function *$xIter(xp) {
  const iter = document.evaluate(
    xp, document, null, 
    XPathResult.ORDERED_NODE_ITERATOR_TYPE, null
  );

  for (;;) {
    const node = iter.iterateNext();
    
    if (!node) {
      break;
    }
    
    yield node;
  }
}

// dump to array
console.log([...$xIter('//h2[contains(., "foo")]')]);

// return next item from generator
const xpGen = $xIter('//h2[text()="foo"]');
console.log(xpGen.next().value);
<h2>foo</h2>
<h2>foobar</h2>
<h2>bar</h2>