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

В этом руководстве показаны три способа парсинга таблиц с веб-сайтов, каждый из которых подходит для разного уровня навыков и рабочего процесса: формула Google Sheets без кода, несколько строк Python с pandas и R-скрипт с использованием rvest. Рабочим примером служит публичная таблица Wikipedia: данные открыты, а разметка чистая. Каждый метод здесь ограничен публичными данными, и короткий раздел об ответственном парсинге в конце стоит прочитать, прежде чем применять это к реальным источникам.

Что вы создадите

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

  • Формула Google Sheets, импортирующая живую таблицу одним вызовом IMPORTHTML без написания кода.
  • Python-скрипт, читающий таблицы с помощью pandas.read_html в одну строку, с запасным вариантом BeautifulSoup для неряшливой разметки.
  • R-скрипт, извлекающий таблицы функцией html_table из rvest в датафрейм.
  • Чистый табличный вывод из каждого метода, готовый к экспорту в CSV или для анализа.

Почему некоторые таблицы требуют большего, чем обычный запрос

Для чистой серверно-рендеримой страницы, такой как большинство страниц Wikipedia, все три метода ниже работают напрямую: разметка таблицы уже присутствует в HTML, поэтому формула или однострочная операция чтения извлекают её сразу. Это простой и распространённый случай, с которого и стоит начинать.

Проблемы возникают с источниками, которые не отдают таблицу так легко. Чаще всего встречаются две проблемы. Во-первых, некоторые сайты создают таблицы с помощью JavaScript после загрузки страницы, поэтому в исходном HTML, который вы получаете, отсутствуют нужные строки. Во-вторых, многие сайты отслеживают трафик, характерный для скраперов, и ограничивают или блокируют запросы с IP датацентров после нескольких первых обращений. Когда любое из этих явлений происходит, IMPORTHTML возвращает ошибку, а обычный requests.get в Python вместо таблицы возвращает страницу блокировки или пустую разметку.

Решение для обоих случаев одинаково: загружать страницу через браузер, выполняющий скрипты, с IP, который сайт воспринимает как реального посетителя. Можно собрать такое решение самостоятельно с помощью ротируемого пула IP и headless-браузера, но это и составляет большую часть работы. В разделе Python мы используем Crawling API именно для таких заблокированных или рендерируемых JavaScript источников, сохраняя обычный путь для всего, что в нём не нуждается.

Предварительные требования

Необходимое зависит от выбранного метода. Ни один из них не требует долгой настройки.

Для Google Sheets: достаточно аккаунта Google и браузера. Устанавливать ничего не нужно.

Для Python: Python 3.8 или выше. Проверьте версию командой python --version. Вы должны уметь запускать скрипты и устанавливать пакеты с помощью pip. Если разбор HTML для вас нов, наш гайд по использованию BeautifulSoup в Python охватывает основы, которые предполагает этот раздел.

Для R: актуальная установка R (версия 4.0 или выше подойдёт) и возможность устанавливать пакеты из CRAN.

Для заблокированных или рендерируемых JavaScript источников: аккаунт и токен Crawlbase. Зарегистрируйтесь, откройте панель управления и скопируйте обычный токен. Храните токен как пароль и не добавляйте его в систему контроля версий. Это нужно только для пути Python, когда обычный запрос не срабатывает.

Метод 1: Google Sheets с IMPORTHTML

Самый быстрый способ получить таблицу с публичной страницы вообще не требует кода. В Google Sheets есть встроенная функция IMPORTHTML, которая загружает страницу, находит таблицу или список по индексу и помещает их в таблицу как живые ячейки. Она даже обновляется автоматически, так что данные остаются актуальными.

Откройте новую таблицу, кликните по пустой ячейке и введите формулу. Она принимает три аргумента: URL страницы, тип объекта ("table" или "list") и индекс нужного объекта (первая таблица на странице, 1, вторая, 2 и т. д.).

html
=IMPORTHTML("https://en.wikipedia.org/wiki/List_of_largest_cities", "table", 1)

Нажмите Enter, и Sheets загрузит страницу, извлечёт первую таблицу и разместит её по ячейкам, начиная с места ввода формулы. Если первая таблица не та, что нужна, увеличивайте индекс, пока не найдёте нужную; часто перед основными данными стоят навигационные или информационные таблицы. После того как данные появятся, можно изменить ширину столбцов или отформатировать ячейки, а формулу можно оставить, чтобы таблица обновлялась при изменении источника.

When IMPORTHTML returns an error

IMPORTHTML загружает исходный HTML и не выполняет JavaScript, поэтому видит только таблицы, присутствующие в исходном коде страницы. Если функция возвращает #N/A или пустой результат, таблица, вероятно, создаётся JavaScript после загрузки или сайт заблокировал запрос. Именно для таких случаев предназначен метод Python с Crawling API, поскольку он может рендерить страницу и сначала загрузить её с доверенного IP.

Crawlbase Crawling API

Формула IMPORTHTML работает, потому что Wikipedia отдаёт таблицы в виде обычного HTML из открытого источника. Как только таблица создаётся JavaScript или защищена от ботов, формула возвращает #N/A, и то же происходит с обычным Python-запросом. Crawling API ротирует резидентские IP на стороне сервера, при необходимости рендерит страницу в реальном браузере и возвращает готовый HTML, избавляя вас от необходимости поддерживать флот headless-браузеров и пул прокси. Начните с публичной страницы на бесплатном уровне.

Метод 2: Python с pandas и Crawling API

Python, основной инструмент для этого. Для чистой публичной таблицы pandas.read_html делает почти всё в одну строку: передайте URL или фрагмент HTML, и он вернёт список всех таблиц на странице в виде датафреймов. Начните с установки библиотек.

bash
python -m venv tables_env
source tables_env/bin/activate

pip install pandas lxml beautifulsoup4 crawlbase

В Windows активируйте окружение командой tables_env\Scripts\activate вместо строки с source. pandas читает таблицы, lxml, используемый под капотом парсер, beautifulsoup4, запасной вариант для неряшливой разметки, а crawlbase, официальный клиент для Crawling API.

Для публичной серверно-рендеримой страницы простейший вариант, действительно один вызов. Передайте URL в read_html и индексируйте в возвращённый список.

python
import pandas as pd

url = "https://en.wikipedia.org/wiki/List_of_largest_cities"

# read_html returns a list of every table it finds on the page
tables = pd.read_html(url)
print(f"Found {len(tables)} tables")

# pick the one you want by index, then work with it as a DataFrame
df = tables[0]
print(df.head())
df.to_csv("cities.csv", index=False)

Это вся работа, когда страница сотрудничает: read_html сканирует HTML, строит датафрейм для каждого тега <table>, и вы оставляете нужный и записываете его в CSV. Как и при индексировании в Sheets, может потребоваться проверить несколько индексов, чтобы найти основную таблицу, а не боковую или навигационную.

Когда источник блокирует обычные запросы или создаёт таблицу с помощью JavaScript, сначала загрузите HTML через Crawling API, затем передайте его в read_html. Единственное изменение, источник разметки.

python
import pandas as pd
from crawlbase import CrawlingAPI

api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"})

def fetch_html(url, render=False):
    options = {"country": "US"}
    if render:
        # use the JS token for JavaScript-built tables
        options["ajax_wait"] = "true"
    response = api.get(url, options)
    if response["status_code"] == 200:
        return response["body"].decode("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

url = "https://en.wikipedia.org/wiki/List_of_largest_cities"
html = fetch_html(url)

if html:
    tables = pd.read_html(html)
    df = tables[0]
    print(df.head())
    df.to_csv("cities.csv", index=False)

Параметр country привязывает запрос к выходному IP в США, а передача render=True указывает вызову использовать JavaScript-токен (здесь через ajax_wait), чтобы страница рендерилась перед получением HTML. Получив разметку, read_html работает точно так же, как раньше. Это версия для тех случаев, когда обычный путь возвращает страницу блокировки или пустой список таблиц.

Запасной вариант с BeautifulSoup для неряшливых таблиц

Иногда read_html не справляется: таблица с объединёнными ячейками, отсутствующим тегом <table> или строками, разбросанными по вложенной разметке. В таком случае переходите на BeautifulSoup и формируйте строки самостоятельно, читая каждую ячейку. Это тот же подход по полям, что и в нашем гайде по CSS-селекторам, применённый к ячейкам таблицы.

python
import pandas as pd
from bs4 import BeautifulSoup

def table_to_rows(html, index=0):
    soup = BeautifulSoup(html, "html.parser")
    table = soup.find_all("table")[index]
    rows = []
    for tr in table.find_all("tr"):
        cells = tr.find_all(["th", "td"])
        row = [cell.get_text(strip=True) for cell in cells]
        if row:
            rows.append(row)
    return rows

rows = table_to_rows(html)
df = pd.DataFrame(rows[1:], columns=rows[0])
print(df.head())

Вспомогательная функция перебирает каждый тег <tr>, читает каждый <th> и <td> как очищенный текст и пропускает пустые строки. Использование первой строки как заголовка и остальных как данных даёт чистый датафрейм, даже когда read_html не смог разобрать таблицу самостоятельно. Передайте тот же HTML от Crawling API в эту функцию, если неряшливая таблица также оказалась заблокированной или рендеримой.

Метод 3: R с rvest

Если ваш анализ уже ведётся в R, незачем его покидать. Пакет rvest читает HTML, а его функция html_table превращает каждую таблицу на странице в датафрейм, почти как pandas. Установите пакет один раз из консоли R.

r
install.packages("rvest")

Загрузив rvest, прочитайте страницу, извлеките таблицы и выберите нужную. Паттерн повторяет Python: читаем HTML, получаем список таблиц, индексируем в него.

r
library(rvest)

url <- "https://en.wikipedia.org/wiki/List_of_largest_cities"

# read the page, then pull every table into a list of data frames
page <- read_html(url)
tables <- page %>% html_elements("table") %>% html_table()

# keep the first table and inspect it
df <- tables[[1]]
head(df)

# write it out for analysis elsewhere
write.csv(df, "cities.csv", row.names = FALSE)

R использует двойные скобки, tables[[1]], для извлечения одного элемента из списка, тогда как Python использует одинарные. В остальном процесс идентичен: читаем страницу, собираем таблицы, оставляем нужную и экспортируем. Дальше датафрейм ведёт себя как любой другой в R, готовый для dplyr, ggplot2 или экспорта в CSV. Если источник блокирует прямое чтение, загрузите отрендеренный HTML через Crawling API, как в разделе Python, и передайте эту строку в read_html вместо URL.

Как выглядит результат

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

csv
Rank,City,Country,Population,Year
1,Tokyo,Japan,37468000,2018
2,Delhi,India,28514000,2018
3,Shanghai,China,25582000,2018
4,Sao Paulo,Brazil,21650000,2018
5,Mexico City,Mexico,21581000,2018

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

Ответственный парсинг таблиц

Описанные выше методы просты в запуске, поэтому стоит осознанно подходить к тому, на что их направлять. Перед сбором данных в больших объёмах проверяйте условия обслуживания каждого источника и его файл robots.txt, предпочитайте по-настоящему публичные данные, такие, что посетитель видит без входа в систему, а не скрытые за аккаунтом или платным доступом. Делайте паузы между запросами, чтобы не перегружать сервер в плотном цикле, и используйте официальный API или опубликованный набор данных, когда источник предлагает таковой, поскольку это почти всегда более лёгкий и надёжный путь.

Если вы публикуете таблицу, полученную парсингом, указывайте источник и соблюдайте его лицензию. Открытые наборы данных, такие как Wikipedia, имеют чёткие условия повторного использования (атрибуция по лицензии Creative Commons в данном случае), и многие другие сайты вообще не разрешают распространение. Сбор публичных данных для собственного анализа, значительно более лёгкое использование, чем публикация чужих скомпилированных данных как своих, поэтому при сомнениях дайте ссылку на источник вместо полного копирования.

Итоги

Ключевые выводы

  • Выбирайте инструмент, подходящий для вашего рабочего процесса. Google Sheets IMPORTHTML для работы без кода, pandas read_html для Python и rvest html_table для R, все они извлекают публичную таблицу в одну-две строки.
  • Индексируйтесь в список таблиц. Все три метода возвращают все таблицы на странице, поэтому попробуйте несколько индексов, чтобы попасть на основные данные, а не боковую или навигационную таблицу.
  • Переходите на BeautifulSoup при неряшливой разметке. Объединённые ячейки или нестандартные таблицы, ломающие read_html, можно восстановить строка за строкой из тегов <tr>, <th> и <td>.
  • Используйте Crawling API для заблокированных или JavaScript-таблиц. Когда формула или обычный запрос возвращает пустой результат, загрузите отрендеренный HTML за доверенным IP, затем разберите его тем же кодом.
  • Парсите ответственно. Соблюдайте условия обслуживания и robots.txt каждого источника, предпочитайте публичные данные и официальные API, и указывайте авторство или лицензию при публикации данных.

Часто задаваемые вопросы

Какой самый простой способ получить таблицу с веб-сайта?

Для публичной серверно-рендеримой страницы проще всего Google Sheets. Введите =IMPORTHTML(url, "table", 1) в ячейку, и Sheets загрузит страницу и поместит первую таблицу в таблицу как живые ячейки без написания кода. Работает для любой страницы, чья таблица присутствует в исходном HTML, и обновляется автоматически.

Почему IMPORTHTML возвращает #N/A или пустой результат?

IMPORTHTML читает исходный HTML и не выполняет JavaScript, поэтому не видит таблицы, которые страница создаёт с помощью скриптов после загрузки. Функция также не работает, если сайт блокирует запрос. В обоих случаях переключитесь на метод Python и загрузите страницу через Crawling API, который умеет рендерить страницу в реальном браузере и запрашивать её с доверенного IP перед разбором.

Как парсить таблицу с помощью Python?

Кратчайший путь: pandas.read_html(url) возвращает список датафреймов, по одному на каждую таблицу на странице. Индексируйтесь в список, чтобы оставить нужную, и экспортируйте через to_csv. Для источников, блокирующих обычные запросы или рендерящих таблицы с помощью JavaScript, сначала загрузите HTML через Crawling API и передайте эту разметку в read_html вместо URL.

Как парсить таблицу в R?

Установите rvest, прочитайте страницу через read_html, затем выполните html_elements("table") %>% html_table(), чтобы получить список таблиц в виде датафреймов. Выберите нужную с помощью двойных скобок, например tables[[1]], и экспортируйте через write.csv. Процесс почти полностью повторяет подход pandas.

Что делать, если pandas read_html не может разобрать таблицу?

Некоторые таблицы имеют объединённые ячейки, отсутствующие теги <table> или строки, вложенные в нестандартную разметку, с которой read_html не справляется. Прибегните к BeautifulSoup: пройдитесь по каждому тегу <tr>, читайте каждый <th> и <td> как текст и формируйте строки в датафрейм самостоятельно. Это даёт полный контроль над интерпретацией каждой ячейки.

Как парсить таблицы с сайта, который меня блокирует?

Сайты, ограничивающие или блокирующие автоматизированный трафик, требуют запроса, похожего на запрос реального посетителя. Маршрутизируйте загрузку через ротируемые резидентские IP, чтобы ни один адрес не превысил лимит, и рендерите JavaScript, когда таблица создаётся на стороне клиента. Crawling API обрабатывает оба случая в одном вызове; получив возвращённый HTML, разберите его с помощью pandas, BeautifulSoup или rvest точно так же, как любую другую страницу.

Начать создавать

Обходите любой сайт в масштабе, без борьбы с инфраструктурой.

Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.

Самообслуживание · Звонок отдела продаж не требуется · Доступны корпоративные объёмы краулинга