Excel 仍然是大量实际分析工作发生的场所。一旦数据集落入电子表格,你就可以排序、筛选、绘图,并传给同事,而对方无需接触任何终端。问题在于如何将干净的结构化数据导入表格:手动从网页复制行既缓慢、易出错,也无法按计划重复执行。
本指南将展示如何用 Python 将网站数据抓取到 Excel。你将构建一个小型、可运行的脚本,通过 Crawling API 获取渲染后的页面,用 BeautifulSoup 解析你所需的记录,并使用 openpyxl 写出一个带表头行、每行一条记录、基本格式的干净 .xlsx 文件。整个演示使用中性示例页面和公开数据,你可以端到端运行每个步骤,然后再将其指向自己的目标。
你将构建什么
一个 Python 脚本,通过 Crawling API 获取公开列表页,为每个条目提取结构化记录,并将整个数据集保存到格式化的 Excel 工作簿中。示例使用占位符 URL 上的通用公开目录页,待流程跑通后,替换为你自己的目标即可。我们提取以下字段:
- Title 每个条目的名称或标题。
- Price 页面上显示的标价。
- Availability 库存或状态标签。
- Rating 页面提供时的评论分数。
- Link 该条目自有页面的规范 URL。
输出是一个包含这些列的单张电子表格,带有粗体冻结表头行和自动调整列宽,可直接在 Excel 或 Google Sheets 中排序、筛选或绘图。
为什么普通请求经常失效
将网络数据导入 Excel 的旧方法是内置的从 Web 导入查询:粘贴 URL,让 Excel 拉取表格,并按计划刷新。这个功能仍然存在,但在大多数现代网站上会因为两个原因失效,而这两个原因同样会让简单的 Python 请求出问题。
首先,许多页面在浏览器中通过 JavaScript 渲染内容。普通 HTTP 客户端收到的初始 HTML 是一个薄壳,价格、标题和库存标签只有在页面脚本运行后才出现。从第一个响应中提取数据,你得到的是空模板而不是真实行。其次,网站会监测自动化流量。数据中心 IP 地址和不像真实浏览器的请求模式,在到达任何内容之前就会被限速、IP 封锁或显示 CAPTCHA,当 Excel 自动刷新过于频繁地请求同一网站时,正是这种情况。
因此,可靠的爬虫在单次请求中需要两样东西:真正能渲染页面的浏览器,以及网站认为是真实访客的 IP。你可以自己用无头浏览器加住宅代理池来实现,但维护这套方案本身就是大部分工作量。Crawling API 将两者合并为一次调用:你发送 URL,它在可信 IP 后渲染页面,返回供你解析的完整 HTML。
Crawlbase 提供两种 token 类型。普通 token 获取静态 HTML,当数据已在页面源码中时是正确选择。JavaScript(JS)token 会先在真实浏览器中渲染页面,当内容是客户端加载时才需要使用。先用普通 token;只有当字段返回为空时才切换到 JS token。
前提条件
在编写任何代码前,你需要准备好以下几项,都不费时。
基础 Python 知识。 你应当能够编写和运行脚本,并使用 pip 安装包。如果你对解析还不熟悉,BeautifulSoup 指南是很好的补充,更广泛的 Python 爬虫演示涵盖了基础知识。
Python 3.8 或更高版本。 通过 python --version 确认你的版本。如果尚未安装,请从 python.org 或 Anaconda 等发行版安装,并确保 Python 已加入系统 PATH。
Crawlbase 账号与 token。 注册账号,打开控制台,从账号文档页复制你的 token。Crawlbase 最多提供 20,000 次免费请求,完全够用于完成本指南,且只对成功请求收费。请像保护密码一样保管 token,切勿提交到版本控制。
搭建项目
创建虚拟环境以隔离项目依赖,然后安装脚本所需的三个库。
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 类,用你的 token 初始化,然后请求目标 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 个字符,这在编写任何选择器之前就确认了请求可用。如果页面是客户端渲染的,正文看起来像一个空模板;此时可以在创建客户端时传入 {"token": "YOUR_CRAWLBASE_TOKEN", "javascript": "true"} 来添加 JS token,API 就会先在浏览器中渲染它。对于这里的中性目录示例,普通 token 已经足够。
上面的 fetch 步骤正是 Excel 自己的从 Web 导入查询在任何规模下都会静默失败的环节:单个 IP 刷新网站会被限速,客户端渲染页面会返回空壳。Crawling API 接受你的 token,在需要时渲染页面,在服务端轮换住宅 IP,然后交付完整的 HTML,让你无需自己运行无头浏览器集群和代理池。先在免费层将其指向一个公开页面试试。
步骤 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:用 openpyxl 将记录写入 Excel
现在将记录列表转换为电子表格。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 vs 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
在 main 中用 collect_all(catalog_url, max_pages=5) 替换单次 crawl 调用,然后将合并后的列表传给 save_to_excel。当某页返回空内容或无记录时,循环会提前停止,因此无需提前知道确切页数。time.sleep(2) 为请求间隔提供节奏控制。对于大规模任务,异步 Crawler 将请求入队并通过 webhook 交付结果,适合在不占用开放连接的情况下爬取大量页面。
如果你已经在使用 DataFrame,可以跳过 openpyxl 的手动表格构建:pandas.DataFrame(records).to_excel("products.xlsx", index=False) 用一行代码写出相同的列(它在底层使用 openpyxl)。你会失去步骤 3 中的逐列格式设置,但获得 pandas 提供的一切清理和分析能力。后续内容请参见 pandas 分析指南。
负责任地进行爬取
将数据导入电子表格是容易的部分;负责任地采集才是让你的项目站得住脚的关键。无论面对什么目标,都要遵守几条规则。只爬取任何访客无需登录即可看到的公开数据,远离任何需要身份验证的内容。阅读网站的服务条款和 robots.txt,并将两者视为采集内容的边界。保持合理的请求速率,以免给网站服务器造成压力;上面分页循环中的短暂停顿是合理的下限,而非上限。
当数据涉及个人时,标准更高。个人数据受 GDPR 和 CCPA 等框架约束,因此除非有明确的法律依据和真实需要,请避免采集姓名、联系方式或任何与可识别个人相关的信息。对于大多数分析工作来说并不需要这些,而本指南中的公开产品字段目录完全在那条线以内。这样使用爬虫,将数据导入 Excel 是一种常规的、低风险的方式来保持数据集的时效性。
核心要点
- Excel 自带的从 Web 导入查询在规模上会失效。 单个 IP 刷新网站会被限速,客户端渲染页面会返回空壳,这就是代码驱动的爬虫更可靠的原因。
-
通过 Crawling API 获取页面。 一次调用在需要时渲染页面,在服务端轮换 IP,返回完整 HTML;在解析前检查
cb_status,确保失败信号清晰可见。 -
防御性地解析。 选择每张卡片,用缺失时返回
None的保护辅助函数读取字段,并在网站标记发生漂移时准备更新选择器。 -
用 openpyxl 写出干净的工作簿。 粗体冻结表头、每行一条记录、自动调整列宽,生成可直接排序、筛选或绘图的表格,或使用 pandas
to_excel一行搞定。 - 负责任地爬取。 只处理公开数据,遵守服务条款和 robots.txt,控制请求节奏,涉及个人数据时遵守 GDPR 或 CCPA 规定。
常见问题
不写代码能直接把网站数据抓取到 Excel 吗?
Excel 内置的从 Web 导入查询(在"数据"功能区下)可以无需代码从 URL 拉取表格,对简单的静态表格页面有效。但在使用 JavaScript 渲染内容的现代网站上表现不佳,而且从单个 IP 定时自动刷新容易被限速或封锁。对于任何超出简单静态表格的情况,通过 Crawling API 获取并写入工作簿的小型 Python 脚本要可靠得多。
应该用 openpyxl 还是 pandas 写 Excel 文件?
两者都可以,pandas 在底层使用 openpyxl。当你需要控制格式(如粗体表头、冻结窗格、列宽)时,直接使用 openpyxl,如步骤 3 所示。当你已经在使用 DataFrame 并希望一行导出时,使用 pandas.to_excel;你放弃了手动格式设置,但获得了 pandas 在清理和分析方面的一切能力。
我需要普通 token 还是 JS token?
先用普通 token。它获取静态 HTML,只要数据已在页面源码中就够用,这覆盖了许多目录和列表页。只有当字段返回为空时才切换到 JS token,这意味着页面是客户端渲染的,需要真实浏览器。JS token 会先渲染页面,再返回完整的 HTML。
解析记录为空,哪里出了问题?
通常是两种情况之一。要么选择器与实时页面不匹配,此时需要在浏览器开发者工具中重新检查并更新;要么页面是客户端渲染的,普通 token 返回了空壳,此时需要切换到 JS token。先打印 HTML 的前几百个字符,看属于哪种情况,再做其他更改。
如何将多页结果抓取到同一个电子表格中?
循环遍历各页,解析每页,并在写入工作簿之前将记录收集到单个列表中。扩展章节中的 collect_all 函数展示了这一模式:它遍历各页直到某页不返回任何记录,请求之间有短暂停顿,然后将合并后的列表一次性传给 save_to_excel。
将数据抓取到 Excel 是否合法?
抓取公开数据用于自己的分析通常是可以的,但取决于网站的服务条款、你所在的司法管辖区以及数据用途。只访问公开页面,遵守网站条款和 robots.txt,保持合理的请求速率,涉及可识别个人的数据时遵守 GDPR 或 CCPA 规定。对于批量或商业使用,请确认网站是否提供官方 API 或数据许可。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
