Селен веб-драйвер для Java API: FindElement дает разные результаты - PullRequest
0 голосов
/ 22 июня 2019

Я использую селеновый веб-драйвер для Java, чтобы сканировать эту страницу:

https://www.immowelt.at/liste/wien/wohnungen/mieten?sort=relevanz

В моем коде метод

WebElement.findElement(...)

выдаёт разные результаты, как показано ниже:

1.) Мой исходный код:

package at.home.digest.services;

import java.util.ArrayList;
import java.util.List;


import org.apache.commons.lang3.StringUtils;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;

import at.home.digest.model.HomeToDeal;

public class ImmoweltBot {

    public static final String URL = "https://www.immowelt.at/";
    public static final String queryURL = URL + "/liste/wien/wohnungen/mieten?sort=relevanz";


    public static void main (String [] args) throws Exception {

        System.setProperty("webdriver.chrome.driver", "C:\\Temp\\chromedriver.exe");

        String URLPage = StringUtils.EMPTY;
        int page = 1;
        int totalNumberOfEntities = 6000;
        int numberOfEntitiesFound = 0;

        List<WebElement> elemnts = new ArrayList<>();

        WebDriver webDriver = new ChromeDriver();

        outer:
        while (numberOfEntitiesFound < totalNumberOfEntities){

        webDriver.get(queryURL + URLPage);


        WebDriverWait wait = new WebDriverWait(webDriver, 5);
        By searchResults = By.xpath("//*[contains(@class, 'clear relative js-listitem')]");

        JavascriptExecutor js = (JavascriptExecutor)webDriver;
        webDriver.manage().window().maximize();
        js.executeScript("window.scrollBy(0,1000)");

        final int totalNumberOfKeyDowns = 190;
        int keyDownTries = 0;
        while ((++keyDownTries < totalNumberOfKeyDowns)) {
            elemnts = wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(searchResults));
            webDriver.findElement(By.tagName("body")).sendKeys(Keys.DOWN);

        }

        WebElement elem = webDriver.findElement(By.xpath("//*[contains(@class, 'ellipsis margin_none')]"));
        totalNumberOfEntities = Utils.parseNumber(elem.getText()).intValue();

        for (int i = 0; i < elemnts.size(); i++) {
            WebElement divListItemClear = elemnts.get(i);
            HomeToDeal homeToRent = new HomeToDeal();
            String exposeURL = divListItemClear.findElement(By.tagName("a")).getAttribute("href");
            homeToRent.setURL(exposeURL);

            WebElement listContentClear = divListItemClear.findElement(By.xpath("//*[contains(@class, 'listcontent clear')]"));
            WebElement h2Elem = listContentClear.findElement(By.tagName("h2"));
            String text = h2Elem.getText();
            homeToRent.setDescription(text);

            System.out.println(homeToRent);
        }

        URLPage = "&cp="+ (++page);
        numberOfEntitiesFound+=elemnts.size();
     }
    }

}

Моя проблема в том, что строка

String exposeURL = divListItemClear.findElement(By.tagName("a")).getAttribute("href");

работает так, как ожидалось, и дает мнепоследующий URL-адрес элемента (для каждого нового изменения в цикле), однако строки

WebElement listContentClear = divListItemClear.findElement(By.xpath("//*[contains(@class, 'listcontent clear')]"));
        WebElement h2Elem = listContentClear.findElement(By.tagName("h2"));
        String text = h2Elem.getText();

дают мне КАЖДЫЙ РАЗ ВРЕМЯ И ТО ЖЕ значение HTML-элемента h2-, и это всегда значениепервый найденный элемент.

Есть идеи, что я делаю не так?

Спасибо!

1 Ответ

1 голос
/ 22 июня 2019

Вы стали жертвой классической ошибки, которую допускают многие при использовании XPath с Selenium. Реализации WebDriver следуют спецификации XPath для определения местоположения элементов, что означает, что // locator всегда относится к верхней части документа. Это даже так, если вы используете findElement из WebElement экземпляра. В коде, на который вы ссылаетесь, который выдает ошибку, вам нужно следующее:

WebElement listContentClear = divListItemClear.findElement(By.xpath(".//*[contains(@class, 'listcontent clear')]"));
WebElement h2Elem = listContentClear.findElement(By.tagName("h2"));
String text = h2Elem.getText();

Обратите внимание на . в начале локатора, указывая текущий узел в качестве узла контекста. Поскольку вы в основном находите элементы на основе значений в классе CSS элементов, это один из случаев, когда использование селекторов CSS вместо XPath позволило бы избежать этой проблемы.

Кроме того, я бы посчитал эти локаторы несколько хрупкими, поскольку атрибут class не гарантирует упорядочение значений класса. Другими словами, что касается браузера, <div class="listcontent clear"> семантически эквивалентен <div class="clear listcontent">. Если бы браузер отображал элементы как последние, а не как первые, CSS-селектор div.listcontent.clear найдет оба отображения, а используемый вами XPath - нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...