Откройте все кнопки "плюс" на странице, используя 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)
Другие вопросы по тегам