【问题标题】:Selenium - Text Property only becomes available after debugger inspectionSelenium - 文本属性仅在调试器检查后才可用
【发布时间】:2019-11-04 05:53:11
【问题描述】:

我正在与基于 Selenium 的 Python 3 网络抓取工具中的意外行为作斗争,并想了解发生了什么:

我正在解析提供工作机会的网站。在最初的搜索之后,我得到了 1 到 n 个结果站点。这个数量的站点在第一页显示为“m-pagination__meta”元素的文本部分,并以德语显示,例如“1 冯 48”。我需要这个字符串进行进一步处理。它在网站上,不是 iframe 的一部分。

Sample link of job website

HTML:

<div class="m-pagination">
  <div class="m-pagination__inner m-pagination__inner--borderBottom">
    <button class="m-pagination__button m-pagination__button--disabled" data-page="" data-event-action="click: pagination-first">
      <svg viewBox="0 0 17 17" width="0" height="0" class="m-icon m-icon--large ">
        <g fill="none" stroke="currentColor" stroke-width=".7" stroke-linecap="round" stroke-linejoin="round">
          <path d="M9 13.2L4.2 8.5 9 3.8"></path>
          <path d="M12.8 13.2L8 8.5l4.7-4.7"></path>
        </g>
      </svg>
    </button>
    <button class="m-pagination__button m-pagination__button--previous m-pagination__button--disabled" data-page="false" data-event-action="click: pagination-previous">
      <svg viewBox="0 0 17 17" width="0" height="0" class="m-icon m-icon--large ">
        <path fill="none" stroke="currentColor" stroke-width=".8" stroke-linecap="round" stroke-linejoin="round" d="M10.9 3.8L6 8.6l4.7 4.6"></path>
      </svg>
    </button>
    <span class="m-pagination__meta" data-number="1"> 1 von 43 </span> 
    <button class="m-pagination__button m-pagination__button--next m-pagination__button--available" data-page="2" data-event-action="click: pagination-next">
      <svg viewBox="0 0 17 17" width="0" height="0" class="m-icon m-icon--large ">
        <path fill="none" stroke="currentColor" stroke-width=".7" stroke-linecap="round" stroke-linejoin="round" d="M6.1 3.8L11 8.6l-4.7 4.6"></path>
      </svg>
    </button>
  </div>
</div>

现在是奇怪的部分:当我调试程序并尝试使用“m-pagination__meta”.text 直接访问字符串元素时,它返回一个空字符串。

然而,当我访问母元素对象 m-pagination__meta 并使用调试器检查它时,向下滚动到文本属性时,预期的“1 von 48”字符串就在那里。经过这次检查,我可以访问“m-pagination__meta”.text 并获得预期的结果。

这种行为似乎不依赖于时间。我试图用类似的代码等待所需元素的存在

wait = WebDriverWait(self.driver, 10)
wait.until(EC.text_to_be_present_in_element((By.CLASS_NAME,"m-pagination__meta"), "1 von 48"))
pagesTotal = int(self.driver.find_element_by_class_name("m-pagination__meta").text.split(" ")[2])

无济于事(当然,当我不知道它会产生哪个特定字符串时,我意识到搜索特定字符串是愚蠢的,但我不知道如何编写它。)

我也尝试了使用睡眠的“正常”等待,但似乎没有任何效果,只有在调试器中提到的检查,这对于生产目的是无用的。

我真的很想了解这里发生了什么。

【问题讨论】:

  • 您能否发布包含您要查找的文本的 HTML 的 sn-p?
  • 您知道吗,看起来页面在您向下滚动时加载。这就是为什么在您运行调试器之前 Selenium 没有找到任何东西的原因。在调试器中,向下滚动到元素,这会导致页面的其余部分加载,然后 Selenium 可以找到该元素。
  • 不,元素在那里没有滚动。我刚刚检查了...

标签: python-3.x selenium xpath css-selectors webdriverwait


【解决方案1】:

存在vertical scroll bar,它将页面分为两个部分。但是,您需要先找到左侧滚动条元素,然后再执行location_once_scrolled_into_view。一旦到达,您就可以识别您所追求的元素。

试试下面的代码。

from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

driver=webdriver.Chrome()
driver.get('https://www.karriere.at/jobs/programmierer/wien')
WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, "//div[@class='c-jobsSearch__listing']"))).location_once_scrolled_into_view
print(driver.find_element_by_xpath("//span[@class='m-pagination__meta']").get_attribute('innerText'))

【讨论】:

  • 非常感谢!该代码基本上立即生效。
【解决方案2】:

问题可能是页面加载时出现在 HTML 源代码中的元素,但 JavaScript 在后台填充了值。

您可以在 XPath 中使用正则表达式来匹配基于模式的文本:

xpath = '//*[contains(@class, "m-pagination__meta") and matches(normalize-space(text()), "\d+ von \d+")]'
wait = WebDriverWait(self.driver, 30)
wait.until(EC.element_to_be_present(By.XPATH, xpath))

注意:为了安全起见,也可以将等待时间增加到 30 秒。

【讨论】:

    【解决方案3】:

    您似乎与 WebDriverWait 非常接近。但不幸的是,该元素位于DOM Tree 的下方,并且不在Viewport 中。因此返回空字符串


    解决方案

    一旦元素在HTML DOM可见,使用EC 作为scroll() visibility_of_element_located(),然后您可以尝试提取所需的文本,您可以使用以下任一Locator Strategies

    • 使用CSS_SELECTOR

      driver.execute_script("return arguments[0].scrollIntoView(true);", WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.CSS_SELECTOR, "span.m-pagination__meta"))))
      print(driver.find_element_by_css_selector("span.m-pagination__meta").get_attribute("innerHTML"))
      
    • 使用XPATH

      driver.execute_script("return arguments[0].scrollIntoView(true);", WebDriverWait(driver, 20).until(EC.visibility_of_element_located((By.XPATH, "//span[@class='m-pagination__meta']"))))
      print(driver.find_element_by_xpath("//span[@class='m-pagination__meta']").get_attribute("innerHTML"))
      
    • 注意:您必须添加以下导入:

      from selenium.webdriver.support.ui import WebDriverWait
      from selenium.webdriver.common.by import By
      from selenium.webdriver.support import expected_conditions as EC
      

    【讨论】:

    • 非常感谢 sn-ps 和解释!我将下面的答案标记为正确,只是因为它是唯一一个开箱即用的代码。很难决定,可惜只有一个答案可以标记为正确。
    猜你喜欢
    • 2020-07-17
    • 2022-01-19
    • 1970-01-01
    • 1970-01-01
    • 2020-10-20
    • 1970-01-01
    • 2016-06-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多