如何用Selenium Java获取印度人口SVG图表1951-2024年悬停数据
问题描述
需要抓取印度人口折线图(1951-2024年)的年度数据,通过Selenium的moveToElement()悬停SVG元素时失效,无法触发tooltip获取数据。当前代码尝试通过SVG矩形坐标移动鼠标,但没有效果。
问题原因与解决方案
1. SVG元素定位错误
原代码定位的rect是图表背景容器,并非折线图的可交互数据点。该网站用Highcharts生成图表,实际触发tooltip的是折线的path元素或数据点circle元素,必须定位到这些可交互元素才能触发悬停事件。
2. 坐标计算逻辑错误
moveToElement(WebElement element, int xOffset, int yOffset)的偏移量是相对于元素左上角的坐标,原代码计算出的负数偏移会把鼠标移出元素范围,自然无法触发交互。
3. 正确实现思路
- 定位到Highcharts生成的折线
path元素(核心交互区域) - 根据年份跨度(1951-2024共74年)计算鼠标移动步长,遍历每个数据点位置
- 每次移动后抓取页面上的HTML tooltip数据(该网站tooltip是独立的HTML元素,不是SVG内部元素)
修改后的完整代码
package selenium_concepts.SVGElements; import org.openqa.selenium.*; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.interactions.Actions; import org.testng.annotations.AfterTest; import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.time.Duration; import java.util.List; public class IndianPopulationSVGElementTest { WebDriver driver; JavascriptExecutor jse; @BeforeTest public void setup() { driver = new ChromeDriver(); driver.manage().window().maximize(); driver.manage().deleteAllCookies(); driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(20)); driver.get("https://www.worldometers.info/world-population/india-population/"); } @Test(description = "India Population Graph data test") public void svgGraphIndiaPopulation() throws InterruptedException { jse = ((JavascriptExecutor) driver); // 定位到SVG图表容器,滚动到可见区域 WebElement svgContainer = driver.findElement(By.xpath("//*[local-name()='svg' and @id='chartContainer']")); jse.executeScript("arguments[0].scrollIntoView({behavior: 'smooth', block: 'center'});", svgContainer); Thread.sleep(2000); // 获取页面基础信息 String heading = driver.findElement(By.cssSelector("#maincounter-wrap h1")).getText(); String totalPopulationIndia = driver.findElement(By.cssSelector(".maincounter-number span")).getText(); String indianPopulationBetweenYear = driver.findElement(By.xpath("(//div[@class='chart-header'])[1]")).getText(); System.out.println("-------------- " + heading + " ----------------- "); System.out.println("Total Population India: " + totalPopulationIndia); System.out.println("Between Years: " + indianPopulationBetweenYear); // 定位到折线图的路径元素(可触发tooltip的交互区域) WebElement linePath = driver.findElement(By.xpath("//*[local-name()='svg']//*[name()='path' and contains(@class, 'highcharts-graph')]")); Actions actions = new Actions(driver); // 获取路径元素的位置和尺寸,计算遍历步长 int pathWidth = linePath.getSize().getWidth(); // 1951到2024共74年,设置步长适配数据点数量 int step = pathWidth / 73; // 遍历每个数据点位置 for (int i = 0; i <= pathWidth; i += step) { // 移动鼠标到路径上的对应位置(相对于元素左上角的偏移) actions.moveToElement(linePath, i, 0).perform(); Thread.sleep(500); // 抓取tooltip中的数据 try { List<WebElement> tooltipTexts = driver.findElements(By.xpath("//*[local-name()='div' and contains(@class, 'highcharts-tooltip')]//span")); if (!tooltipTexts.isEmpty()) { String year = tooltipTexts.get(0).getText(); String population = tooltipTexts.get(1).getText(); System.out.println(year + " -> " + population); } } catch (NoSuchElementException e) { System.out.println("Tooltip not found at position: " + i); } } } @AfterTest public void tearDown() throws InterruptedException { Thread.sleep(2000); driver.quit(); } }
关键说明
- 替换原有的
rect定位,改为定位折线图的path元素(Highcharts生成的可交互折线) - 按年份跨度计算移动步长,确保每个数据点都能被覆盖
- 抓取独立的HTML tooltip元素,而非SVG内部元素(该网站tooltip采用HTML渲染)
- 增加异常处理,避免因tooltip加载延迟导致的元素未找到错误
内容的提问来源于stack exchange,提问作者Ravi Lohana
相关产品推荐
相关产品推荐

