CoinMarketCap 是加密货币市场数据的默认参考平台:它实时追踪数千种币种的价格、市值、交易量和排名,数据近乎实时更新。这张公开的表格正是价格看板、投资组合追踪工具和市场研究的数据来源,这也是为什么如此多人希望获得一份干净的结构化副本。
本教程将带你用 Python 抓取 CoinMarketCap 的加密货币价格。你将构建一个小型、可运行的爬虫,通过 Crawling API 获取已渲染的列表页面,用 BeautifulSoup 解析所需字段,并为每种币种写入一条干净的记录。整个教程仅涵盖公开市场数据:币种名称、代号、价格、市值、24小时涨跌幅和交易量。接近末尾的合法性章节不是套话,CoinMarketCap 也提供官方 API,请在将本代码用于实际流量之前两者都阅读一遍。
你将构建什么
一个 Python 脚本,获取 CoinMarketCap 公开首页,通过 Crawling API 取回已渲染的 HTML,并从表格中提取排名靠前的币种结构化记录。每种币种将提取以下字段:
- 名称币种的全名,例如"Bitcoin"。
- 代号代码符号,例如"BTC"。
- 价格以美元计的当前价格。
- 市值该行显示的总市值。
- 24小时涨跌幅过去24小时的百分比变动。
- 交易量24小时交易量。
为何普通请求在 CoinMarketCap 上失败
如果你用裸 HTTP 客户端请求 CoinMarketCap,很少能得到你在浏览器中看到的干净价格表。有两个不利因素。第一,CoinMarketCap 大量市场表格是客户端渲染的:初始 HTML 只是一个空壳,行数据要等页面 JavaScript 运行后才会填充。第二,该网站受到 Cloudflare 等机器人防护,数据中心 IP 和不像真实浏览器的请求模式会在到达已渲染的表格之前被挑战或限速。
因此,朴素的 requests.get() 往往返回空壳或验证页,而非币种数据。一个可用的 CoinMarketCap 爬虫需要两样东西结合使用:一个能渲染页面的浏览器,以及一个被网站识别为普通访客的 IP。
你可以自己搭建无头浏览器加轮换住宅代理池,但把它们组合起来并保持健康运行才是大部分工作。Crawling API 把这些难点折叠进一次调用:你把 CoinMarketCap 的 URL 连同 JavaScript token 一起发过去,它在可信 IP 后面渲染页面,并把渲染完成的 HTML 返回给你解析。如果你更倾向于通过轮换池路由自己的请求,Smart AI Proxy 以直连代理端点的形式提供相同的住宅 IP 轮换。
Crawlbase 提供两种 token 类型。普通 token 获取静态 HTML;JavaScript(JS)token 先在真实浏览器中渲染页面。CoinMarketCap 的市场表格是客户端渲染的,因此这里需要 JS token。普通 token 往往返回与普通请求相同的空壳,没有任何行可以解析。
前提条件
编写代码之前需要准备好几样东西,每样都不需要很长时间。
基础 Python。你应该能够编写和运行 Python 脚本,并用 pip 安装包。如果你不熟悉解析 HTML,我们的 BeautifulSoup 使用指南涵盖了本教程所需的选择器基础知识。
Python 3.8 或更高版本。用 python --version 确认你的版本。如果没有安装,请从 python.org 或 Anaconda 等发行版安装。
Crawlbase 账号和 JS token。注册后打开控制台,从账号文档页面复制你的 JavaScript(JS)token。把 token 当成密码对待:它用于验证你的请求,请不要提交到版本控制系统。
搭建项目
创建一个虚拟环境以隔离项目依赖,然后安装爬虫所需的两个库。
python --version python -m venv cmc_env source cmc_env/bin/activate pip install crawlbase beautifulsoup4
在 Windows 上,用 cmc_env\Scripts\activate 代替 source 那行来激活环境。两个依赖各司其职:crawlbase 是 Crawling API 的官方客户端,beautifulsoup4 解析返回的 HTML,让你可以通过 CSS 选择器提取各个字段。
步骤 1:获取已渲染的页面
首先获取完整页面。导入 CrawlingAPI 类,用你的 JS token 初始化,然后请求 CoinMarketCap URL。在解析前检查状态码,可以让失败显而易见,而非悄无声息。
from crawlbase import CrawlingAPI api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"}) def crawl(page_url): options = {"ajax_wait": "true", "page_wait": 5000} response = api.get(page_url, options) if response["status_code"] == 200: return response["body"].decode("latin1") print(f"Request failed: {response['status_code']}") return None if __name__ == "__main__": page_url = "https://coinmarketcap.com/" html = crawl(page_url) print(html[:500] if html else "No HTML returned")
两个等待选项对于像这样的客户端渲染目标非常重要:ajax_wait 告知 API 等待异步内容加载完成,page_wait 在加载后再等待固定毫秒数,让延迟渲染的表格行在页面被捕获前出现。5秒是一个合理的起点;如果表格返回为空,可以适当增加。CoinMarketCap 发送的页面编码范围较广,因此用 latin1 解码可以避免严格 UTF-8 解码可能抛出的字节错误。运行脚本后,你应该看到真实的市场标记,而不是普通请求返回的空壳。
CoinMarketCap 的表格需要在可信 IP 后面渲染页面,而这只需一次调用。Crawling API 接受 JS token,在真实浏览器中运行页面,在服务器端轮换住宅 IP,并将渲染完成的 HTML 返回给你,省去了自己运行无头浏览器集群和代理池的麻烦。先在免费层级向公开首页发起请求。
步骤 2:用 BeautifulSoup 解析币种字段
拿到渲染后的 HTML,将其加载到 BeautifulSoup 并遍历表格行。CoinMarketCap 将每种币种排列在主市场表格的一行中,币种单元格内包含名称和代号,右侧各列是数值字段。将每行的提取逻辑包裹在 try/except 中,这样单个字段缺失不会导致整次运行崩溃。
from bs4 import BeautifulSoup def text_of(row, selector): el = row.select_one(selector) return el.get_text(strip=True) if el else None def scrape_coins(html, limit=10): soup = BeautifulSoup(html, "html.parser") rows = soup.select("table tbody tr")[:limit] coins = [] for row in rows: try: cells = row.select("td") coins.append({ "name": text_of(row, "p.coin-item-name"), "symbol": text_of(row, "p.coin-item-symbol"), "price": cells[3].get_text(strip=True), "change_24h": cells[4].get_text(strip=True), "market_cap": cells[7].get_text(strip=True), "volume_24h": cells[8].get_text(strip=True), }) except (IndexError, AttributeError) as e: print("Skipping a row due to error:", e) return coins
名称和代号有稳定、易读的类钩子 p.coin-item-name 和 p.coin-item-symbol,可以直接定位。数值列(价格、24小时涨跌幅、市值和交易量)按行的 td 单元格位置提取,因为 CoinMarketCap 的价格单元格使用经常变化的哈希类名,不适合硬编码。text_of 辅助函数在元素缺失时返回 None 而不是抛出异常,try/except 则防止列布局不同的行拖垮整次运行,确保一条异常行不会中断全局。
CoinMarketCap 的标记,尤其是其哈希类名,会在没有通知的情况下变更。请将上述选择器和列索引视为起始模板,而非固定合约。当某个字段返回错误或为空时,在浏览器开发者工具中右键点击该单元格,检查实时结构,然后调整选择器或索引。定期维护选择器对任何生产爬虫来说都是正常的,不代表有什么问题。
步骤 3:整合在一起
现在将获取和解析逻辑整合成一个可运行的脚本。获取已渲染的 HTML,传给解析器,并将结构化记录以 JSON 格式打印出来。
import json from crawlbase import CrawlingAPI from bs4 import BeautifulSoup api = CrawlingAPI({"token": "YOUR_CRAWLBASE_JS_TOKEN"}) def crawl(page_url): options = {"ajax_wait": "true", "page_wait": 5000} response = api.get(page_url, options) if response["status_code"] == 200: return response["body"].decode("latin1") print(f"Request failed: {response['status_code']}") return None def text_of(row, selector): el = row.select_one(selector) return el.get_text(strip=True) if el else None def scrape_coins(html, limit=10): soup = BeautifulSoup(html, "html.parser") rows = soup.select("table tbody tr")[:limit] coins = [] for row in rows: try: cells = row.select("td") coins.append({ "name": text_of(row, "p.coin-item-name"), "symbol": text_of(row, "p.coin-item-symbol"), "price": cells[3].get_text(strip=True), "change_24h": cells[4].get_text(strip=True), "market_cap": cells[7].get_text(strip=True), "volume_24h": cells[8].get_text(strip=True), }) except (IndexError, AttributeError) as e: print("Skipping a row due to error:", e) return coins def main(): html = crawl("https://coinmarketcap.com/") if not html: return data = scrape_coins(html) print(json.dumps(data, indent=2)) if __name__ == "__main__": main()
输出结果示例
用 python scraper.py 运行完整脚本,你将得到一个干净的结构化记录列表,可直接写入 JSON、CSV 或数据库。由于市场不断变动,实际价格会与下面的示例不同。
[ { "name": "Bitcoin", "symbol": "BTC", "price": "$92,477.64", "change_24h": "2.14%", "market_cap": "$1.83T", "volume_24h": "$38.20B" }, { "name": "Ethereum", "symbol": "ETH", "price": "$1,744.77", "change_24h": "1.06%", "market_cap": "$210.6B", "volume_24h": "$14.95B" } ]
导出为 CSV 和 JSON
将 JSON 输出到控制台适合演示,但通常你会希望把数据保存到磁盘以便存储或后续分析。这些记录已经是扁平字典列表,因此写入两种格式只需几行代码。复用上面完整脚本中的 crawl 和 scrape_coins 函数即可。
import csv import json html = crawl("https://coinmarketcap.com/") coins = scrape_coins(html) fields = ["name", "symbol", "price", "change_24h", "market_cap", "volume_24h"] with open("crypto_prices.csv", "w", newline="") as f: writer = csv.DictWriter(f, fieldnames=fields) writer.writeheader() writer.writerows(coins) with open("crypto_prices.json", "w") as f: json.dump(coins, f, indent=2) print(f"Saved {len(coins)} coins to crypto_prices.csv and crypto_prices.json")
从这里开始,CSV 可以直接导入电子表格或 pandas DataFrame 进行分析,JSON 文件也易于加载到下游服务中。如果你需要的是实时数据流而非一次性快照,可以将脚本设置为定时运行,并将每次抓取结果追加到带日期的文件中。
扩展到更多币种和页面
首页列出的是排名靠前的币种,但 CoinMarketCap 对完整排名进行了分页。工作方式不变:每页的列表结构相同,因此已写好的解析器可继续使用。遍历编号页面,通过 Crawling API 获取每一页,并收集各行数据。
all_coins = [] for page in range(1, 4): # first three pages of the ranking url = f"https://coinmarketcap.com/?page={page}" html = crawl(url) if html: all_coins.extend(scrape_coins(html, limit=100)) print(f"Collected {len(all_coins)} coins across pages")
如果需要无人值守地大规模运行,可以通过异步 Crawler 排队处理页面 URL,而非同步循环,这样你可以提交整批页面并在结果就绪时收集。有关渲染密集型目标的更多内容,我们关于用 Python 抓取 JavaScript 页面的教程对本爬虫所依赖的等待渲染模式有更深入的讲解。
保持不被封锁
即使渲染问题已解决,CoinMarketCap 仍会监视爬虫形态的流量。以下几个习惯可以让运行保持健康,适用于任何受保护的 JavaScript 密集型目标。
- 控制请求节奏。在紧循环中拉取每一页是最快被限速的方式。分散请求,对于实时数据流来说每隔几分钟抓一次就足够了,不必在高频节奏上反复猛敲。
- 善用轮换。住宅 IP 池将请求分散到许多真实用户地址上,避免单个地址触发速率限制。Crawling API 会为你处理这一切;如果你自建技术栈,这是最关键的环节。
- 关注状态码。运行中开始返回挑战或错误,说明当前的速率或 IP 层级已经不够用了。把这当作需要退让的信号,而不是可以忽略的噪音。
更完整的方法请参阅如何在不被封锁的情况下抓取网站,以及关于网络爬虫中如何绕过 CAPTCHA 的深度文章。
抓取 CoinMarketCap 合法吗?
抓取 CoinMarketCap 是否被允许,取决于该网站的服务条款、你所在的司法管辖区以及你对数据的用途。CoinMarketCap 发布了服务条款和 robots.txt,两者共同划定了自动访问的边界。这里的任何代码都不能改变这一事实,它只是让技术部分得以实现。请阅读这些文档,并将其视为你采集内容和请求频率的上限,在任何商业项目之前尤为重要。
保持范围精简。本教程涵盖的市场表格是真正的公开数据:币种名称、代号、价格、市值、24小时涨跌幅和交易量,无需账号即可查看。这条线让工作有充分的合理性。本教程不涉及的内容同样重要:登录后才能看到的内容、与用户绑定的账号或自选数据、个人信息,以及你会再发布的受版权保护的媒体,这些都超出范围。不要绕过身份验证去获取公开表格未显示的数据。
还有一点专门针对本目标:CoinMarketCap 提供了官方 API。对于生产系统、商业用途或大批量抓取,官方 API 才是正确路径,因为它提供了有支持保障的合约、可预测的速率限制以及明确授权的数据。抓取公开网站适合学习、原型验证和小型公开数据作业,不适合需要保证正常运行时间和明确使用权的业务。当不确定时,优先选择官方 API。
核心要点
- 市场表格是客户端渲染且受保护的。普通请求返回空壳或挑战页,因此必须在解析前通过可信 IP 渲染页面。
-
一次调用同时完成两件事。使用 JS token 的 Crawling API 在服务器端渲染页面并轮换住宅 IP;
ajax_wait和page_wait控制等待行加载的时间。 -
混合使用选择器策略。用
p.coin-item-name和p.coin-item-symbol锚定名称和代号,按位置提取数值列,并预期哈希类名会漂移。 - 干净地导出和扩展。扁平字典可直接写入 CSV 或 JSON,同一解析器适用于分页排名的所有页面。
- 生产环境优先使用官方 API。坚守公开市场数据,遵守服务条款和 robots.txt,商业或大批量使用时选择 CoinMarketCap 的官方 API。
常见问题
为什么普通请求无法从 CoinMarketCap 获取加密货币价格?
有两个原因。第一,CoinMarketCap 大量市场表格通过 JavaScript 在客户端渲染,因此原始 HTML 只是一个空壳,只有在页面脚本运行后才会填充。第二,该网站受到 Cloudflare 等机器人防护,数据中心 IP 发出的自动化流量往往会在到达数据之前被挑战或限速。使用 Crawling API 的 JS token 在可信 IP 后面渲染页面,才能让行数据出现。
抓取 CoinMarketCap 需要普通 token 还是 JS token?
JS token。普通 token 获取静态 HTML,在 CoinMarketCap 上往往与普通请求返回的空壳相同。JS token 在返回 HTML 之前先在真实浏览器中渲染页面,这样 BeautifulSoup 解析时价格、市值和交易量等表格行才会存在。
我可以多频繁地抓取加密货币价格?
对于实时数据流,每隔几分钟抓取一次通常是安全的,且远低于任何速率上限。在紧循环中猛敲网站是最快触发限速或挑战的方式。通过轮换住宅 IP 路由请求,监控状态码,并在出现挑战时退让。如需保证吞吐量,官方 CoinMarketCap API 更适合。
我的选择器返回了错误的值。发生了什么变化?
几乎可以确定是 CoinMarketCap 的标记发生了变化。其哈希 CSS 类名频繁更改,列顺序也可能调整,上个月有效的选择器和位置索引可能已经失效。在可能的情况下,用稳定的 p.coin-item-name 和 p.coin-item-symbol 钩子进行锚定,当某个字段看起来不对时,在浏览器开发者工具中重新检查实时页面,并更新选择器或单元格索引。定期维护选择器对任何生产爬虫来说都是正常的。
我应该抓取 CoinMarketCap 还是使用其官方 API?
对于学习、原型验证和小型公开数据作业,抓取公开表格是获取名称、代号、价格、市值、24小时涨跌幅和交易量的好方式。对于生产系统、商业用途或大批量抓取,请使用 CoinMarketCap 的官方 API:它提供有支持保障的合约、可预测的速率限制以及明确的使用权。根据项目的重要程度选择合适的工具。
我可以合法采集哪些加密货币数据?
坚守无需账号即可查看的公开市场数据:币种名称、代号、价格、市值、24小时涨跌幅和交易量。不要采集账号、自选或个人数据,不要触碰任何需要登录的内容,也绝对不要绕过身份验证。遵守 CoinMarketCap 的服务条款和 robots.txt,商业或批量使用时依赖官方 API,而非针对公开网站扩大爬虫规模。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
