Excel по-прежнему остаётся рабочей средой, где происходит большая часть реального анализа. Как только набор данных попадает в таблицу, его можно сортировать, фильтровать, строить графики и передать коллеге, которому никогда не нужно открывать терминал. Загвоздка состоит в том, чтобы с самого начала получить чистые, структурированные данные в таблицу, а копирование строк с веб-страницы вручную, это медленно, чревато ошибками и невозможно повторить по расписанию.
Это руководство показывает, как парсить данные с сайта в Excel с помощью Python. Вы создадите небольшой рабочий скрипт, который загружает отрисованную страницу через Crawling API, парсит нужные записи с помощью BeautifulSoup и записывает чистый файл .xlsx с заголовочной строкой, одной записью в строке и базовым форматированием с помощью openpyxl. Пошаговое руководство построено на нейтральном примере и публичных данных, поэтому вы можете выполнить каждый шаг от начала до конца, прежде чем направить скрипт на собственный целевой сайт.
Что вы создадите
Скрипт на Python, который загружает публичную страницу-каталог через Crawling API, извлекает структурированную запись для каждого элемента и сохраняет весь набор в отформатированную книгу Excel. В качестве рабочего примера используется универсальная публичная страница-каталог по ссылке-заглушке, поэтому после того, как поток заработает, подставьте собственную цель. Извлекаются следующие поля:
- Title (название) имя или заголовок каждого элемента.
- Price (цена) указанная цена, как она показана на странице.
- Availability (наличие) метка статуса наличия на складе.
- Rating (рейтинг) оценка отзывов, когда страница её предоставляет.
- Link (ссылка) канонический URL собственной страницы элемента.
Результат, одна таблица с этими столбцами, жирной закреплённой строкой заголовка и автоподбором ширины столбцов, готовая к сортировке, фильтрации и построению диаграмм в Excel или Google Sheets.
Почему обычный запрос часто не работает
Старый способ получить данные из интернета в Excel, встроенный запрос Из Интернета: вставьте URL, дайте Excel выгрузить таблицы и обновляйте по таймеру. Он по-прежнему существует, но ломается на большинстве современных сайтов по двум причинам, и те же две причины подводят и наивный Python-запрос.
Во-первых, многие страницы отрисовывают содержимое в браузере с помощью JavaScript. Исходный HTML, который получает обычный HTTP-клиент, это тонкая оболочка, а цены, заголовки и метки наличия появляются только после выполнения скриптов страницы. Извлеките данные из этого первого ответа, и вы получите пустой шаблон вместо реальных строк. Во-вторых, сайты следят за автоматическим трафиком. IP-адреса дата-центров и паттерны запросов, не похожие на реальный браузер, подвергаются ограничению скорости, блокировке по IP или показу CAPTCHA ещё до того, как достигнут содержимого, именно это происходит, когда автоматическое обновление Excel слишком часто обращается к сайту с одного адреса.
Таким образом, надёжный скрапер нуждается в двух вещах в рамках одного запроса: браузере, который действительно отрисовывает страницу, и IP-адресе, который сайт воспринимает как реального посетителя. Это можно собрать самостоятельно с помощью headless-браузера и пула ротируемых жилых прокси, но поддержание этого стека в рабочем состоянии само по себе является основной задачей. Crawling API объединяет оба компонента в одном вызове: вы отправляете ему URL, он отрисовывает страницу за доверенным IP и возвращает готовый HTML для парсинга.
Crawlbase предоставляет два типа токенов. Обычный токен загружает статический HTML и подходит, когда данные уже есть в исходнике страницы. JavaScript (JS) токен сначала отрисовывает страницу в реальном браузере, это нужно, когда контент загружается на стороне клиента. Начните с обычного токена; переключитесь на JS-токен только если поля возвращаются пустыми.
Предварительные требования
Перед написанием кода необходимо подготовить несколько вещей. Ни одна из них не займёт много времени.
Базовые знания Python. Вы должны уметь писать и запускать скрипты, а также устанавливать пакеты с помощью pip. Если парсинг для вас в новинку, руководство по BeautifulSoup, хороший спутник, а обзор парсинга с Python охватывает основы.
Python 3.8 или выше. Проверьте версию командой python --version. Если Python не установлен, скачайте его с python.org или через дистрибутив наподобие Anaconda и убедитесь, что Python добавлен в PATH.
Аккаунт Crawlbase и токен. Зарегистрируйтесь, откройте панель управления и скопируйте токен со страницы документации аккаунта. Crawlbase включает до 20 000 бесплатных запросов для старта, чего вполне достаточно для прохождения этого руководства, и вы платите только за успешные запросы. Относитесь к токену как к паролю и не добавляйте его в систему контроля версий.
Настройка проекта
Создайте виртуальное окружение, чтобы зависимости проекта оставались изолированными, затем установите три необходимые библиотеки.
python --version python -m venv excel_env source excel_env/bin/activate pip install crawlbase beautifulsoup4 openpyxl
В Windows активируйте окружение командой excel_env\Scripts\activate вместо строки с source. Три зависимости выполняют основную работу: crawlbase, официальный клиент для Crawling API, beautifulsoup4 парсит возвращаемый HTML, позволяя извлекать отдельные поля по CSS-селектору, а openpyxl записывает файл .xlsx с форматированием. Если вы предпочитаете работать с DataFrame, pandas тоже умеет писать в Excel; короткая заметка об этом есть ближе к концу.
Шаг 1: Загрузка страницы через Crawlbase
Начните с получения готовой страницы. Импортируйте класс CrawlingAPI, инициализируйте его с вашим токеном и запросите целевой URL. Проверка Crawlbase cb_status (legacy pc_status) перед парсингом позволяет явно сообщать об ошибках, а не замалчивать их: так вы никогда не попытаетесь парсить страницу с ошибкой.
from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) def crawl(page_url): response = api.get(page_url) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['cb_status']}") return None if __name__ == "__main__": catalog_url = "https://example.com/products" html = crawl(catalog_url) print(html[:500] if html else "No HTML returned")
Запустите скрипт командой python excel_scraper.py, и вы должны увидеть первые 500 символов реальной разметки страницы, что подтверждает работу запроса до написания первого селектора. Если страница отрисовывает данные на стороне клиента, тело будет похоже на пустой шаблон; в этом случае добавьте JS-токен, передав {"token": "YOUR_CRAWLBASE_TOKEN", "javascript": "true"} при создании клиента, и API сначала отрисует страницу в браузере. Для нейтрального примера каталога, используемого здесь, достаточно обычного токена.
Шаг с загрузкой, это именно та часть, на которой тихо ломается встроенный запрос Excel Из Интернета при любом объёме: один IP, обновляющий сайт, подвергается ограничению скорости, а страницы на стороне клиента возвращают пустую оболочку. Crawling API принимает ваш токен, отрисовывает страницу по мере необходимости и ротирует жилые IP-адреса на серверной стороне, затем передаёт вам готовый HTML, избавляя от необходимости самостоятельно запускать флот headless-браузеров и пул прокси. Сначала попробуйте его на публичной странице в бесплатном тарифе.
Шаг 2: Парсинг нужных записей
Страница-каталог, это сетка карточек товаров, каждая с названием, ценой, меткой наличия и ссылкой. Загрузите отрисованный HTML в BeautifulSoup, выберите каждую карточку и считайте поля из каждой. Защитный поиск для каждого поля означает, что карточка, в которой отсутствует поле, возвращает None вместо прерывания цикла.
from bs4 import BeautifulSoup def text_of(card, selector): el = card.select_one(selector) return el.get_text(strip=True) if el else None def parse_records(html, base_url): soup = BeautifulSoup(html, "html.parser") records = [] for card in soup.select("article.product"): link_el = card.select_one("a.product-link") href = link_el["href"] if link_el else None records.append({ "title": text_of(card, "h2.product-title"), "price": text_of(card, "span.price"), "availability": text_of(card, "span.stock"), "rating": text_of(card, "span.rating"), "link": urljoin(base_url, href) if href else None, }) return records
Вспомогательная функция text_of запрашивает один элемент внутри карточки и возвращает его текст без пробелов или None, если элемент отсутствует. Вызов select("article.product") возвращает каждую карточку товара, и цикл считывает из неё название, цену, наличие и рейтинг. urljoin превращает относительный href в полный URL, чтобы столбец со ссылками был самодостаточным. Селекторы здесь (article.product, h2.product-title, span.price) являются иллюстративными; для реального целевого сайта проверьте страницу в инструментах разработчика браузера и замените их на актуальные имена классов.
Сгенерированные имена классов и разметка макета на производственных сайтах меняются без предупреждения. Относитесь к любому селектору как к стартовому шаблону, а не как к гарантии. Когда записи возвращаются пустыми, заново проверьте живую страницу в инструментах разработчика и обновите селектор. Периодическое обслуживание селекторов является нормой для любого скрапера, а не признаком неисправности.
Шаг 3: Запись записей в Excel с помощью openpyxl
Теперь превратите список записей в таблицу. openpyxl строит книгу в памяти: вы создаёте лист, пишете жирную заголовочную строку, добавляете по одной строке на запись, закрепляете заголовок, подбираете ширину столбцов по содержимому и сохраняете. Функция ниже принимает записи и имя файла и делает всё это.
from openpyxl import Workbook from openpyxl.styles import Font from openpyxl.utils import get_column_letter HEADERS = ["Title", "Price", "Availability", "Rating", "Link"] KEYS = ["title", "price", "availability", "rating", "link"] def save_to_excel(records, filename="products.xlsx"): wb = Workbook() ws = wb.active ws.title = "Products" ws.append(HEADERS) for cell in ws[1]: cell.font = Font(bold=True) for record in records: ws.append([record.get(key) for key in KEYS]) ws.freeze_panes = "A2" for i, header in enumerate(HEADERS, start=1): column = ws[get_column_letter(i)] width = max(len(str(c.value)) for c in column if c.value) ws.column_dimensions[get_column_letter(i)].width = width + 2 wb.save(filename) print(f"Saved {len(records)} rows to {filename}")
Несколько деталей делают таблицу удобной в использовании. ws.append(HEADERS) записывает заголовки столбцов, а цикл по ws[1] делает эту первую строку жирной. freeze_panes = "A2" закрепляет заголовок, чтобы он оставался видимым при прокрутке. Цикл по ширине столбцов измеряет самое длинное значение в каждом столбце и устанавливает ширину по нему с небольшим отступом, чтобы ничего не обрезалось. Чтение значений через record.get(key) означает, что запись с отсутствующим полем записывает пустую ячейку, а не вызывает ошибку. В результате получается аккуратная книга, которую можно открыть в Excel, Numbers или Google Sheets.
Шаг 4: Сборка полного скрипта
Теперь соедините три части в один рабочий файл: загрузите страницу, распарсите записи и сохраните таблицу.
from urllib.parse import urljoin from crawlbase import CrawlingAPI from bs4 import BeautifulSoup from openpyxl import Workbook from openpyxl.styles import Font from openpyxl.utils import get_column_letter api = CrawlingAPI({"token": "YOUR_CRAWLBASE_TOKEN"}) HEADERS = ["Title", "Price", "Availability", "Rating", "Link"] KEYS = ["title", "price", "availability", "rating", "link"] def crawl(page_url): response = api.get(page_url) if response["headers"]["cb_status"] == "200": return response["body"].decode("utf-8") print(f"Request failed: {response['headers']['cb_status']}") return None def text_of(card, selector): el = card.select_one(selector) return el.get_text(strip=True) if el else None def parse_records(html, base_url): soup = BeautifulSoup(html, "html.parser") records = [] for card in soup.select("article.product"): link_el = card.select_one("a.product-link") href = link_el["href"] if link_el else None records.append({ "title": text_of(card, "h2.product-title"), "price": text_of(card, "span.price"), "availability": text_of(card, "span.stock"), "rating": text_of(card, "span.rating"), "link": urljoin(base_url, href) if href else None, }) return records def save_to_excel(records, filename="products.xlsx"): wb = Workbook() ws = wb.active ws.title = "Products" ws.append(HEADERS) for cell in ws[1]: cell.font = Font(bold=True) for record in records: ws.append([record.get(key) for key in KEYS]) ws.freeze_panes = "A2" for i, header in enumerate(HEADERS, start=1): column = ws[get_column_letter(i)] width = max(len(str(c.value)) for c in column if c.value) ws.column_dimensions[get_column_letter(i)].width = width + 2 wb.save(filename) print(f"Saved {len(records)} rows to {filename}") def main(): catalog_url = "https://example.com/products" html = crawl(catalog_url) if not html: return records = parse_records(html, catalog_url) save_to_excel(records) if __name__ == "__main__": main()
Функция main загружает страницу каталога, парсит её в список записей и сохраняет таблицу. Ранний выход при неудаче загрузки предотвращает запись пустого файла при плохом ответе. Замените catalog_url и селекторы в parse_records под собственную цель, и остальная часть скрипта останется неизменной.
Как выглядит результат
Запустите полный скрипт командой python excel_scraper.py, и вы получите файл products.xlsx с жирной строкой заголовка, одной строкой на элемент и столбцами, подобранными по содержимому. Концептуально данные выглядят так:
Title,Price,Availability,Rating,Link Wireless Mouse,$24.99,In stock,4.5,https://example.com/products/wireless-mouse Mechanical Keyboard,$79.00,In stock,4.7,https://example.com/products/mechanical-keyboard USB-C Hub,$39.50,Out of stock,4.2,https://example.com/products/usb-c-hub Laptop Stand,$32.00,In stock,4.6,https://example.com/products/laptop-stand
Таблица содержит те же пять столбцов с закреплённым заголовком, поэтому вы можете сортировать по цене, фильтровать по наличию или строить диаграммы рейтингов без какой-либо дополнительной очистки. Отсюда данные представляют собой обычное табличное содержимое, которое также переносится в другие форматы; об отличиях между форматами, удобными для таблиц и для кода, читайте в сравнении JSON и CSV.
Масштабирование на несколько страниц
Одна страница каталога, это обычно фрагмент более крупного набора. Большинство листингов разбиты на страницы с параметром запроса или сегментом пути, поэтому вы загружаете каждую страницу, парсите её и собираете записи в один список перед записью книги. Короткая пауза между запросами позволяет запуску быть вежливым.
import time def collect_all(base_url, max_pages): all_records = [] for page in range(1, max_pages + 1): page_url = f"{base_url}?page={page}" html = crawl(page_url) if not html: break records = parse_records(html, page_url) if not records: break all_records.extend(records) time.sleep(2) return all_records
Замените одиночный вызов crawl в main на collect_all(catalog_url, max_pages=5), затем передайте объединённый список в save_to_excel. Цикл останавливается раньше, когда страница возвращает пустой результат или не содержит записей, поэтому не нужно заранее знать точное количество страниц. time.sleep(2) между запросами регулирует скорость запуска. Для значительно больших заданий асинхронный Crawler ставит запросы в очередь и доставляет результаты на вебхук, что удобно при сборе данных со множества страниц без удержания открытых соединений.
Если вы уже работаете с DataFrame, можно пропустить ручное построение листа в openpyxl: pandas.DataFrame(records).to_excel("products.xlsx", index=False) записывает те же столбцы в одну строку (под капотом используется openpyxl). Вы теряете пошаговое форматирование из шага 3, но получаете всё, что pandas предлагает для очистки и анализа. Читайте далее в руководстве по анализу с pandas.
Ответственный парсинг
Загрузить данные в таблицу, это простая часть; собирать их ответственно, вот что делает ваш проект защищённым. Придерживайтесь нескольких правил вне зависимости от цели. Парсите только публичные данные, которые любой посетитель может видеть без входа в систему, и не трогайте ничего, требующего аутентификации. Читайте условия использования сайта и его robots.txt и относитесь к обоим как к границе того, что вы собираете. Поддерживайте разумную скорость запросов, чтобы не перегружать серверы сайта; короткие паузы в цикле пагинации выше, это разумный минимум, а не потолок.
Когда данные касаются людей, планка выше. Персональные данные регулируются такими системами, как GDPR и CCPA, поэтому избегайте сбора имён, контактных данных или чего-либо, привязанного к идентифицируемым лицам, если у вас нет чёткого правового основания и реальной необходимости. Для большинства аналитических задач этого нет, и каталог публичных полей продукта, как в этом руководстве, явно не переступает эту черту. Используемый таким образом, парсинг в Excel является рутинным и малорисковым способом поддерживать набор данных в актуальном состоянии.
Ключевые выводы
- Встроенный запрос Excel «Из Интернета» ломается при масштабировании. Один IP, обновляющий сайт, подвергается ограничению скорости, а страницы на стороне клиента возвращают пустую оболочку, именно поэтому скрапер на основе кода надёжнее.
-
Загружайте через Crawling API. Один вызов при необходимости отрисовывает страницу и ротирует IP на серверной стороне, возвращая готовый HTML; проверяйте
cb_statusперед парсингом, чтобы сбои были явными. -
Парсите защитно. Выбирайте каждую карточку, считывайте поля с защитным помощником, возвращающим
Noneпри промахе, и ожидайте обновления селекторов при изменении разметки сайта. -
Записывайте чистую книгу с помощью openpyxl. Жирный закреплённый заголовок, одна строка на запись и столбцы с автоподбором ширины дают таблицу, готовую к сортировке, фильтрации или построению диаграмм; или используйте
to_excelот pandas для однострочного экспорта. - Парсите ответственно. Оставайтесь на публичных данных, уважайте условия использования и robots.txt, регулируйте скорость запросов и применяйте меры осторожности в соответствии с GDPR или CCPA при работе с персональными данными.
Часто задаваемые вопросы
Можно ли парсить сайт прямо в Excel без написания кода?
Встроенный запрос Excel «Из Интернета» (в разделе «Данные» ленты) может загружать таблицы по URL без кода и работает для простых статичных страниц с табличной структурой. Он плохо справляется с современными сайтами, отрисовывающими контент с помощью JavaScript, а запланированное автоматическое обновление с одного IP склонно к ограничению скорости или блокировкам. Для чего-либо сложнее простых статичных таблиц небольшой Python-скрипт, загружающий через Crawling API и записывающий книгу, значительно надёжнее.
Что использовать для записи файла Excel: openpyxl или pandas?
Оба работают, и pandas использует openpyxl под капотом. Используйте openpyxl напрямую, когда вам нужен контроль над форматированием: жирные заголовки, закреплённые области и ширина столбцов, как показано в шаге 3. Используйте pandas.to_excel, когда вы уже работаете с DataFrame и хотите однострочный экспорт; вы теряете ручное форматирование, но получаете всё, что pandas предлагает для очистки и анализа.
Какой токен нужен: обычный или JS?
Начните с обычного токена. Он загружает статический HTML и достаточен, когда данные уже есть в исходнике страницы, что справедливо для многих страниц каталогов и листингов. Переключитесь на JS-токен только когда поля возвращаются пустыми, это значит, что страница отрисовывает контент на стороне клиента и нуждается в реальном браузере. JS-токен сначала отрисовывает страницу, затем возвращает готовый HTML.
Распарсенные записи пустые. Что пошло не так?
Обычно одна из двух причин. Либо селекторы не соответствуют живой странице, в этом случае заново проверьте её в инструментах разработчика браузера и обновите, либо страница отрисовывается на стороне клиента и обычный токен вернул пустую оболочку, тогда переключитесь на JS-токен. Выведите первые несколько сотен символов HTML, чтобы понять, с каким случаем вы имеете дело, прежде чем что-либо менять.
Как парсить несколько страниц результатов в одну таблицу?
Переберите страницы, распарсите каждую и соберите записи в один список перед записью книги. Функция collect_all в разделе масштабирования показывает паттерн: она обходит страницы до тех пор, пока одна не вернёт пустой результат, делая короткую паузу между запросами, а затем вы один раз передаёте объединённый список в save_to_excel.
Законно ли парсить данные в Excel?
Парсинг публичных данных для собственного анализа, как правило, допустим, но зависит от условий использования сайта, вашей юрисдикции и того, что вы делаете с данными. Оставайтесь на публичных страницах, уважайте условия сайта и robots.txt, поддерживайте разумную скорость запросов и применяйте меры осторожности в соответствии с GDPR или CCPA, когда данные касаются идентифицируемых лиц. Для массового или коммерческого использования проверьте, предлагает ли сайт официальный API или лицензию на данные.
Обходите любой сайт в масштабе, без борьбы с инфраструктурой.
Crawlbase берёт на себя прокси, отпечатки и CAPTCHA, чтобы ваша команда выпускала конвейеры данных вместо поддержки обвязки краулинга. 1 000 запросов бесплатно, без карты.
