Есть отличная удаленная работа для php+codeception+jenkins+allure+docker спецов. 100% remote! Присоединиться к проекту

Выбрать нужный option из select по value

python
webdriver
Теги: #<Tag:0x00007f7b70a80398> #<Tag:0x00007f7b70a80230>

(Bolatbek) #1

В общем, есть у меня select, выглядит примерно так:

<select>
<option value="domain\00031477">Фамилия1 Имя1</option>
<option value="domain\00031478">Фамилия2 Имя2</option>
</select>

В связи с тем, что из-за проблем с кодировкой (это другой вопрос, меня не касается), видимый текст “Фамилия1 Имя1” бывает показывается так: “Ф?м?лия1 Им?1”.
Поэтому мне нужно выбрать по value.

Но как передать в value значение со обратным слэшэм “domain\00031477”?

        select.select_by_value(value)

Пробовал и двойным слэшем.
Дает ошибку.
Это значение “domain\00031477” - мне известно.


(Dmitrii Demin) #2

Двойной слеш?


(Bolatbek) #3

Пробовал.


(Евгений Салмин) #4

Как вариант - можно дебагом поймать момент появления нужного Select, посмотреть его значения и скопировать ровно то. что там есть, со спец. символами и т.д.


(Bolatbek) #5

Даже пробовал через Keys (то ли SEPARATOR, то ли SUBSTRACT), тоже не помогло.

С дебаггером надо попробовать.


(5am) #6

… или делать кастомный селект


(Сергей Блохин) #7

Можно проще.
Я Python плохо знаю (моя стихия — это Ruby), так что за корявость кода сильно не бейте.

Итак, у нас есть файл /index.html

<!-- I am /index.html -->
<select>
  <option value="domain\00031477">Alex Foo</option>
  <option value="domain\00031478">Kevin Bar</option>
</select>

Нам нужно выбрать второй элемент Kevin Bar по атрибуту value.

Создадим файл ‘/webdriver.py’

# I am /webdriver.py
from selenium import webdriver
driver = webdriver.Chrome()
driver.get('file:///index.html')
select = driver.find_element_by_css_selector('select')
options = select.find_elements_by_css_selector('option')
for option in options:
  if option.get_attribute('value') == u'domain\\00031478':
    option.click()
    break

Сначала мы находим искомый элемент select.
Потом в нём находим все элементы option.
Проходим в цикле все option, и если его атрибут value равен нужному нам, то кликаем по нему.

Объяснил сумбурно, но он работает! :slight_smile:


(Bolatbek) #8

Самое интересно, как выглядит мой stacktrace (внутри сделано как вы написали):

self = <selenium.webdriver.support.select.Select instance at 0x03120468>
value = 'domain\\00031478'
def select_by_value(self, value):
"""Select all options that have a value matching the argument. That is, when given "foo" this
would select an option like:

<option value="foo">Bar</option>

:Args:
- value - The value to match against

throws NoSuchElementException If there is no option with specisied value in SELECT
"""
css = "option[value =%s]" % self._escapeString(value)
opts = self._el.find_elements(By.CSS_SELECTOR, css)
matched = False
for opt in opts:
self._setSelected(opt)
if not self.is_multiple:
return
matched = True
if not matched:
>           raise NoSuchElementException("Cannot locate option with value: %s" % value)
E           NoSuchElementException: Message: Cannot locate option with value: domain\00031478

(Ant1dot) #9

На css действительно не работает даже с экранированием, я бы завел багу на webdriver.
Xpath работает
>>> d.find_element(‘xpath’, “//option[@value=’%s’]” % value)
<selenium.webdriver.remote.webelement.WebElement (session=“ef9a7d95a0e4187fd82c8d7e45af49da”, element=“0.9059672434567432-1”)>
>>> value
‘domain\00031477’


(Bolatbek) #10

Не поленился - оформил issue. Правда не уверен, там ли надо было).


(Сергей Блохин) #11

Я не думаю, что это дефект в Selenium WebDriver.
По крайней мере у меня заработало.

Исходный index.html:

<!-- index.html -->

<select>
  <option value="domain\00031477">Alex Foo</option>
  <option value="domain\00031478">Kevin Bar</option>
</select>

Исходный webdriver.py:

# webdriver.py

from selenium import webdriver
from selenium.webdriver.support.ui import Select

driver = webdriver.PhantomJS()
driver.get('file:///index.html')
element = driver.find_element_by_css_selector('select')
select = Select(element)
select.select_by_value(r'domain\\00031478'.strip())

(Bolatbek) #12

Таким образом - да, работает:

r'domain\\00031478'

(Ant1dot) #13
>>> r'domain\\00031477'                                                                 
'domain\\\\00031477'                                                                    
>>> [i.get_attribute('value') for i in d.find_elements('css selector', 'option')]       
['domain\\00031477', 'domain\\00031478']                                                

Но "r" это же дополнительное экранирование, мне кажется это все таки бага, возможно даже только python версии

(Сергей Блохин) #14

Если посмотреть в исходники модуля Select, то там можно увидеть дополнительно деэкранирование.

def select_by_value(self, value):
        """Select all options that have a value matching the argument. That is, when given "foo" this
           would select an option like:

           <option value="foo">Bar</option>

           :Args:
            - value - The value to match against
           """
        css = "option[value =%s]" % self._escapeString(value)
        opts = self._el.find_elements(By.CSS_SELECTOR, css)
        matched = False
        for opt in opts:
            self._setSelected(opt)
            if not self.is_multiple:
                return
            matched = True
        if not matched:
            raise NoSuchElementException("Cannot locate option with value: %s" % value)

(Ant1dot) #15

Это не деэкранирование, а экранирование кавычек

def _escapeString(self, value):
        if '"' in value and "'" in value:
            substrings = value.split("\"")
            result = ["concat("]
            for substring in substrings:
                result.append("\"%s\"" % substring)
                result.append(", '\"', ")
            result = result[0:-1]
            if value.endswith('"'):
                result.append(", '\"'")
            return "".join(result) + ")"