Откройте все кнопки "плюс" на странице, используя Selenium и Python
Пожалуйста, кто-нибудь, помогите мне. Я работаю над этим уже больше недели, и мне не повезло! Я хочу нажать и открыть несколько кнопок "плюс" и распечатать их содержимое, если в нем есть слово "сердечно-сосудистая система", как на рисунке ниже.
Вот код, который у меня есть:
from selenium import webdriver
from selenium import webdriver
chrome_path=r"G:\My Drive\chrome_driver\chromedriver_win32\chromedriver.exe"
driver=webdriver.Chrome(chrome_path)
driver.get('https://meshb.nlm.nih.gov/treeView')
sidebar = driver.find_element_by_xpath("/html/body/div[2]/div")
i=1
for i in range(16): # since I have 16 div(s)
sidebar.find_elements_by_xpath("/html/body/div[2]/div/div[i]")
element = driver.find_element_by_xpath("/html/body/div[2]/div").find_element_by_xpath("/html/body/div[2]/div/div[i]").find_element_by_xpath("//*[@class='ng-scope']/span")
element.click()
Но я продолжаю получать эту ошибку:
no such element: Unable to locate element: {"method":"xpath","selector":"/html/body/div[2]/div/div[i]"}
Я также поместил 2 снимка экрана html-страницы. Один показывает все div(s), другой показывает один из расширенных div (s). Любая помощь приветствуется!
1 ответ
Пояснения к коду:
i = 0
while True:
# locate all elements
elements = driver.find_elements_by_xpath("//span[@ng-if = 'node.HasChildren']/i[@ng-click='getTreeChildren(node)']")
if len(elements) > i:
elements[i].click() # click on the i-th element in the list
i += 1 # increment i
time.sleep(0.5) # wait until list will be updated
continue
break
вы создаете бесконечный цикл, каждый раз определяя местонахождение всех элементов для расширения. Возьмите i-й элемент и нажмите, чтобы развернуть. Дождитесь загрузки выпадающего списка (вы можете установить другое значение в wait
). Тогда вы выполните continue
заявление для начала loop
с начала. И это будет выполняться до list size
расположенных элементов больше, чем i
, Тогда вы достигнете break
заявление к break
loop
, После этого вы можете удалить данные.
Теперь у вас есть все данные, видимые на странице, и вы можете найти все необходимые элементы. Я предполагаю, что вы хотите пролеты (как <span ng-if="!node.strong" class="ng-binding ng-scope">Blood-Air Barrier [A07.025]</span>
) текст:
# spans is the list of all descendants (children, grandchildren, etc.) of the current node and the current node itself
spans = driver.find_elements_by_xpath("//span[contains(., 'Cardiovascular')]/parent::*/parent::*/descendant-or-self::node()/a/span")
если вам не нужен узел self в списке, вы можете сделать это так:
# spans is the list of all descendants (children, grandchildren, etc.) of the current node without current node itself
spans = driver.find_elements_by_xpath("//span[contains(., 'Cardiovascular')]/parent::*/parent::*/descendant::node()/a/span")
И, наконец, вы можете напечатать, например, текст всех элементов:
for span in spans
print span.text
Шаблон:
from selenium import webdriver
import time
chrome_path = r"C:\Users\Andrei\Desktop\driver\chromedriver.exe"
driver = webdriver.Chrome(chrome_path)
driver.get('https://meshb.nlm.nih.gov/treeView')
driver.implicitly_wait(5) # wait until page will be loaded
i = 0
while True:
# locate all elements
elements = driver.find_elements_by_xpath("//span[@ng-if = 'node.HasChildren']/i[@ng-click='getTreeChildren(node)']")
if len(elements) > i:
elements[i].click() # click on the i-th element in the list
i += 1 # increment i
time.sleep(0.5) # wait until list will be updated
continue
break
spans = driver.find_elements_by_xpath("//span[contains(., 'Cardiovascular')]/parent::*/parent::*/descendant-or-self::node()/a/span")
for span in spans:
print(span.text)
Выход:
Blood-Air Barrier [A07.025]
Blood-Aqueous Barrier [A07.030]
Blood-Brain Barrier [A07.035]
...
РЕДАКТИРОВАТЬ:
для быстрой проверки вы можете использовать этот шаблон (он расширит только несколько узлов, только для тестирования):
from selenium import webdriver
import time
chrome_path = r"C:\Users\Andrei\Desktop\driver\chromedriver.exe"
driver = webdriver.Chrome(chrome_path)
driver.get('https://meshb.nlm.nih.gov/treeView')
driver.implicitly_wait(5) # wait until page will be loaded
i = 0
while i < 9:
# locate all elements
elements = driver.find_elements_by_xpath("//span[@ng-if = 'node.HasChildren']/i[@ng-click='getTreeChildren(node)']")
if len(elements) > i:
if i == 0:
elements[i].click()
i += 6
elements[i].click() # click on the i-th element in the list
i += 1 # increment i
time.sleep(0.5) # wait until list will be updated
continue
break
spans = driver.find_elements_by_xpath("//span[contains(., 'Cardiovascular')]/parent::*/parent::*/descendant-or-self::node()/a/span")
for span in spans:
print(span.text)
Выход:
Cardiovascular System [A07]
Blood-Air Barrier [A07.025]
Blood-Aqueous Barrier [A07.030]
Blood-Brain Barrier [A07.035]
Blood-Nerve Barrier [A07.037]
Blood-Retinal Barrier [A07.040]
Blood-Testis Barrier [A07.045]
Blood Vessels [A07.231]
Adventitia [A07.231.057]
Arteries [A07.231.114]
Microvessels [A07.231.461]
Retinal Vessels [A07.231.611]
Tunica Intima [A07.231.700]
Tunica Media [A07.231.733]
Vasa Nervorum [A07.231.765]
Vasa Vasorum [A07.231.836]
Veins [A07.231.908]
Glomerular Filtration Barrier [A07.500]
Heart [A07.541]
Больше информации о xPath
оси здесь
ДОБАВЛЯТЬ:
Поскольку список узлов на сайте очень большой (я не знаю этого), я добавил облегченную версию кода выше. Здесь почти такая же логика, как уже была. Разница в следующем: сначала будут расширены 16 основных узлов, затем будет расположен узел, который мы ищем, а затем будут расширены все только его дочерние узлы. Намного быстрее получить результат, но если поисковый узел не находится на "первом" уровне, тогда ничего не будет найдено. Можно пойти "глубже" и искать на втором, третьем и т. Д. Уровнях, но это будет сложно реализовать. По крайней мере, логика, как бороться с этой проблемой, я думаю, ясна. PS приведенный выше код также работает, но он замедляется, когда представлено много узлов, поэтому это занимает больше времени time.sleep()
,
Примечание: вы должны указать полное слово, чтобы убедиться, что вы получите только один узел по строке поиска. Например для Cardiovascular
Есть два узла: Cardiovascular System [A07]
а также Cardiovascular Diseases [C14]
, И программа не будет расширять все узлы для Cardiovascular Diseases [C14]
, Если вы хотите расширить второй узел, вам нужно немного изменить приведенный ниже код. За Cardiovascular System
будет только один узел.
from selenium import webdriver
import time
chrome_path = r"C:\Users\Andrei\Desktop\driver\chromedriver.exe"
driver = webdriver.Chrome(chrome_path)
driver.get('https://meshb.nlm.nih.gov/treeView')
driver.implicitly_wait(1) # wait until page will be loaded
search_word = "Cardiovascular System"
elements_xpath = "//span[@ng-if = 'node.HasChildren']/i[@ng-click='getTreeChildren(node)']"
spans_xpath = "//span[contains(., '" + search_word + "')]/parent::*/parent::*/descendant-or-self::node()/a/span"
link_xpath = "//span[@class = 'ng-binding ng-scope']"
# expand all nodes first level
elements = driver.find_elements_by_xpath(elements_xpath)
for element in elements:
element.click()
time.sleep(0.3)
# search for span position
elements = driver.find_elements_by_xpath(link_xpath)
i = 0
for element in elements:
if search_word in element.text:
break
i += 1
# i is the position where the 'Cardiovascular' was found
# now is time to expand all child nodes at i position
end = i + 1
elements = driver.find_elements_by_xpath("//span[@class = 'ng-scope']")
old_length = len(elements)
while i < end:
elements[i].click()
i += 1
time.sleep(0.3)
elements = driver.find_elements_by_xpath("//span[@class = 'ng-scope']")
end = end + len(elements) - old_length
old_length = len(elements)
# all child nodes are expanded
# time to collect information
spans = driver.find_elements_by_xpath(spans_xpath)
for span in spans:
print(span.text)