Crunchbase 是网络上最大的公司和初创企业数据目录之一。公开的公司主页承载着驱动市场研究、竞争分析、销售拓客和融资调研的结构化情报:公司名称、业务描述、总部位置、员工规模、官网、排名以及创始人信息。问题在于,Crunchbase 以客户端方式渲染这些主页,并对自动化流量进行严格的防御,因此一个普通的 HTTP 请求只会返回一个空壳,而非你所需要的数据。

本指南将展示如何用可靠的方式通过 Python 爬取 Crunchbase。你将构建一个小型、可运行的爬虫:通过 Crawling API 抓取渲染后的公司主页,用 BeautifulSoup 解析目标字段,并输出干净的结构化数据。整个教程范围仅限于公开主页数据,末尾的合法性部分不是样板文字,在将此脚本对准任何真实规模的目标前,请务必阅读。

你将构建什么

一个 Python 脚本,接受一个公开的 Crunchbase 公司 URL,通过 Crawling API 获取渲染后的 HTML,并为该公司提取结构化记录。我们将以一个公开的组织主页作为贯穿示例,提取以下字段:

  • 名称公司名称,例如"OpenAI"。
  • 描述关于公司业务的简短摘要。
  • 位置公司所在地,如"San Francisco, California"。
  • 员工数员工规模区间,例如"1001-5000"。
  • 公司官网指向公司自有网站的链接。
  • 排名该公司在 Crunchbase 排名中的位置。
  • 成立年份公司成立的年份。
  • 创始人创立该公司的人员。

为何普通请求在 Crunchbase 上会失败

如果你用裸 HTTP 客户端请求 Crunchbase 的组织 URL,你会得到一个状态码为 200 的响应,但响应体中几乎没有任何主页数据。有两个因素对你不利。第一,Crunchbase 用 JavaScript 在浏览器中渲染其主页内容,因此初始 HTML 只是一个壳,只有在页面脚本运行后才会填充。第二,Crunchbase 会迅速标记自动化流量:数据中心 IP 以及看起来不像真实浏览器的请求模式,会在到达渲染内容之前就被质询或封锁。

因此,一个有效的 Crunchbase 爬虫在一次请求中需要两样东西:一个真正能渲染页面的浏览器,以及一个平台识别为真实访客的 IP。你可以自行组装:使用无头浏览器加上轮换住宅代理池,但将这两者拼合在一起并保持健康运行,占据了大部分工作量。Crawling API 将两者整合进一次调用:你发送带有 JavaScript token 的 URL,它在受信任 IP 后面渲染页面,并将完整的 HTML 返回给你解析。

为什么需要 JS token

Crawlbase 提供两种 token 类型。普通 token 获取静态 HTML;JavaScript(JS)token 先在真实浏览器中渲染页面。Crunchbase 是客户端渲染的,所以这里需要 JS token。使用普通 token 会返回与普通请求相同的空壳,其中没有任何内容可供解析。

前置条件

在编写任何代码之前,你需要准备好以下几样东西。它们都不需要太长时间。

Python 基础。你应该能够编写和运行 Python 脚本,并用 pip 安装包。如果你是这门语言的新手,官方 Python 文档和任何初级课程都能让你达到本教程所需的水平。

Python 3.8 或更高版本。python --version 确认你的版本。如果没有,从 python.org 或 Anaconda 等发行版安装。

Crawlbase 账号和 JS token。注册后,打开控制台,从账号文档页复制你的 JavaScript(JS)token。像对待密码一样对待这个 token:它用于验证你的请求,所以不要提交到版本控制中。

搭建项目

创建一个虚拟环境,使项目依赖保持隔离,然后安装爬虫所需的两个库。

bash
python --version

python -m venv crunchbase_env
source crunchbase_env/bin/activate

pip install crawlbase beautifulsoup4

在 Windows 上,用 crunchbase_env\Scripts\activate 替换 source 行来激活环境。两个依赖项各司其职:crawlbase 是 Crawling API 的官方客户端,beautifulsoup4 通过 CSS 选择器解析返回的 HTML,让你可以提取各个字段。

第一步:抓取渲染后的主页

首先获取完整的页面。导入 CrawlingAPI 类,用你的 JS token 初始化它,并请求公司 URL。在解析之前检查状态码,可以让失败信息清晰可见而不是悄悄忽略。

python
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("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

if __name__ == "__main__":
    page_url = "https://www.crunchbase.com/organization/openai"
    html = crawl(page_url)
    print(html[:500] if html else "No HTML returned")

两个等待选项对于这类客户端渲染目标非常重要。ajax_wait 告诉 API 等待异步内容加载完成,page_wait 在加载后等待固定的毫秒数,让延迟渲染的元素在页面捕获前出现。五秒是一个合理的起点;如果主页字段返回为空,可以适当增加。用 python scraper.py 运行脚本,你应该能在前 500 个字符中看到真实的主页标记,而不是普通请求返回的空壳,这确认了渲染在你编写任何选择器之前就已经生效。

Crawlbase Crawling API

Crunchbase 需要在受信任 IP 后面的渲染页面,且要在一次调用中完成。Crawling API 接受 JS token,在真实浏览器中运行页面,在服务端轮换住宅 IP,并将完整的 HTML 交给你,省去了自行运行无头浏览器集群和代理池的麻烦。先在免费套餐上对一个公开公司主页进行测试。

第二步:用 BeautifulSoup 解析公司字段

拿到渲染后的 HTML,将其加载到 BeautifulSoup 中,通过选择器提取各个字段。Crunchbase 主页以可预测的结构排列核心详情,因此你可以将名称、描述、位置、员工数、官网、排名、成立年份和创始人映射到各自的选择器。用 try/except 包装整个提取过程,使单个缺失字段不会使整个运行崩溃。

python
from bs4 import BeautifulSoup

def text_of(soup, selector):
    el = soup.select_one(selector)
    return el.get_text(strip=True) if el else None

def scrape_company(html):
    soup = BeautifulSoup(html, "html.parser")
    rows = ".section-content-wrapper li.ng-star-inserted"

    company_link = soup.select_one(f"{rows}:nth-of-type(5) a[role='link']")

    return {
        "title": text_of(soup, "h1.profile-name"),
        "description": text_of(soup, "span.description"),
        "location": text_of(soup, f"{rows}:nth-of-type(1)"),
        "employees": text_of(soup, f"{rows}:nth-of-type(2)"),
        "company_url": company_link["href"] if company_link else None,
        "rank": text_of(soup, f"{rows}:nth-of-type(6) span"),
        "founded": text_of(soup, ".text_and_value li:nth-of-type(4) field-formatter"),
        "founders": text_of(soup, ".text_and_value li:nth-of-type(5) field-formatter"),
    }

text_of 辅助函数同时做两件有用的事:查询单个元素,并在元素缺失时返回 None,而不是对空值调用 .get_text() 时抛出异常。这使提取在特定主页上某个字段缺失时仍能保持健壮,这种情况很常见,因为并非每家公司都列出了排名或官网。公司官网从锚点的 href 而非其文本中读取,因此需要单独处理。

选择器会漂移

Crunchbase 的类名(Angular 的 ng-star-inserted 标记、field-formatter 元素以及区块包装器)会在不事先通知的情况下变更。将上面的选择器视为起始模板,而非固定合约。当某个字段返回为 None 时,在浏览器开发者工具中重新检查线上主页并更新选择器。定期进行选择器维护对任何生产级爬虫来说都是正常的,不是出了什么问题的迹象。

第三步:整合在一起

现在将抓取和解析整合成一个可运行的脚本。抓取渲染后的 HTML,交给解析器,并打印结构化记录。

python
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("utf-8")
    print(f"Request failed: {response['status_code']}")
    return None

def text_of(soup, selector):
    el = soup.select_one(selector)
    return el.get_text(strip=True) if el else None

def scrape_company(html):
    soup = BeautifulSoup(html, "html.parser")
    rows = ".section-content-wrapper li.ng-star-inserted"
    company_link = soup.select_one(f"{rows}:nth-of-type(5) a[role='link']")

    return {
        "title": text_of(soup, "h1.profile-name"),
        "description": text_of(soup, "span.description"),
        "location": text_of(soup, f"{rows}:nth-of-type(1)"),
        "employees": text_of(soup, f"{rows}:nth-of-type(2)"),
        "company_url": company_link["href"] if company_link else None,
        "rank": text_of(soup, f"{rows}:nth-of-type(6) span"),
        "founded": text_of(soup, ".text_and_value li:nth-of-type(4) field-formatter"),
        "founders": text_of(soup, ".text_and_value li:nth-of-type(5) field-formatter"),
    }

def main():
    page_url = "https://www.crunchbase.com/organization/openai"
    html = crawl(page_url)
    if not html:
        return
    data = scrape_company(html)
    print(json.dumps(data, indent=2))

if __name__ == "__main__":
    main()

输出示例

python scraper.py 运行完整脚本,你将得到该公司的干净结构化记录,可以直接写入 JSON、CSV 或数据库。

json
{
  "title": "OpenAI",
  "description": "OpenAI is an AI research and deployment company.",
  "location": "San Francisco, California, United States",
  "employees": "1001-5000",
  "company_url": "https://openai.com",
  "rank": "5",
  "founded": "2015",
  "founders": "Elon Musk, Greg Brockman, Ilya Sutskever, Sam Altman"
}

扩展到多家公司

单个主页只是演示;真实任务需要遍历一个公司列表。形式保持不变:维护一个组织 URL 列表,通过 Crawling API 抓取每一个,用同一个函数解析,并收集各行数据。由于每个主页共享相同的结构,你已经编写的解析器无需修改即可适用于所有公司。

python
companies = [
    "https://www.crunchbase.com/organization/openai",
    "https://www.crunchbase.com/organization/anthropic",
]

results = []
for url in companies:
    html = crawl(url)
    if html:
        results.append(scrape_company(html))

with open("companies.json", "w") as f:
    json.dump(results, f, indent=2)

要大规模查找公司 URL,你可以用相同的抓取-解析模式爬取 Crunchbase 的公开搜索和发现页面,收集组织链接,然后逐一访问。只需保持合理的请求量,并遵守以下提到的频率限制。

保持不被封锁

即使渲染已经处理好,Crunchbase 仍然会监视爬虫特征的流量。几个习惯能保持运行健康,它们适用于任何防御严密的商业目标。

  • 控制请求节奏。在紧密循环中轰炸主页是触发频率限制最快的方式。将请求分散开来,分散目标而非全速爬取单一路径。
  • 依赖轮换。住宅 IP 池将请求分散到众多真实用户地址上,使单个地址不会触发频率限制。Crawling API 为你处理这一点;如果你自己搭建技术栈,这是需要做好的部分。
  • 读取状态码。开始返回挑战或错误的运行在告诉你当前的频率或 IP 层级已不再足够。将其视为退出信号,而非需要忽略的噪音。

更广泛的策略手册,请参阅如何在不被封锁的情况下抓取网站和关于如何在网络爬取中绕过验证码的深入分析。如果你更倾向于通过轮换池路由自己的流量而不使用托管 API,Smart AI Proxy(也称为 AI Proxy)可以作为直连代理端点提供相同的住宅 IP 轮换。

爬取 Crunchbase 合法吗?

爬取 Crunchbase 是否被允许,取决于 Crunchbase 的服务条款、你所在的司法管辖区以及你对数据的用途。Crunchbase 的条款限制自动访问,因此无论你的工具多么谨慎,爬取都可能与这些条款相抵触。这里的任何代码都不能改变这一点;它只是让技术部分能够运行。请阅读 Crunchbase 的服务条款及其 robots.txt,并将两者视为你收集内容的边界。

几条值得坚守的原则。只收集公开数据:任何人无需账号即可看到的公司名称、描述、位置、员工规模区间、官网、排名、成立年份和创始人。遵守 Crunchbase 的频率预期,保持请求量足够低,避免给其服务器造成压力。避免个人数据,包括任何与可识别个人相关的内容,超出公司主页上公开列出的部分。如果你计划商业用途或批量重用数据,应获得许可或正式协议,而不是假设沉默即为同意。

对于需要授权或批量访问的情况,Crunchbase 提供官方 Crunchbase API,当你需要大容量、有保证的数据结构或商业权利时,这才是正确的工具。本指南故意将范围限定在公开公司主页,因为这是保持工作可辩护性的界限。它不涵盖登录后的任何内容、Crunchbase Pro 或付费套餐数据、需要登录才能访问的私人或财务数据、用户账号或主页数据,以及任何试图绕过身份验证的行为。如果你的项目需要的不止于公开主页,官方 Crunchbase API 或数据协议才是正确路径,而非更聪明的爬虫。

回顾

核心要点

  • Crunchbase 是客户端渲染的。普通请求返回空壳,因此必须在解析之前先渲染页面。
  • 你需要同时具备渲染能力和受信任的 IP。带有 JS token 的 Crawling API 在一次调用中完成两者;ajax_waitpage_wait 控制等待内容的时长。
  • BeautifulSoup 负责提取。将名称、描述、位置、员工数、官网、排名、成立年份和创始人映射到当前选择器,并预期这些选择器会漂移。
  • 通过循环 URL 来扩展。同一个解析器适用于每个主页,所以真实任务只是一个组织链接列表加上合理的请求节奏控制。
  • 坚守公开数据。遵守 Crunchbase 的服务条款和 robots.txt,对于需要授权或批量数据的情况优先使用官方 Crunchbase API,绝不触碰账号、Pro 数据或个人信息。

常见问题

为什么普通请求从 Crunchbase 返回不了数据?

因为 Crunchbase 用 JavaScript 在客户端渲染其主页内容。初始 HTML 是一个壳,只有在页面脚本在浏览器中运行后才会填充,所以一个原始 HTTP 请求返回状态码 200,但公司字段都是空的。要获得真实数据,必须先渲染页面,这正是 Crawling API 的 JS token 为你处理的事情。

对 Crunchbase 我需要普通 token 还是 JS token?

JS token。普通 token 获取静态 HTML,在 Crunchbase 上与普通请求返回的空壳相同。JS token 在将 HTML 返回之前先在真实浏览器中渲染页面,因此当 BeautifulSoup 解析时,主页字段是存在的。

我的选择器返回 None,发生了什么变化?

几乎可以肯定是 Crunchbase 的标记变了。它的 Angular ng-star-inserted 标记、field-formatter 元素以及区块包装器会在不事先通知的情况下变更,所以上个月有效的选择器可能现在已经失效。在浏览器开发者工具中重新检查线上主页并更新选择器。定期进行选择器维护对任何生产级爬虫来说都是正常的。

我应该使用官方 Crunchbase API 还是爬取网站?

如果你需要授权数据、批量访问、有保证的数据结构或商业重用权利,请使用官方 Crunchbase API。它正是为此而生,且能让你符合其服务条款。本指南中的方式爬取公开主页,适合没有 API 访问权限的小规模公开数据研究,但前提是遵守服务条款、robots.txt 和频率限制。

我可以从 Crunchbase 爬取财务或 Pro 数据吗?

不可以,本指南也不涵盖这部分。财务详情和 Crunchbase Pro 数据位于登录墙或付费套餐后面,因此不是公开数据。爬取登录墙后或付费内容,或绕过身份验证来获取这些内容,超出了本文讨论范围,并违反 Crunchbase 的服务条款。对于这类数据,正确路径是官方 Crunchbase API 或授权协议。

爬取 Crunchbase 时如何避免被封锁?

保持低单 IP 请求频率,分散目标而非循环单一路径,并通过轮换住宅 IP 路由,使单个地址不触发频率限制。Crawling API 为你管理轮换和受信任的 IP 池;如果你自己搭建技术栈,这是需要投入的部分。监控状态码,当开始看到挑战时退出。

开始构建

大规模爬取任何站点,无需与基础设施对抗。

Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。

自助开通 · 无需销售通话 · 提供企业级爬取量