Извлеките простой HTML после преобразования JS с помощью R и chromote

Я работаю над проектом, в котором мне нужно обрабатывать HTML-таблицы, созданные пакетом tinytable. HTML включает JavaScript, который динамически применяет стили CSS к ячейкам таблицы. Моя цель — извлечь измененный простой HTML-код после применения всех стилей.

Это пример таблицы, которую я хотел бы обработать:

library(tinytable)
tt(mtcars[1:4, 1:4]) |> 
    style_tt(j = 1:2, background = "teal", color = "white") |>
    save_tt("example.html", overwrite = TRUE)

Этот код сохраняет файл example.html с цветами, примененными с помощью JavaScript. Я хотел бы преобразовать это в простой HTML со стилями.

Я очень открыт для предложений по альтернативам. Единственный путь, который я попробовал, — это сохранить HTML во временный файл, использовать servr для обслуживания файла, затем chromote для просмотра файла без головы и его извлечения. Тем не менее, я продолжаю сталкиваться с проблемами тайм-аута.

Опять же, я буду рад попробовать другую стратегию, если вы сможете предложить что-то более эффективное и прямое.

Вот что я пробовал до сих пор:

library(servr)
library(chromote)
library(tinytable)

serve_and_strip <- function(filename) {
    fn <- file.path(tempdir(), "index.html")
    file.copy(filename, fn, overwrite = TRUE)
    srv <- servr::httd(tempdir())
    url <- file.path(srv$url, "index.html")
    b <- ChromoteSession$new()
    b$Page$navigate(url)
    tab <- b$Runtime$evaluate("document.querySelector('table').outerHTML")$result$value
    sty <- b$Runtime$evaluate("document.querySelector('style').outerHTML")$result$value
    out <- list(tab, sty)
    b$close()
    servr::daemon_stop(srv$daemon)
    return(out)
}

serve_and_strip("example.html")

Обновлено: если я просто скопирую HTML-файл, первая ячейка отобразится как <td>21.0</td>. Однако если вы загрузите страницу в Firefox или Chrome и щелкните правой кнопкой мыши, чтобы «Проверить» ячейку, вы увидите, что она стала: <td class = "tinytable_css_n9oxlmixvkthzx38wcrd">21.0</td>. Это связано с тем, что функции Javascript запускались Firefox и добавляли в ячейку информацию о классе. Я хочу получить код HTML и CSS со страницы после применения функций JS. Вот почему я предложил пройти через headless-браузер.

HTML-код перед преобразованием выглядит следующим образом:

<!DOCTYPE html> 
<html lang = "en">
  <head>
    <meta charset = "UTF-8">
    <meta name = "viewport" content = "width=device-width, initial-scale=1.0">
    <title>tinytable_xg1s2bqenh3yuyr9x2mg</title>
    <link href = "https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel = "stylesheet">
    <style>
.table td.tinytable_css_u43qs0b5ucz8ik5jm341, .table th.tinytable_css_u43qs0b5ucz8ik5jm341 {    border-bottom: solid 0.1em #d3d8dc; }
.table td.tinytable_css_4rjvz3zmw0n1t4i0jlbe, .table th.tinytable_css_4rjvz3zmw0n1t4i0jlbe {    color: white; background-color: teal; }
    </style>
    <script src = "https://polyfill.io/v3/polyfill.min.js?features=es6"></script>
    <script id = "MathJax-script" async src = "https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
    <script>
    MathJax = {
      tex: {
        inlineMath: [['$', '$'], ['\\(', '\\)']]
      },
      svg: {
        fontCache: 'global'
      }
    };
    </script>
  </head>

  <body>
    <div class = "container">
      <table class = "table table-borderless" id = "tinytable_xg1s2bqenh3yuyr9x2mg" style = "width: auto; margin-left: auto; margin-right: auto;" data-quarto-disable-processing='true'>
        <thead>
        
              <tr>
                <th scope = "col">mpg</th>
                <th scope = "col">cyl</th>
                <th scope = "col">disp</th>
                <th scope = "col">hp</th>
              </tr>
        </thead>
        
        <tbody>
                <tr>
                  <td>21.0</td>
                  <td>6</td>
                  <td>160</td>
                  <td>110</td>
                </tr>
                <tr>
                  <td>21.0</td>
                  <td>6</td>
                  <td>160</td>
                  <td>110</td>
                </tr>
                <tr>
                  <td>22.8</td>
                  <td>4</td>
                  <td>108</td>
                  <td> 93</td>
                </tr>
                <tr>
                  <td>21.4</td>
                  <td>6</td>
                  <td>258</td>
                  <td>110</td>
                </tr>
        </tbody>
      </table>
    </div>

    <script src = "https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
    <script>
      function styleCell_tinytable_pii6zht3qrjzjy0n9jwp(i, j, css_id) {
        var table = document.getElementById("tinytable_xg1s2bqenh3yuyr9x2mg");
        table.rows[i].cells[j].classList.add(css_id);
      }
      function insertSpanRow(i, colspan, content) {
        var table = document.getElementById('tinytable_xg1s2bqenh3yuyr9x2mg');
        var newRow = table.insertRow(i);
        var newCell = newRow.insertCell(0);
        newCell.setAttribute("colspan", colspan);
        // newCell.innerText = content;
        // this may be unsafe, but innerText does not interpret <br>
        newCell.innerHTML = content;
      }
      function spanCell_tinytable_pii6zht3qrjzjy0n9jwp(i, j, rowspan, colspan) {
        var table = document.getElementById("tinytable_xg1s2bqenh3yuyr9x2mg");
        const targetRow = table.rows[i];
        const targetCell = targetRow.cells[j];
        for (let r = 0; r < rowspan; r++) {
          // Only start deleting cells to the right for the first row (r == 0)
          if (r === 0) {
            // Delete cells to the right of the target cell in the first row
            for (let c = colspan - 1; c > 0; c--) {
              if (table.rows[i + r].cells[j + c]) {
                table.rows[i + r].deleteCell(j + c);
              }
            }
          }
          // For rows below the first, delete starting from the target column
          if (r > 0) {
            for (let c = colspan - 1; c >= 0; c--) {
              if (table.rows[i + r] && table.rows[i + r].cells[j]) {
                table.rows[i + r].deleteCell(j);
              }
            }
          }
        }
        // Set rowspan and colspan of the target cell
        targetCell.rowSpan = rowspan;
        targetCell.colSpan = colspan;
      }

window.addEventListener('load', function () { styleCell_tinytable_pii6zht3qrjzjy0n9jwp(0, 0, 'tinytable_css_u43qs0b5ucz8ik5jm341') })
window.addEventListener('load', function () { styleCell_tinytable_pii6zht3qrjzjy0n9jwp(0, 1, 'tinytable_css_u43qs0b5ucz8ik5jm341') })
window.addEventListener('load', function () { styleCell_tinytable_pii6zht3qrjzjy0n9jwp(0, 2, 'tinytable_css_u43qs0b5ucz8ik5jm341') })
window.addEventListener('load', function () { styleCell_tinytable_pii6zht3qrjzjy0n9jwp(0, 3, 'tinytable_css_u43qs0b5ucz8ik5jm341') })
window.addEventListener('load', function () { styleCell_tinytable_pii6zht3qrjzjy0n9jwp(0, 0, 'tinytable_css_4rjvz3zmw0n1t4i0jlbe') })
window.addEventListener('load', function () { styleCell_tinytable_pii6zht3qrjzjy0n9jwp(0, 1, 'tinytable_css_4rjvz3zmw0n1t4i0jlbe') })
window.addEventListener('load', function () { styleCell_tinytable_pii6zht3qrjzjy0n9jwp(1, 0, 'tinytable_css_4rjvz3zmw0n1t4i0jlbe') })
window.addEventListener('load', function () { styleCell_tinytable_pii6zht3qrjzjy0n9jwp(1, 1, 'tinytable_css_4rjvz3zmw0n1t4i0jlbe') })
window.addEventListener('load', function () { styleCell_tinytable_pii6zht3qrjzjy0n9jwp(2, 0, 'tinytable_css_4rjvz3zmw0n1t4i0jlbe') })
window.addEventListener('load', function () { styleCell_tinytable_pii6zht3qrjzjy0n9jwp(2, 1, 'tinytable_css_4rjvz3zmw0n1t4i0jlbe') })
window.addEventListener('load', function () { styleCell_tinytable_pii6zht3qrjzjy0n9jwp(3, 0, 'tinytable_css_4rjvz3zmw0n1t4i0jlbe') })
window.addEventListener('load', function () { styleCell_tinytable_pii6zht3qrjzjy0n9jwp(3, 1, 'tinytable_css_4rjvz3zmw0n1t4i0jlbe') })
window.addEventListener('load', function () { styleCell_tinytable_pii6zht3qrjzjy0n9jwp(4, 0, 'tinytable_css_4rjvz3zmw0n1t4i0jlbe') })
window.addEventListener('load', function () { styleCell_tinytable_pii6zht3qrjzjy0n9jwp(4, 1, 'tinytable_css_4rjvz3zmw0n1t4i0jlbe') })
    </script>

  </body>

</html>

Использование kableExtra вместо tinytable бесполезно? Что-то вроде kbl(mtcars[1:4, 1:4], row.names = FALSE) |> kable_styling() |> column_spec(1:2, background = "teal", color = "white") |> save_kable("kable.html") ?

margusl 12.04.2024 18:49

Да, к сожалению. На самом деле это для разработки новой функции в самом пакете tinytable.

Vincent 12.04.2024 19:29
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
1
2
60
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Это решение кажется намного проще и работает:

library(chromote)
url <- "file:/home/username/example.html"
b <- ChromoteSession$new()
b$Page$navigate(url)
b$Page$loadEventFired(wait = FALSE)
body <- b$Runtime$evaluate("document.querySelector('body').outerHTML")$result$value
b$close()

Другие вопросы по теме