为什么某些元素的 XPath 有时会发生变化?

Why does the XPath of some elements change sometimes?

我正在使用 Python 和 Selenium 为 Instagram 开发一些自动化操作,有时我的代码会因为 NoSuchElementException 而崩溃。 例如,当我第一次写一个取消关注用户的函数时,我使用了类似这样的东西:

following_xpath = "//*[@id='react-root']/section/main/div/header/section/div[1]/div[2]/div/span/span[1]/button"

几次 运行 之后,我的代码崩溃了,因为它找不到元素,所以在检查页面时我发现现在的 XPath 是:

following_xpath = "//*[@id="react-root"]/section/main/div/header/section/div[2]/div/div/div[2]/div/span/span[1]/button"

div[1]/div[2]/divdiv[2]/div/div/div[2] 有细微差别。所以我有两个问题:

  1. 为什么会这样?
  2. 是否有一种万无一失的方法可以保证我始终获得正确的 XPath(或元素)?

(1)的答案很简单:页面内容已更改。

首先,文档中的每个元素都有一个“XPath”的概念是错误的:有许多(无限多)XPath 表达式将 select 一个给定的元素。您可能已经使用一种工具生成了这些 XPath,该工具试图为您提供它认为最有用的 XPath 表达式,但它不是唯一可能的工具。

最好使用的 XPath 表达式是在页面内容更改时不会更改的表达式:但是任何工具都很难为您提供该表达式,因为它不知道页面内容可能会发生什么变化页面内容。

使用 @id 属性值(这些路径这样做)比使用数字索引(这些路径也这样做)更可能稳定,但这是基于对可能发生变化的猜测,而这些猜测可以总是错的。编写在页面更改时继续执行“正确操作”的 XPath 表达式的唯一方法是正确猜测页面结构的哪些方面将发生变化以及哪些部分将保持稳定。所以唯一的“防弹”答案 (2) 不仅要了解当前的页面结构,还要了解它随时间的不变量。

是时候打破 XPath 改变的神话了。

e.g. and 由用户派生,构建的定位器越规范,它们就越耐用。

XML Path Language (XPath)

XPath 3.1 is an expression language that allows the processing of values conforming to the data model defined in XQuery and XPath Data Model (XDM) 3.1. The name of the language derives from its most distinctive feature, the path expression, which provides a means of hierarchic addressing of the nodes in an XML tree. As well as modeling the tree structure of XML, the data model also includes atomic values, function items, and sequences. This version of XPath supports JSON as well as XML, adding maps and arrays to the data model and supporting them with new expressions in the language and new functions in XQuery and XPath Functions and Operators 3.1.

Selectors

CSS (Cascading Style Sheets) is a language for describing the rendering of HTML and XML documents on screen, on paper, in speech, etc. CSS uses Selectors for binding style properties to elements in the document. These expressions can also be used, for instance, to select a set of elements, or a single element from a set of elements, by evaluating the expression across all the elements in a subtree.


这个用例

根据您的代码试验:

following_xpath = "//*[@id='react-root']/section/main/div/header/section/div[1]/div[2]/div/span/span[1]/button"

following_xpath = "//*[@id="react-root"]/section/main/div/header/section/div[2]/div/div/div[2]/div/span/span[1]/button"

这里有一些要点:

  • DOM Tree contains elements. So it is quite clear that the app uses ReactJS。 React 是一个用于构建用户界面的声明式、高效且灵活的 JavaScript 库。它使您可以从称为 components.
  • 的小而孤立的代码片段组成复杂的 UI
  • xpath 是绝对 xpath。
  • xpath 包含索引。

因此,应用程序本质上是动态的,并且可以在 HTML DOM on firing of any DOM events 中添加和移动元素。


解决方案

在这种情况下,当应用程序基于以下之一时:

规范的方法是构造 relative and/or dynamic 定位器诱导 。一些例子:

  • 要与 登录页面上的用户名字段交互:

    WebDriverWait(browser, 20).until(EC.element_to_be_clickable((By.CSS_SELECTOR, "input[name='username']"))).send_keys("anon")
    

You can find a detailed discussion in Filling in login forms in Instagram using selenium and webdriver (chrome) python OSX

  • :

    上找到地址第一行 FIND US
    WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, "//span[normalize-space()='FIND US']//following::span[2]")))
    

You can find a detailed discussion in

  • GWT 元素交互:

    WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//div[@title='Viewers']//preceding::span[1]//label"))).click()
    

You can find a detailed discussion in