如何使用 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 绑定执行鼠标悬停(悬停在元素上)?

python+selenium遇到鼠标悬停不成功可以使用js进行操作

Selenium在python中按类名查找悬停按钮元素

python selenium 鼠标悬停