Selenium 的 XPath 问题
XPath trouble with Selenium
我想这是那些永恒的问题之一,但我需要一些有关 XPath 表达式的帮助。用 Selenium 搜索的 HTML 看起来像这样:
<div class="container">
<div class"row">
<div class="col-md-6 col-md-offset-3 jumbotron">
<div class="text-center">
<h1>Start a new To-Do list</h1>
<form method="POST" action="/lists/new">
<input name="item_text" id="id_new_item"
class="form-control input-lg"
placeholder="Enter a to-do item" />
<input type="hidden" name="csrfmiddlewaretoken" value="***********">
<div class="form-group has-error">
<span class="help-block">You can't have an empty list item</span>
</div>
</form>
</div>
</div>
</div>
</div>
Python 中的搜索表达式如下所示:
self.wait_for(lambda: self.assertEqual(
self.browser.find_element_by_xpath(
"//span[contains(text(), 'You can't have an empty list item')]"
)
)
)
这是测试中的 运行,它无法找到文本,即使它显然存在。测试的 ttaceback 是:
ERROR: test_cannot_add_empty_list_items (functional_tests.test_list_item_validation.ItemValidationTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/eric/Git/TDD/functional_tests/test_list_item_validation.py", line 15, in test_cannot_add_empty_list_items
self.wait_for(lambda: self.assertEqual(
File "/home/eric/Git/TDD/functional_tests/base.py", line 40, in wait_for
raise e
File "/home/eric/Git/TDD/functional_tests/base.py", line 37, in wait_for
return fn()
File "/home/eric/Git/TDD/functional_tests/test_list_item_validation.py", line 17, in <lambda>
"//span[contains(text(), 'You can't have an empty list item')]"
File "/home/eric/Git/TDD/venv/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 394, in find_element_by_xpath
return self.find_element(by=By.XPATH, value=xpath)
File "/home/eric/Git/TDD/venv/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 978, in find_element
'value': value})['value']
File "/home/eric/Git/TDD/venv/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
self.error_handler.check_response(response)
File "/home/eric/Git/TDD/venv/lib/python3.6/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: //span[contains(text(), 'You can't have an empty list item')]
----------------------------------------------------------------------
Ran 4 tests in 34.851s
FAILED (errors=1)
编辑:断言应该是 assertTrue 而不是 assertEqual,因为我没有将结果与任何东西进行比较。
您的 HTML 文档中没有 '
。有一个'
。
'
只是通知 HTML 解析器在文档树的这个位置插入一个单引号,它实际上并没有作为你可以搜索的东西结束。
你可以这样做:
self.wait_for(lambda: self.assertEqual(
self.browser.find_element_by_xpath(
'//span[contains(text(), "You can\'t have an empty list item")]'
)
)
)
但这只有在引号完全按照这种方式时才有效。当您的搜索文本包含双引号时,上述内容就会中断,您必须以另一种方式转义。只要搜索文本是预定义的,那是可行的。
只要生成的 XPath 有效,就可以开始了。在这种情况下,上面的结果是这个完全有效的 XPath 表达式:
//span[contains(text(), "You can't have an empty list item")]
但如果搜索文本是可变的(例如用户定义的),那么事情就会变得棘手。 Python 知道字符串转义序列,您始终可以使用 \"
或 \'
将引号放入字符串中。 XPath 不知道这样的事情。
假设搜索文本为 You can't have an "empty" list item
。这很容易用 Python 生成,但它不起作用:
//span[contains(text(), "You can't have an "empty" list item")]
-------------------------------------------^ breaks here
而且这个 XPath 也不起作用:
//span[contains(text(), 'You can't have an "empty" list item')]
--------------------------------^ breaks here
这个也不会,因为 XPath 没有转义序列:
//span[contains(text(), 'You can\'t have an "empty" list item')]
---------------------------------^ breaks here
要解决此问题,您可以在 XPath 中执行连接不同引号的字符串。这个:
//span[contains(text(), concat('You can', "'" ,'t have an "empty" list item'))]
完全有效,将搜索文本 You can't have an "empty" list item
。
而您在 Python 中可以做的就是创建这个结构:
- 在
'
处拆分搜索字符串
- 加入
', "'", '
的部分
- 前置
concat('
,附加')
- 插入 XPath 表达式
由于 XPath 格式错误,以下内容将允许永远不会抛出 运行 时间错误的字符串搜索:
search_text = 'You can\'t have an "empty" list item'
concat_expr = "', \"'\", '".join(search_text.split("'"))
concat_expr = "concat('" + concat_expr + "')"
xpath = "//span[contains(text(), %s)]" % concat_expr
xpath
,作为 Python 字符串文字(将其打印到控制台时会看到的内容):
'//span[contains(text(), concat(\'You can\', "\'", \'t have an "empty" list item\'))]'
XPath 引擎查看它的方式(即内存中的实际字符串):
//span[contains(text(), concat('You can', "'", 't have an "empty" list item'))]
lxml 库允许 XPath variables,这比那要优雅很多,但我怀疑 Selenium 的 find_elements_by_xpath
是否支持它们。
@Tomalak 的回答让我们对 xpath 的 text()
有了深刻的了解。但是,当您使用 find_element_by_xpath()
时,您可以轻松组合 class 属性,您可以使用以下 xpath 基于解决方案:
self.wait_for(lambda: self.assertEqual(
self.browser.find_element_by_xpath(
"//span[@class='help-block' and contains(., 'have an empty list item')]"
)
)
)
我想这是那些永恒的问题之一,但我需要一些有关 XPath 表达式的帮助。用 Selenium 搜索的 HTML 看起来像这样:
<div class="container">
<div class"row">
<div class="col-md-6 col-md-offset-3 jumbotron">
<div class="text-center">
<h1>Start a new To-Do list</h1>
<form method="POST" action="/lists/new">
<input name="item_text" id="id_new_item"
class="form-control input-lg"
placeholder="Enter a to-do item" />
<input type="hidden" name="csrfmiddlewaretoken" value="***********">
<div class="form-group has-error">
<span class="help-block">You can't have an empty list item</span>
</div>
</form>
</div>
</div>
</div>
</div>
Python 中的搜索表达式如下所示:
self.wait_for(lambda: self.assertEqual(
self.browser.find_element_by_xpath(
"//span[contains(text(), 'You can't have an empty list item')]"
)
)
)
这是测试中的 运行,它无法找到文本,即使它显然存在。测试的 ttaceback 是:
ERROR: test_cannot_add_empty_list_items (functional_tests.test_list_item_validation.ItemValidationTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/eric/Git/TDD/functional_tests/test_list_item_validation.py", line 15, in test_cannot_add_empty_list_items
self.wait_for(lambda: self.assertEqual(
File "/home/eric/Git/TDD/functional_tests/base.py", line 40, in wait_for
raise e
File "/home/eric/Git/TDD/functional_tests/base.py", line 37, in wait_for
return fn()
File "/home/eric/Git/TDD/functional_tests/test_list_item_validation.py", line 17, in <lambda>
"//span[contains(text(), 'You can't have an empty list item')]"
File "/home/eric/Git/TDD/venv/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 394, in find_element_by_xpath
return self.find_element(by=By.XPATH, value=xpath)
File "/home/eric/Git/TDD/venv/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 978, in find_element
'value': value})['value']
File "/home/eric/Git/TDD/venv/lib/python3.6/site-packages/selenium/webdriver/remote/webdriver.py", line 321, in execute
self.error_handler.check_response(response)
File "/home/eric/Git/TDD/venv/lib/python3.6/site-packages/selenium/webdriver/remote/errorhandler.py", line 242, in check_response
raise exception_class(message, screen, stacktrace)
selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: //span[contains(text(), 'You can't have an empty list item')]
----------------------------------------------------------------------
Ran 4 tests in 34.851s
FAILED (errors=1)
编辑:断言应该是 assertTrue 而不是 assertEqual,因为我没有将结果与任何东西进行比较。
您的 HTML 文档中没有 '
。有一个'
。
'
只是通知 HTML 解析器在文档树的这个位置插入一个单引号,它实际上并没有作为你可以搜索的东西结束。
你可以这样做:
self.wait_for(lambda: self.assertEqual(
self.browser.find_element_by_xpath(
'//span[contains(text(), "You can\'t have an empty list item")]'
)
)
)
但这只有在引号完全按照这种方式时才有效。当您的搜索文本包含双引号时,上述内容就会中断,您必须以另一种方式转义。只要搜索文本是预定义的,那是可行的。
只要生成的 XPath 有效,就可以开始了。在这种情况下,上面的结果是这个完全有效的 XPath 表达式:
//span[contains(text(), "You can't have an empty list item")]
但如果搜索文本是可变的(例如用户定义的),那么事情就会变得棘手。 Python 知道字符串转义序列,您始终可以使用 \"
或 \'
将引号放入字符串中。 XPath 不知道这样的事情。
假设搜索文本为 You can't have an "empty" list item
。这很容易用 Python 生成,但它不起作用:
//span[contains(text(), "You can't have an "empty" list item")]
-------------------------------------------^ breaks here
而且这个 XPath 也不起作用:
//span[contains(text(), 'You can't have an "empty" list item')]
--------------------------------^ breaks here
这个也不会,因为 XPath 没有转义序列:
//span[contains(text(), 'You can\'t have an "empty" list item')]
---------------------------------^ breaks here
要解决此问题,您可以在 XPath 中执行连接不同引号的字符串。这个:
//span[contains(text(), concat('You can', "'" ,'t have an "empty" list item'))]
完全有效,将搜索文本 You can't have an "empty" list item
。
而您在 Python 中可以做的就是创建这个结构:
- 在
'
处拆分搜索字符串
- 加入
', "'", '
的部分
- 前置
concat('
,附加')
- 插入 XPath 表达式
由于 XPath 格式错误,以下内容将允许永远不会抛出 运行 时间错误的字符串搜索:
search_text = 'You can\'t have an "empty" list item'
concat_expr = "', \"'\", '".join(search_text.split("'"))
concat_expr = "concat('" + concat_expr + "')"
xpath = "//span[contains(text(), %s)]" % concat_expr
xpath
,作为 Python 字符串文字(将其打印到控制台时会看到的内容):
'//span[contains(text(), concat(\'You can\', "\'", \'t have an "empty" list item\'))]'
XPath 引擎查看它的方式(即内存中的实际字符串):
//span[contains(text(), concat('You can', "'", 't have an "empty" list item'))]
lxml 库允许 XPath variables,这比那要优雅很多,但我怀疑 Selenium 的 find_elements_by_xpath
是否支持它们。
@Tomalak 的回答让我们对 xpath 的 text()
有了深刻的了解。但是,当您使用 find_element_by_xpath()
时,您可以轻松组合 class 属性,您可以使用以下 xpath 基于解决方案:
self.wait_for(lambda: self.assertEqual(
self.browser.find_element_by_xpath(
"//span[@class='help-block' and contains(., 'have an empty list item')]"
)
)
)