Как скрести HTML-таблицы на JavaScript [Экспорт данных таблицы в CSV]

Первоначально опубликовано на ScraperAPI.

HTML-таблицы являются лучшими источниками данных в Интернете. Они просты для понимания и могут хранить огромное количество данных в простом для чтения и понимания формате. Умение скрапировать HTML-таблицы — важный навык для любого разработчика, интересующегося наукой о данных или анализом данных в целом.

В этом учебнике мы углубимся в изучение HTML-таблиц и создадим простой, но мощный скрипт для извлечения табличных данных и экспорта их в CSV-файл.

Что такое веб-таблица HTML?

Таблица HTML — это набор строк и столбцов, которые используются для отображения информации в формате сетки непосредственно на веб-странице. Они обычно используются для отображения табличных данных, таких как электронные таблицы или базы данных, и являются отличным источником данных для наших проектов.

От спортивных данных и данных о погоде до книг и авторских данных — большинство больших наборов данных в Интернете доступны через HTML-таблицы, потому что они отлично подходят для отображения информации в структурированном и удобном для навигации формате.

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

Понимание структуры HTML-таблицы

Хотя вы видите только столбцы и строки на переднем плане, на самом деле эти таблицы создаются с помощью нескольких различных HTML-тегов:

  • : : обозначает начало HTML-таблицы
  • : Указывает на строку в таблице
  • <td>: Определяет ячейку в таблице
  • Содержимое помещается внутрь тега <td>, а <tr> используется для создания строки. Другими словами: Table > Row > Cell || table > tr > td иерархия соблюдается для создания HTML-таблицы.

    Специальная ячейка может быть создана с помощью тега <th>, который означает заголовок таблицы. В принципе, первые ячейки первой строки могут быть созданы с помощью тега <th>, чтобы указать, что эта строка является заголовком таблицы.

    Вот пример создания простой HTML-таблицы с двумя строками и двумя столбцами:

    https://gist.github.com/scraperapikerins/ada86e5a5c5398599b77d4399be543ed

    <table>
      <tr>
        <th>Pet 1</th>
        <th>Pet 2</th>
      </tr>
      <tr>
        <td>Dog</td>
        <td>Cat</td>
      </tr>
    </table>
    

    Однако есть одно существенное отличие при соскабливании HTML-таблиц. В отличие от других элементов на веб-странице, селекторы CSS нацелены на общие ячейки и строки — или даже на всю таблицу — потому что все эти элементы фактически являются компонентами элемента <table>.

    Вместо того чтобы использовать CSS-селектор для каждой точки данных, которые мы хотим соскоблить, нам нужно будет создать список со всеми строками таблицы и пройтись по ним, чтобы взять данные из их ячеек.

    Если мы понимаем эту логику, то создание нашего скрипта на самом деле довольно простое.

    Скраппинг HTML-таблиц в CSV с помощью Node.JS

    Если вы впервые используете Node.JS для веб-скраппинга, возможно, будет полезно просмотреть некоторые из наших предыдущих уроков:

    • Web Scraping with JavaScript and Node.js
    • Как создать скрепер для LinkedIn бесплатно
    • Как создать скребок данных для футбола Шаг за шагом

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

    Примечание: Инструкции по установке Node.JS приведены в первой статье списка.

    В сегодняшнем проекте мы создадим веб-скрейпер с использованием Axios и Cheerio для сбора данных о сотрудниках, отображаемых на сайте https://datatables.net/examples/styling/display.html.

    Мы извлечем имя, должность, офис, возраст, дату начала работы и зарплату для каждого сотрудника, а затем отправим данные в CSV с помощью пакета ObjectsToCsv.

    1. Подготовка файлов

    Чтобы начать наш проект, давайте создадим новую директорию с именем html-table-scraper, откроем новую папку в VScode (или вашем редакторе кода) и откроем новый терминал.

    В терминале запустим npm init -y, чтобы начать новый проект Node.JS. Теперь у вас в папке будет новый JSON-файл.

    Далее мы установим наши зависимости с помощью следующих команд:

    • Axios: npm install axios.
    • Cheerio: npm install cheerio.
    • ObjectsToCsv: npm install objects-to-csv

    Для установки одной командой: npm i axios cheerio objects-to-csv.

    Теперь мы можем создать новый файл с именем tablescraper.js и импортировать наши зависимости в верхней части.

    const axios = require("axios");
    const cheerio = require("cheerio");
    const ObjectsToCsv = require("objects-to-csv");
    

    Кроме того, ваш проект должен выглядеть следующим образом:

    2. Тестирование целевого сайта с помощью DevTools

    Прежде чем писать код, нам нужно понять, как структурирован сайт. Да, все таблицы используют базовую структуру, но это не значит, что все они созданы одинаково.

    Первое, что нам нужно определить, является ли это на самом деле таблицей HTML. Очень часто сайты используют JavaScript для внедрения данных в свои таблицы, особенно если в них используются данные в реальном времени. Для таких случаев нам придется использовать совершенно другой подход, например, использовать безголовый браузер.

    Чтобы проверить, находятся ли данные внутри HTML-файла, достаточно скопировать некоторые точки данных — допустим, имя — и поискать их в исходном коде страницы.

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

    Кроме того, теперь мы знаем, что всего есть 57 строк для сканирования. Это важно, потому что мы можем знать, действительно ли мы захватываем все доступные данные.

    Второе, что мы хотим проверить непосредственно в браузере, — это наши селекторы. Вместо того чтобы отправлять кучу ненужных запросов, мы можем использовать консоль браузера для захвата элементов с помощью метода document.querySelectorAll() method.

    Если мы откроем консоль и напечатаем document.querySelectorAll('table'), она выдаст четыре различные таблицы.

    Перемещаясь по таблицам, мы быстро поняли, что первая таблица (номер 0) является правильной. Поэтому давайте повторим все сначала, но уже с указанием класса, который в списке представлен точками (.).

    Отлично, мы на шаг ближе к нашим данным!

    Если присмотреться, данные таблицы обернуты вокруг тега <tbody>, поэтому давайте добавим его в наш селектор, чтобы убедиться, что мы захватываем только те строки, которые содержат нужные нам данные.

    Наконец, мы хотим захватить все строки и убедиться, что наш селектор захватывает все 57 строк.

    Примечание: Поскольку мы используем консоль для выбора элементов в отображаемом HTML, нам необходимо установить общее количество отображаемых элементов равным 100. В противном случае наш селектор на консоли покажет только 10 элементов узла.

    Со всей этой информацией мы можем приступить к написанию кода!

    3. Отправка HTTP-запроса и парсинг необработанного HTML

    Axios позволяет очень просто отправлять HTTP-запросы внутри Async Function. Все, что нам нужно сделать, это создать функцию async и передать URL в Axios в константе с именем response. Мы также будем регистрировать код состояния ответа (который должен быть равен 200 для успешного запроса).

    (async function html_scraper() {
       const response = await axios('https://datatables.net/examples/styling/display.html');
       console.log(response.status)
    })();
    

    Примечание: Вы можете назвать эти переменные как угодно, но пусть они будут как можно более описательными.

    Далее мы сохраним данные из ответа (необработанный HTML) в новой константе с именем html, чтобы затем передать их Cheerio для разбора с помощью cheerio.load().

    const html = await response.data;
       const $ = cheerio.load(html);
    

    4. Итерация строк таблицы HTML

    Используя селектор, который мы тестировали ранее, давайте выберем все строки внутри HTML-таблицы.

    const allRows = $('table.display &amp;gt; tbody &amp;gt; tr');
      console.log(allRows.length)
    

    Для проверки, давайте выведем в console.log() длину allRows, чтобы убедиться, что мы действительно выбрали все целевые строки.

    57 — это именно то, к чему мы стремились!

    Конечно, чтобы пройти по списку строк, мы будем использовать метод .each(), но есть еще одна вещь, которую нам нужно выяснить: порядок ячеек.

    В отличие от обычных элементов HTML, ячейки не имеют присвоенного им уникального класса. Поэтому попытка отсканировать каждую точку данных с помощью CSS-класса может привести к путанице. Вместо этого мы будем ориентироваться на позицию <td> в строке.

    Другими словами, мы скажем нашему скрипту перейти к каждой строке, выбрать все ячейки внутри строки, а затем сохранить каждую точку данных в переменной на основе ее позиции в строке.

    Примечание: В Node.JS все списки начинаются с 0. Поэтому первая позиция будет [0], а вторая ячейка — [1].

    Но как узнать, какая позиция какая? Вернемся к консоли нашего браузера и проверим это:

    Теперь, когда мы знаем, где находится каждый элемент по отношению к остальным, вот готовый парсер:

    allRows.each((index, element) =&amp;gt; {
        const tds = $(element).find('td');
        const name = $(tds[0]).text();
        const position = $(tds[1]).text();
        const office = $(tds[2]).text();
        const age = $(tds[3]).text();
        const startDate = $(tds[4]).text();
        const salary = $(tds[5]).text();
    

    5. Запихивание собранных данных в пустой массив

    Если мы console.log() соскобленные данные, то увидим, что мы соскабливаем текст из каждой ячейки, но с очень беспорядочными результатами — что, в свою очередь, затрудняет создание нашего CSV-файла.

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

    Сначала создайте пустой массив вне основной функции — если вы создадите пустой массив внутри функции, он будет перезаписываться при каждой итерации, а это не то, что нам нужно.

    employeeData = [];
    

    Затем, в рамках нашего парсера, воспользуемся методом .push(), чтобы сохранить наши данные в созданном нами пустом списке.

    employeeData.push({
        'Name': name,
        'Position': position,
        'Office': office,
        'Age': age,
        'Start Date': startDate,
        'Salary': salary,
    })
    

    Как всегда, давайте console.log() длину employeeData, чтобы убедиться, что теперь у нас в нем 57 элементов.

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

    Как мы видим, все данные теперь хранятся внутри узловых элементов, которые содержат каждую часть данных в структурированном формате.

    6. Отправка собранных данных в CSV-файл

    Когда наши данные упорядочены, мы можем передать наш список в ObjectsToCsv, и он создаст файл для нас без лишней работы:

    const csv = new ObjectsToCsv(employeeData);
    await csv.toDisk('./employeeData.csv')
    

    Все, что нам нужно сделать, это создать новый объект csv и передать список в ObjectsToCsv, а затем сказать ему сохранить его на нашей машине, указав путь.

    7. Скребок HTML-таблиц [Полный код]

    Поздравляем, вы официально создали свой первый скребок HTML-таблиц! Сравните свой код с готовой кодовой базой этого руководства, чтобы убедиться, что вы ничего не упустили:

    const axios = require("axios");
    const cheerio = require("cheerio");
    const ObjectsToCsv = require("objects-to-csv");
    
    employeeData = [];
    
    (async function html_scraper() {
       const response = await axios('https://datatables.net/examples/styling/display.html')
       const html = await response.data;
       const $ = cheerio.load(html);
    
       //Selecting all rows inside our target table
       const allRows = $('table.display &gt; tbody &gt; tr');
       console.log('Going through rows')
    
       //Looping through the rows
       allRows.each((index, element) =&gt; {
           //Selecting all cells within the row
           const tds = $(element).find('td');
           //Extracting the text out of each cell
           const name = $(tds[0]).text();
           const position = $(tds[1]).text();
           const office = $(tds[2]).text();
           const age = $(tds[3]).text();
           const startDate = $(tds[4]).text();
           const salary = $(tds[5]).text();
    
           //Pushing scraped data to our empty array
           employeeData.push({
               'Name': name,
               'Position': position,
               'Office': office,
               'Age': age,
               'Start Date': startDate,
               'Salary': salary,
           })
       })
       //Exporting scraped data to a CSV file
       console.log('Saving data to CSV');
       const csv = new ObjectsToCsv(employeeData);
       await csv.toDisk('./employeeData.csv')
       console.log('Saved to CSV')
    })();
    

    После запуска нашего скрипта в папке проекта создается новый CSV-файл:

    Теперь вы можете использовать эти данные для дальнейшего анализа, например, для сравнения зарплат на основе названия должности или даты начала работы, или для поиска тенденций в больших наборах данных о вакансиях.

    Конечно, этот скрипт можно адаптировать для работы практически с любой HTML-таблицей, которую вы найдете, так что будьте открыты для новых возможностей.

    Избегайте блокировки: Интеграция ScraperAPI в одной строке кода

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

    По этой причине вам необходимо следовать некоторым лучшим практикам веб-скрейпинга, чтобы убедиться, что вы правильно ведете свои проекты, не оказывая слишком большого давления на целевой сайт и не подвергая свой скрипт и IP риску получить бан или попасть в черный список, что сделает невозможным повторный доступ к нужным данным с вашей машины.

    Для обработки ротации IP, рендеринга JavaScript, поиска и внедрения HTTP-заголовков, CAPTCHA и многого другого, все, что нам нужно сделать, это отправить наш первоначальный запрос через сервер ScraperAPI. Этот API будет использовать многолетний статистический анализ и машинное обучение для определения наилучшей комбинации заголовков и прокси, обработки любых неуспешных запросов и определения времени запроса, чтобы не перегружать целевой сервер.

    Добавить его в наш скрипт так же просто, как добавить эту строку в URL, передаваемый Axios:

    const response = await axios('http://api.scraperapi.com?api_key={Your_API_Key}&url=https://datatables.net/examples/styling/display.html')
    

    Не забудьте заменить {Ваш_API_Key} своим собственным ключом API, который вы можете получить, создав бесплатный аккаунт ScraperAPI.

    Ваш первый запрос займет немного больше времени, пока ScraperAPI будет обрабатывать все сложности за вас и будет расходовать кредиты API только на успешные запросы.

    Теперь ваша очередь. Веб-скрейпинг — это все о практике. Каждый сайт — это отдельная головоломка, поэтому нет единого способа сделать что-то одно. Вместо этого сосредоточьтесь на использовании фундаментальных знаний для решения более сложных задач.

    Если вы хотите продолжать практиковаться, мы рекомендуем вам несколько сайтов:

    • https://quotes.toscrape.com/
    • https://books.toscrape.com/
    • https://datatables.net/examples/index

    До следующего раза, счастливого скрапбукинга!

Оцените статью
devanswers.ru
Добавить комментарий