XPath проблема с селеном - PullRequest
       15

XPath проблема с селеном

0 голосов
/ 31 января 2019

Полагаю, это один из тех вечных вопросов, но мне нужна помощь с XPath-выражением.Поиск HTML с помощью Selenium выглядит следующим образом:

<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&#39;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&#39;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&#39;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&#39;t have an empty list item')]


----------------------------------------------------------------------
Ran 4 tests in 34.851s

FAILED (errors=1)

РЕДАКТИРОВАТЬ: Утверждение должно быть assertTrue вместо assertEqual, так как я не сравниваю результат ни с чем.

Ответы [ 2 ]

0 голосов
/ 31 января 2019

@ Ответ Томалака дает нам отличное понимание text() из xpath .Тем не менее, поскольку вы используете 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')]"
    )
  )
)
0 голосов
/ 31 января 2019

В вашем HTML-документе нет &#39;.Существует '.

&#39; только информирует анализатор 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 знает escape-последовательности строк, вы всегда можете использовать \" или \', чтобы получить кавычку в строку.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не имеет escape-последовательностей:

//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, так это создать такую ​​структуру:

  1. разбить строку поиска на '
  2. соединить детали с помощью ', "'", '
  3. prepend concat(', добавить ')
  4. вставить в выражение 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 , что намного элегантнее, но я сомневаюсь, что find_elements_by_xpath в Selenium их поддерживает.

...