使用 selenium (python) 获取具有部分字符串匹配的元素文本
Get element text with a partial string match using selenium (python)
我正在尝试从深深嵌套在该网页 html 中的 <strong>
标签中提取文本:https://www.marinetraffic.com/en/ais/details/ships/imo:9854612
例如:
强标签是网页上唯一包含字符串 'cubic meters' 的标签
我的 objective 是提取整个文本,即“138124 立方米液化气”。当我尝试以下操作时,出现错误:
url = "https://www.marinetraffic.com/en/ais/details/ships/imo:9854612"
driver.get(url)
time.sleep(3)
element = driver.find_element_by_link_text("//strong[contains(text(),'cubic meters')]").text
print(element)
NoSuchElementException: Message: no such element: Unable to locate element: {"method":"link text","selector":"//strong[contains(text(),'cubic meters')]"}
我在这里做错了什么?任何建议,将不胜感激! 编辑:下面也报错:
element = driver.find_element_by_xpath("//strong[contains(text(),'cubic')]").text
我猜你应该先滚动到那个元素,然后才尝试访问它,包括获取它的文本。
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
element = driver.find_element_by_xpath("//div[contains(@class,'MuiTypography-body1')][last()]//div")
actions.move_to_element(element).build().perform()
text = element.text
如果上述方法还不够好,您可以像这样滚动一次页面高度:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(0.5)
the_text = driver.find_element_by_xpath("//div[contains(@class,'MuiTypography-body1')][last()]//strong").text
您可以为此使用 BeautifulSoup,更准确地说是 the string
argument;来自文档,“您可以搜索字符串而不是标签”。
作为参数,您还可以传递一个正则表达式模式。
>>> from bs4 import BeautifulSoup
>>> import re
>>> soup = BeautifulSoup(driver.page_source, "html.parser")
>>> soup.find_all(string=re.compile(r"\d+ cubic meters"))
['173400 cubic meters Liquid Gas']
如果您确定只有一个结果,或者您只需要第一个,您也可以使用 find
而不是 find_all
。
您的代码适用于 Firefox()
但不适用于 Chrome()
页面使用 lazy loading
,因此您必须滚动到 Summary
,然后它会加载预期的 strong
.
文本
我使用了稍微慢一点的方法 - 我搜索了所有元素
class='lazyload-wrapper
,然后循环滚动到项目并检查是否有 strong
。如果没有 strong
然后我滚动到下一个 class='lazyload-wrapper
from selenium import webdriver
import time
#driver = webdriver.Firefox()
driver = webdriver.Chrome()
url = "https://www.marinetraffic.com/en/ais/details/ships/imo:9854612"
driver.get(url)
time.sleep(3)
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
elements = driver.find_elements_by_xpath("//span[@class='lazyload-wrapper']")
for number, item in enumerate(elements):
print('--- item', number, '---')
#print('--- before ---')
#print(item.text)
actions.move_to_element(item).perform()
time.sleep(0.1)
#print('--- after ---')
#print(item.text)
try:
strong = item.find_element_by_xpath("//strong[contains(text(), 'cubic')]")
print(strong.text)
break
except Exception as ex:
#print(ex)
pass
结果:
--- item 0 ---
--- item 1 ---
--- item 2 ---
173400 cubic meters Liquid Gas
结果显示我可以使用 elements[2]
跳过两个元素,但我不确定此文本是否总是在第三个元素中。
编辑:
在创建我的版本之前,我测试了其他版本,这里是完整的工作代码
from selenium import webdriver
import time
#driver = webdriver.Firefox()
driver = webdriver.Chrome()
url = "https://www.marinetraffic.com/en/ais/details/ships/imo:9854612"
driver.get(url)
time.sleep(3)
def test0():
elements = driver.find_elements_by_xpath("//strong")
for item in elements:
print(item.text)
print('---')
item = driver.find_element_by_xpath("//strong[contains(text(), 'cubic')]")
print(item.text)
def test1a():
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
element = driver.find_element_by_xpath("//div[contains(@class,'MuiTypography-body1')][last()]//div")
actions.move_to_element(element).build().perform()
text = element.text
print(text)
def test1b():
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(0.5)
text = driver.find_element_by_xpath("//div[contains(@class,'MuiTypography-body1')][last()]//strong").text
print(text)
def test2():
from bs4 import BeautifulSoup
import re
soup = BeautifulSoup(driver.page_source, "html.parser")
soup.find_all(string=re.compile(r"\d+ cubic meters"))
def test3():
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
elements = driver.find_elements_by_xpath("//span[@class='lazyload-wrapper']")
for number, item in enumerate(elements, 1):
print('--- number', number, '---')
#print('--- before ---')
#print(item.text)
actions.move_to_element(item).perform()
time.sleep(0.1)
#print('--- after ---')
#print(item.text)
try:
strong = item.find_element_by_xpath("//strong[contains(text(), 'cubic')]")
print(strong.text)
break
except Exception as ex:
#print(ex)
pass
#test0()
#test1a()
#test1b()
#test2()
test3()
您的 xpath 是正确的并且在 Chrome 中有效。你得到 NoSuchElementException
因为元素没有加载你等待 3 秒并且不存在。
等待元素使用 WebDriverWait
class。它明确地等待元素的特定条件,在你的情况下礼物就足够了。
在下面的代码中,Selenium 将等待元素出现在 HTML 中 10 秒,每 500 毫秒轮询一次。您可以阅读 WebDriverWait
和条件 here。
一些有用的信息:
不可见元素 return 空字符串。在这种情况下,您需要等待元素的可见性,或者如果元素需要滚动才能滚动到它(添加的示例)。
您也可以使用 JavaScript 从不可见元素中获取文本。
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from selenium import webdriver
url = "https://www.marinetraffic.com/en/ais/details/ships/imo:9854612"
locator = "//strong[contains(text(),'cubic meters')]"
with webdriver.Chrome() as driver: # type: webdriver
wait = WebDriverWait(driver, 10)
driver.get(url)
cubic = wait.until(ec.presence_of_element_located((By.XPATH, locator))) # type: WebElement
print(cubic.text)
# Below examples just for information and not need for the case
# Example with scroll. Scroll to the element to make it visible
cubic.location_once_scrolled_into_view
print(cubic.text)
# Example using JavaScript. Works for not visible elements.
text = driver.execute_script("return arguments[0].textContent", cubic)
print(text)
使用 marinetraffic API 是正确的。
我正在尝试从深深嵌套在该网页 html 中的 <strong>
标签中提取文本:https://www.marinetraffic.com/en/ais/details/ships/imo:9854612
例如:
强标签是网页上唯一包含字符串 'cubic meters' 的标签 我的 objective 是提取整个文本,即“138124 立方米液化气”。当我尝试以下操作时,出现错误:
url = "https://www.marinetraffic.com/en/ais/details/ships/imo:9854612"
driver.get(url)
time.sleep(3)
element = driver.find_element_by_link_text("//strong[contains(text(),'cubic meters')]").text
print(element)
NoSuchElementException: Message: no such element: Unable to locate element: {"method":"link text","selector":"//strong[contains(text(),'cubic meters')]"}
我在这里做错了什么?任何建议,将不胜感激! 编辑:下面也报错:
element = driver.find_element_by_xpath("//strong[contains(text(),'cubic')]").text
我猜你应该先滚动到那个元素,然后才尝试访问它,包括获取它的文本。
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
element = driver.find_element_by_xpath("//div[contains(@class,'MuiTypography-body1')][last()]//div")
actions.move_to_element(element).build().perform()
text = element.text
如果上述方法还不够好,您可以像这样滚动一次页面高度:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(0.5)
the_text = driver.find_element_by_xpath("//div[contains(@class,'MuiTypography-body1')][last()]//strong").text
您可以为此使用 BeautifulSoup,更准确地说是 the string
argument;来自文档,“您可以搜索字符串而不是标签”。
作为参数,您还可以传递一个正则表达式模式。
>>> from bs4 import BeautifulSoup
>>> import re
>>> soup = BeautifulSoup(driver.page_source, "html.parser")
>>> soup.find_all(string=re.compile(r"\d+ cubic meters"))
['173400 cubic meters Liquid Gas']
如果您确定只有一个结果,或者您只需要第一个,您也可以使用 find
而不是 find_all
。
您的代码适用于 Firefox()
但不适用于 Chrome()
页面使用 lazy loading
,因此您必须滚动到 Summary
,然后它会加载预期的 strong
.
我使用了稍微慢一点的方法 - 我搜索了所有元素
class='lazyload-wrapper
,然后循环滚动到项目并检查是否有 strong
。如果没有 strong
然后我滚动到下一个 class='lazyload-wrapper
from selenium import webdriver
import time
#driver = webdriver.Firefox()
driver = webdriver.Chrome()
url = "https://www.marinetraffic.com/en/ais/details/ships/imo:9854612"
driver.get(url)
time.sleep(3)
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
elements = driver.find_elements_by_xpath("//span[@class='lazyload-wrapper']")
for number, item in enumerate(elements):
print('--- item', number, '---')
#print('--- before ---')
#print(item.text)
actions.move_to_element(item).perform()
time.sleep(0.1)
#print('--- after ---')
#print(item.text)
try:
strong = item.find_element_by_xpath("//strong[contains(text(), 'cubic')]")
print(strong.text)
break
except Exception as ex:
#print(ex)
pass
结果:
--- item 0 ---
--- item 1 ---
--- item 2 ---
173400 cubic meters Liquid Gas
结果显示我可以使用 elements[2]
跳过两个元素,但我不确定此文本是否总是在第三个元素中。
编辑:
在创建我的版本之前,我测试了其他版本,这里是完整的工作代码
from selenium import webdriver
import time
#driver = webdriver.Firefox()
driver = webdriver.Chrome()
url = "https://www.marinetraffic.com/en/ais/details/ships/imo:9854612"
driver.get(url)
time.sleep(3)
def test0():
elements = driver.find_elements_by_xpath("//strong")
for item in elements:
print(item.text)
print('---')
item = driver.find_element_by_xpath("//strong[contains(text(), 'cubic')]")
print(item.text)
def test1a():
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
element = driver.find_element_by_xpath("//div[contains(@class,'MuiTypography-body1')][last()]//div")
actions.move_to_element(element).build().perform()
text = element.text
print(text)
def test1b():
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")
time.sleep(0.5)
text = driver.find_element_by_xpath("//div[contains(@class,'MuiTypography-body1')][last()]//strong").text
print(text)
def test2():
from bs4 import BeautifulSoup
import re
soup = BeautifulSoup(driver.page_source, "html.parser")
soup.find_all(string=re.compile(r"\d+ cubic meters"))
def test3():
from selenium.webdriver.common.action_chains import ActionChains
actions = ActionChains(driver)
elements = driver.find_elements_by_xpath("//span[@class='lazyload-wrapper']")
for number, item in enumerate(elements, 1):
print('--- number', number, '---')
#print('--- before ---')
#print(item.text)
actions.move_to_element(item).perform()
time.sleep(0.1)
#print('--- after ---')
#print(item.text)
try:
strong = item.find_element_by_xpath("//strong[contains(text(), 'cubic')]")
print(strong.text)
break
except Exception as ex:
#print(ex)
pass
#test0()
#test1a()
#test1b()
#test2()
test3()
您的 xpath 是正确的并且在 Chrome 中有效。你得到 NoSuchElementException
因为元素没有加载你等待 3 秒并且不存在。
等待元素使用 WebDriverWait
class。它明确地等待元素的特定条件,在你的情况下礼物就足够了。
在下面的代码中,Selenium 将等待元素出现在 HTML 中 10 秒,每 500 毫秒轮询一次。您可以阅读 WebDriverWait
和条件 here。
一些有用的信息:
不可见元素 return 空字符串。在这种情况下,您需要等待元素的可见性,或者如果元素需要滚动才能滚动到它(添加的示例)。
您也可以使用 JavaScript 从不可见元素中获取文本。
from selenium.webdriver.common.by import By
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as ec
from selenium import webdriver
url = "https://www.marinetraffic.com/en/ais/details/ships/imo:9854612"
locator = "//strong[contains(text(),'cubic meters')]"
with webdriver.Chrome() as driver: # type: webdriver
wait = WebDriverWait(driver, 10)
driver.get(url)
cubic = wait.until(ec.presence_of_element_located((By.XPATH, locator))) # type: WebElement
print(cubic.text)
# Below examples just for information and not need for the case
# Example with scroll. Scroll to the element to make it visible
cubic.location_once_scrolled_into_view
print(cubic.text)
# Example using JavaScript. Works for not visible elements.
text = driver.execute_script("return arguments[0].textContent", cubic)
print(text)
使用 marinetraffic API 是正确的。