如何使用 selenium python 在悬停的 highcharts 上抓取值?
Posted
技术标签:
【中文标题】如何使用 selenium python 在悬停的 highcharts 上抓取值?【英文标题】:How to scrape values on hovering highcharts using selenium python? 【发布时间】:2021-08-01 13:28:00 【问题描述】:我正在尝试使用 Python 和 Selenium 从 https://www.similarweb.com/website/zalando.de/#overview 抓取数据。困难的部分是数据只有在图表上的一个点悬停时才会出现。
这是我的代码。
websites = ['https://www.similarweb.com/website/zalando.de/#overview']
options = webdriver.ChromeOptions()
options.add_argument('start-maximized')
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
browser = webdriver.Chrome(ChromeDriverManager().install(), options=options)
delays = [7, 4, 6, 2, 10, 19]
delay = np.random.choice(delays)
for crawler in websites:
browser.get(crawler)
time.sleep(2)
time.sleep(delay)
tooltip = browser.find_element(By.XPATH, "//*[local-name() = 'svg']/*[local-name()='g'][8]/*[local-name()='text']")
ActionChains(browser).move_to_element(tooltip).perform()
month_value = browser.find_element(By.XPATH, "//*[local-name() = 'svg']/*[local-name()='g' and @class='highcharts-tooltip']/*[local-name()='text']")
print('Are they here?', month_value.text)
months = browser.find_elements(By.XPATH, "//*[local-name() = 'svg']/*[local-name()='g'][6]/*/*")
for date in months:
print(date.text)
我可以将月份数据打印为:
Nov '20
Dec '20
Jan '21
Feb '21
Mar '21
Apr '21
但无法打印每个月的值 - 它给出了一个空打印 - 他们在这里吗?
我如何确保它是先悬停然后刮掉?请帮忙
编辑:这是更新后的代码
def website_monitoring():
websites = ['https://www.similarweb.com/website/zalando.de/#overview']
options = webdriver.ChromeOptions()
options.add_argument('start-maximized')
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option("useAutomationExtension", False)
browser = webdriver.Chrome(ChromeDriverManager().install(), options=options)
for crawler in websites:
browser.get(crawler)
wait = WebDriverWait(browser, 10)
months = []
monthly_values = []
charts = wait.until(EC.presence_of_element_located((By.XPATH, '//*[@id="highcharts-0"]')))
highchart = browser.find_elements_by_xpath('//*[@id="highcharts-0"]/svg/g[4]/g[1]')
for elements in highchart:
hover = ActionChains(browser).move_to_element(elements)
hover.perform()
month = browser.find_elements_by_css_selector('#highcharts-0 > svg > g.highcharts-tooltip > text > tspan:nth-child(1)')
month_values = browser.find_elements_by_css_selector('#highcharts-0 > svg > g.highcharts-tooltip > text > tspan:nth-child(3)')
months.append(month[0].text)
monthly_values.append(month_values[0].text)
print('Months', months)
print('Monthly Values', monthly_values)
if __name__ == "__main__":
website_monitoring()
我得到的输出是:
Months []
Monthly Values []
【问题讨论】:
【参考方案1】:当网站显示动态图表时,它会从其数据库或外部 API 中检索基础数据。然后,服务器发送此数据,或使此数据可用(Json、xml、plain、csv)用于图形框架(d3js、highcharts...)。有时这些数据会通过模板引擎集成到 html 中,或者硬写在 javascript 文件中。
经过一番调查,我们看到这里的数据存储在 html 末尾的脚本标记中(参见 F12 -> Inspector)。包含数据的变量是 preloadedData。它似乎包含页面动画中使用的所有数据,包括您感兴趣的数据。
from selenium import webdriver
from bs4 import BeautifulSoup as bs
import time
import json
import re
driver = webdriver.Firefox()
driver.get("https://www.similarweb.com/website/zalando.de")
html = driver.page_source
soup = bs(html, "html.parser")
# get all scripts tags and select the one of interest
balises_script = soup.find_all("script")
target_balise = [str(el) for el in balises_script if "Sw.preloadedData" in str(el)][0]
# use regex to extract dict like string
m = re.findall(r"Sw.preloadedData = (.+)\;", target_balise)[0]
# dict like string to dict
data = json.loads(m)
# explore data to see where data of interest is
sub_data_of_interest = data['overview']['EngagementsSimilarweb']['WeeklyTrafficNumbers']
for items in sub_data_of_interest.items():
print(items)
driver.close()
导致:
('2020-11-01', 29914593)
('2020-12-01', 27141507)
('2021-01-01', 26863605)
('2021-02-01', 22589520)
('2021-03-01', 24745220)
('2021-04-01', 26249414)
注意 1:Selenium 经常被误用,它被设计用于测试网页,而不是检索数据。但是,有时使用此工具会更容易。
注2:我尝试了经典的requests + bs方法,它更复杂:包含数据的脚本标签是由另一个使用cookie rimbabelle的javascript生成的。
注意 3:请注意,该站点会检测到可能是非人类的请求(太快)。考虑在你的 for 循环中放一个 time.sleep(如果你在多个 URL 上循环)。
【讨论】:
【参考方案2】:这有点棘手。但我注意到一些我认为会有所帮助的东西:无论它是否在页面上,信息都存在于 DOM 上,并且有一个独特的 css 选择器('tspan:nth-child(3)')
。问题是,它只是在您移动鼠标时动态显示值的一个元素。因此,如果您确定要从哪些点刮取值,但这里有一种快速打印我认为您想要的值的方法:
for point in points_to_hover:
driver.find_element_by_css_selector('tspan:nth-child(3)').get_attribute("innerText")
【讨论】:
for 循环中的“points_to_hover”到底是什么? 您定义的一组 web 元素,由您要悬停的每个点组成。 哦!工具提示。!!明白了 它说找不到元素...它不工作 @technophile_3 你能发布你最近尝试的代码,以及你得到的错误信息吗?以上是关于如何使用 selenium python 在悬停的 highcharts 上抓取值?的主要内容,如果未能解决你的问题,请参考以下文章
Selenium3+python3--如何定位鼠标悬停才显示的元素
使用 Python 和 Selenium 将鼠标悬停在图形上
有没有办法使用 Selenium 和 Python 绑定执行鼠标悬停(悬停在元素上)?