问题描述
我正在使用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]/div
与div[2]/div/div/div[2]
之间的差别很小。所以我有两个问题:
- 为什么会这样?
- 是否有防弹方法来确保我将始终获得正确的XPath(或元素)?
解决方法
(1)的答案很简单:页面内容已更改。
首先,文档中每个元素都有一个“ XPath”的概念是错误的:有许多(无限数量)XPath表达式将选择给定元素。您可能已经使用一种工具尝试生成了这些XPath,该工具试图为您提供它认为最有用的XPath表达式,但这并不是唯一的一种。
最好使用的XPath表达式是在页面内容更改时不会改变的表达式:但是对于任何工具来说,都很难做到这一点,因为它不知道页面中可能更改的内容内容。
使用@id属性值(这些路径可以做到)比使用数字索引(这些路径也可以做到)更稳定,但这是基于对可能发生变化的猜测,并且这些猜测总是可以错误。编写XPath表达式以在页面更改时继续做“正确的事”的唯一方法是正确猜测页面结构的哪些方面将发生变化,哪些部分将保持稳定。因此,唯一的“防弹”答案(2)不仅要了解当前的页面结构,还要了解其随时间的不变性。
,现在该是我们打破 XPath 改变的神话了。
Locator Strategies例如xpath和css-selectors由用户派生,定位器的构造越规范,它们就越耐用。
XML Path Language (XPath)
XPath 3.1是一种表达式语言,它允许处理符合XQuery and XPath Data Model (XDM) 3.1中定义的数据模型的值。语言的名称源自其最独特的功能,即路径表达式,该路径表达式提供了对XML树中的节点进行分层寻址的方法。除了为XML的树结构建模之外,数据模型还包括原子值,功能项和序列。此版本的XPath支持JSON和XML,将映射和数组添加到数据模型中,并通过语言的新表达式和XQuery and XPath Functions and Operators 3.1中的新函数来支持它们。
Selectors
CSS(层叠样式表)是一种用于描述在屏幕,纸张,语音等上呈现HTML和XML文档的语言。CSS使用选择器将样式属性绑定到文档中的元素。通过评估子树中所有元素的表达式,这些表达式还可以用于例如选择一组元素或从一组元素中选择单个元素。
此用例
根据您的代码试用:
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包含React个元素。因此很明显,该应用程序使用ReactJS。 React是用于构建用户界面的声明性,高效且灵活的JavaScript库。它使您可以通过小的独立代码段(称为 components )组成复杂的UI。
- xpath是绝对xpath。
- xpath包含索引。
因此,该应用程序本质上是动态的,并且在触发任何HTML DOM时很容易在DOM events内添加和移动元素。
解决方案
在这种情况下,应用程序基于以下任意一种情况:
规范的方法是构造相对和/或动态定位器,以诱导WebDriverWait。一些例子:
-
与instagram登录页面上的用户名字段进行交互:
WebDriverWait(browser,20).until(EC.element_to_be_clickable((By.CSS_SELECTOR,"input[name='username']"))).send_keys("anon")
您可以在Filling in login forms in Instagram using selenium and webdriver (chrome) python OSX
中找到详细的讨论
-
要在facebook上通过查找我们在文本下方找到地址的第一行:
WebDriverWait(driver,20).until(EC.visibility_of_element_located((By.XPATH,"//span[normalize-space()='FIND US']//following::span[2]")))
中找到详细的讨论
-
与 GWT 元素相交:
WebDriverWait(driver,20).until(EC.element_to_be_clickable((By.XPATH,"//div[@title='Viewers']//preceding::span[1]//label"))).click()
您可以在How to click on GWT enabled elements using Selenium and Python
中找到详细的讨论