首页 > 解决方案 > 如何在 selenium 中的 python 中使用相同的 xpath 访问第二个元素

问题描述

我的意思是我正在使用的网站有 2 个名为省的下拉菜单,其 ID 完全相同,所以我如何告诉 python 我想选择哪个下拉菜单。当然,这是假设问题是 python 总是选择它看到的第一个 id

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

   # There are two dropmenu with the same xpath. first time it works fine 
   # 2nd time it throws an error about element not interactable 

   Prov = Select(web.find_element_by_xpath('//*[@id="province"]'))
   Prov.select_by_index(2)

def Start():
    # once opened it will fill in the confirm your age
    Day = Select(web.find_element_by_xpath('//*[@id="bday_day"]'))
    Day.select_by_index(2)
    Month = Select(web.find_element_by_xpath('//*[@id="bday_month"]'))
    Month.select_by_index(4)
    Month = Select(web.find_element_by_xpath('//*[@id="bday_year"]'))
    Month.select_by_index(24)
    Prov = Select(web.find_element_by_xpath('//*[@id="province"]'))
    Prov.select_by_index(5)
    Button = web.find_element_by_xpath('//*[@id="popup-subscribe"]/button')
    Button.click()

# have to go through select your birthday
Start()
# 2 seconds is enough for the website to load
time.sleep(2)
# this throws and error. 
Prov = Select(web.find_element_by_xpath('//*[@id="province"]'))
Prov.select_by_index(5)

标签: pythonselenium

解决方案


硒具有功能

  • find_element_by_...没有s文字element只得到第一个元素
  • find_elements_by_...sin wordelements获取所有元素

Selenium 文档:4. 定位元素¶

因此,您可以将所有元素作为列表获取(即使 HTML 中只有一个元素)
(如果没有元素,那么您将获得空列表)

elements = web.find_elements_by_xpath('//*[@id="province"]')

然后切片

first  = elements[0] 
second = elements[1] 

last   = elements[-1]

list_first_and_second = elements[:1] 

编辑:

您也可以尝试直接在 xpath 中切片
(它从一开始计数,而不是零)

'//*[@id="province"][2]'

或者可能

'(//*[@id="province"])[2]'

但我从来没有用它来确认它是否会起作用。


顺便提一句:

所有人ID都应该有唯一的名字——你不应该重复 ID。

如果您检查文档4. Locating Elements¶那么您会看到word中find_element_by_id没有 char - 获取第一个也是唯一具有某些 ID 的元素 - 但word中没有with char - 获取多个具有某些 ID 的元素。selementfind_elements_by_idselements


编辑:

代码中带有示例 HTML 的最小工作代码

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

html = '''
<select id="province">
<option value="value_A">A</options>
<option value="value_B">B</options>
</select>
<select id="province">
<option value="value_1">1</options>
<option value="value_2">2</options>
</select>
'''

driver = webdriver.Firefox()
driver.get("data:text/html;charset=utf-8," + html)

all_elements = driver.find_elements_by_xpath('//*[@id="province"]')

first  = all_elements[0] 
second = all_elements[1] 

prov1 = Select(first)
prov2 = Select(second)

print('--- first ---')
for item in prov1.options:
    print('option:', item.text, item.get_attribute('value'))
for item in prov1.all_selected_options:
    print('selected:', item.text, item.get_attribute('value'))
    
print('--- second ---')
for item in prov2.options:
    print('option:', item.text, item.get_attribute('value'))
for item in prov2.all_selected_options:
    print('selected:', item.text, item.get_attribute('value'))

编辑:

有两个province

当您使用find_elementin时,Start您会首先province出现在弹出窗口中 - 您可以填写它。当您单击按钮时,它会关闭此弹出窗口,但不会首先province从 HTML 中删除 - 它只会隐藏它。

稍后,当您使用时,find_element您会再次province在隐藏的弹出窗口中再次出现 - 这次它不可见并且无法使用它 - 这会产生错误。你必须province像这个例子中那样使用第二个。

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

def Start():
    
    # once opened it will fill in the confirm your age
    Day = Select(web.find_element_by_xpath('//*[@id="bday_day"]'))
    Day.select_by_index(2)
    Month = Select(web.find_element_by_xpath('//*[@id="bday_month"]'))
    Month.select_by_index(4)
    Month = Select(web.find_element_by_xpath('//*[@id="bday_year"]'))
    Month.select_by_index(24)

    # it uses first `province`
    Prov = Select(web.find_element_by_xpath('//*[@id="province"]'))
    Prov.select_by_index(5)
    Button = web.find_element_by_xpath('//*[@id="popup-subscribe"]/button')
    Button.click()

web = webdriver.Firefox()
web.get('https://www.tastyrewards.com/en-ca/contest/fritolaycontest/participate')

# have to go through select your birthday
Start()

# 2 seconds is enough for the website to load
time.sleep(2)

# `find_elements` with `s` - to get second `province`
all_province = web.find_elements_by_xpath('//*[@id="province"]')
second_province = all_province[1]

Prov = Select(second_province)
Prov.select_by_index(5)

推荐阅读