代码之家  ›  专栏  ›  技术社区  ›  Prophet

Selenium:选择页面上包含任何文本的所有元素

  •  0
  • Prophet  · 技术社区  · 4 年前

    我想选择页面上包含任何文本的所有元素。
    只有实际包含文本本身的元素,而不是仅在其子元素中包含文本的父元素。
    此XPath匹配包含任何非空文本的元素

    //*[text() != ""]
    

    然而,这

    List<WebElement> list = driver.findElements(By.xpath("//*[text() != '']"));
    

    给了我一个包含文本本身或其子元素的所有元素的列表。
    我可以重复这个 list 用这样的方法将实际包含文本的元素放入 real 列表

    List<WebElement> real = new ArrayList<>();
    for(WebElement element : list){
        js = (JavascriptExecutor)driver;
        String text = js.executeScript("""
        return jQuery(arguments[0]).contents().filter(function() {
            return this.nodeType == Node.TEXT_NODE;
        }).text();
        """, element);
        if(text.length()>0){
            real.add(element);
    }
    

    但这是一种变通方法。
    我想知道有没有一种方法可以直接或更优雅地获得包含任何文本的元素列表?

    1 回复  |  直到 4 年前
        1
  •  1
  •   pburgr    4 年前
        List<WebElement> elementsWithOwnText = new ArrayList<WebElement>();
        List<WebElement> allElements = driver.findElements(By.xpath("//*"));
        for (WebElement element: allElements) {
            List<WebElement> childElements = element.findElements(By.xpath(".//*"));
            String text = element.getText();
            if (childElements.size() == 0 && text.lenght() > 0) {
                elementsWithOwnText.add(element);
            }
        }
    

    注意 org.openqa.selenium.StaleElementReferenceException 。循环时 allElements 它们中的任何一个都可以不再附加到页面文档(动态内容f.e)。

        2
  •  1
  •   CCC    4 年前

    你可以试试这个: 它选择所有带有文本的叶子元素。

    List<WebElement> list = driver.findElements(By.xpath("//*[not(child::*) and text()]"));
            for (WebElement webElement : list)
                System.out.println(webElement.getText());
    
        3
  •  1
  •   doris    4 年前

    在找到所需的xpath之前,作为一个临时解决方案,我建议也尝试下面的迭代(尽管不如直接xpath有效)。

    在我的案例中,使用文本评估700个节点花费了1分钟,并返回了152个具有自己文本的元素:

    public static List<WebElement> getElementsWithText(WebDriver driver) {
        return driver.findElements(By.xpath("//*[normalize-space() != '']"))
                .stream().filter(element -> doesParentHaveText(element))
                .collect(Collectors.toList());
    }
    
    private static boolean doesParentHaveText(WebElement element) {
        try {
            String text = element.getText().trim();
            List<WebElement> children = element.findElements(By.xpath("./*"));
    
            for (WebElement child: children) {
                text = text.replace(child.getText(), "").trim();
            }
    
            return text.trim().replace("[\\n|\\t|\\r]", "").length() > 0;
        } catch (WebDriverException e) {
            return false; //in case something does wrong on reading text; you can change the return false with thrown error
        }
    }
    
        4
  •  0
  •   CCC    4 年前

    这可能会有所帮助: source

    List<String> elements = driver.findElements(By.xpath("//a")).stream().map(productWebElement -> productWebElement.getText()).distinct().collect(Collectors.toList());
            
        // Print count of product found
        System.out.println("Total unique product found : " + elements.size());
            
        // Printing product names
        System.out.println("All product names are : ");
        elements.forEach(name -> System.out.println(name));