Как вычитать данные из динамической вебтаблицы?qa-57

Основные подходы к работе с таблицами

1. Определение структуры таблицы

Перед извлечением данных важно проанализировать HTML-структуру:

  • Таблица может быть реализована через <table> или div-структуру
  • Динамические таблицы часто используют lazy loading
  • Могут присутствовать вложенные элементы

2. Базовый способ чтения стандартной HTML-таблицы

// Получаем все строки таблицы
List<WebElement> rows = driver.findElements(By.cssSelector("table#dataTable tbody tr"));

for (WebElement row : rows) {
    // Получаем все ячейки в строке
    List<WebElement> cells = row.findElements(By.tagName("td"));

    // Извлекаем текст из каждой ячейки
    for (WebElement cell : cells) {
        System.out.print(cell.getText() + " | ");
    }
    System.out.println();
}

3. Работа с динамически загружаемыми таблицами

Прокрутка до загрузки всех данных:

WebElement table = driver.findElement(By.id("dynamicTable"));
long lastHeight = ((JavascriptExecutor)driver).executeScript("return arguments[0].scrollHeight", table);

while (true) {
    ((JavascriptExecutor)driver).executeScript("arguments[0].scrollTo(0, arguments[0].scrollHeight)", table);
    Thread.sleep(1000); // Ожидание загрузки

    long newHeight = ((JavascriptExecutor)driver).executeScript("return arguments[0].scrollHeight", table);
    if (newHeight == lastHeight) break;
    lastHeight = newHeight;
}

4. Чтение таблицы с пагинацией

List<DataRecord> allData = new ArrayList<>();

do {
    // Чтение данных с текущей страницы
    List<WebElement> rows = driver.findElements(By.cssSelector(".data-row"));
    for (WebElement row : rows) {
        // Парсинг данных строки
        DataRecord record = parseRow(row);
        allData.add(record);
    }

    // Переход на следующую страницу
    try {
        driver.findElement(By.cssSelector(".next-page:not(.disabled)")).click();
        wait.until(ExpectedConditions.stalenessOf(rows.get(0)));
    } catch (NoSuchElementException e) {
        break; // больше нет страниц
    }
} while (true);

5. Использование XPath для сложных таблиц

// Получение данных из конкретного столбца по имени заголовка
String headerXpath = "//table//th[contains(., '%s')]";
String columnName = "Price";
int colIndex = driver.findElements(By.xpath(String.format(headerXpath, columnName))).size();

List<String> prices = new ArrayList<>();
List<WebElement> rows = driver.findElements(By.xpath("//table/tbody/tr"));

for (int i = 1; i <= rows.size(); i++) {
    String cellXpath = String.format("//table/tbody/tr[%d]/td[%d]", i, colIndex);
    String price = driver.findElement(By.xpath(cellXpath)).getText();
    prices.add(price);
}

Обработка особых случаев

1. Таблицы с lazy loading

// Ожидание появления хотя бы одной строки
wait.until(ExpectedConditions.numberOfElementsToBeMoreThan(By.cssSelector(".table-row"), 0));

2. Таблицы с динамическими классами

// Использование частичного совпадения атрибутов
List<WebElement> rows = driver.findElements(By.cssSelector("div[class*='table-row-']"));

3. Извлечение сложных данных

// Получение данных-атрибутов
String dataId = row.getAttribute("data-id");

// Извлечение вложенных элементов
WebElement nested = cell.findElement(By.cssSelector(".nested-element"));

Оптимизация производительности

  1. Кэширование элементов:

    WebElement table = driver.findElement(By.id("mainTable"));
    // Дальнейшие поиски только внутри таблицы
    List<WebElement> rows = table.findElements(By.cssSelector("tr"));
    
  2. Пакетное чтение данных:

    JavascriptExecutor js = (JavascriptExecutor)driver;
    List<String> allData = (List<String>)js.executeScript(
        "return Array.from(document.querySelectorAll('table tr')).map(row => row.innerText);"
    );
    
  3. Параллельная обработка (для больших таблиц):

    rows.parallelStream().forEach(row -> processRow(row));
    

Хранение извлеченных данных

1. В коллекции объектов

public class TableData {
    private String name;
    private BigDecimal price;
    private LocalDate date;
    // геттеры/сеттеры
}

List<TableData> tableData = new ArrayList<>();
// заполнение данных при парсинге

2. В файлы

try (PrintWriter writer = new PrintWriter("output.csv")) {
    for (TableData data : tableData) {
        writer.println(data.toCsvString());
    }
}

3. В базу данных

jdbcTemplate.batchUpdate(
    "INSERT INTO table_data (name, price) VALUES (?, ?)",
    tableData.stream()
        .map(data -> new Object[]{data.getName(), data.getPrice()})
        .collect(Collectors.toList())
);

Резюмируем

  • Анализируйте структуру таблицы перед извлечением данных
  • Используйте комбинации CSS/XPath селекторов
  • Учитывайте динамическую природу - добавляйте ожидания
  • Оптимизируйте производительность при работе с большими таблицами
  • Выбирайте подходящую структуру для хранения извлеченных данных
  • Обрабатывайте особые случаи - lazy loading, пагинацию, виртуальный скроллинг

Профессиональный совет: Для сложных таблиц создайте класс-обертку TableReader с методами getColumnValues(), getRowData(), filterRows() и т.д., который будет инкапсулировать логику работы с конкретной таблицей в вашем приложении.