Bloomberg 是互联网上最具影响力的金融新闻与市场平台之一。其版块和话题页面持续发布技术、市场、经济和政治领域的头条新闻,每条新闻都附有链接、发布时间戳和所属版块。分析师追踪市场动向,研究人员梳理报道趋势,产品团队监测哪些报道出现在哪些栏目。所有这些信号都存在于版块页面的公开列表布局中,你甚至不需要打开任何一篇文章。
本指南介绍如何用 JavaScript 和 Node.js 结合 Cheerio 抓取 Bloomberg。你将构建一个小型可运行爬虫,通过 Crawling API 获取 Bloomberg 公开版块页面,解析页面上每篇报道的标题、文章链接、发布时间戳和版块,并将结果导出为 JSON 和 CSV。整个教程仅涉及公开的标题和链接元数据,不涉及完整文章正文,靠近结尾的合法性章节也不是样板文字,因为 Bloomberg 的编辑内容受版权保护。在将此爬虫指向任何真实量级流量之前,请先阅读该章节。
你将构建的内容
一个 Node.js 脚本,接收 Bloomberg 公开版块 URL,通过 Crawling API 获取渲染后的 HTML,并为列表页上的每篇报道链接提取一条结构化记录。我们以技术版块作为贯穿全文的示例,收集每篇报道的以下公开列表字段:
- Headline(标题)列表卡片上显示的文章标题。
- Link(链接)指向 bloomberg.com 上该文章的 URL。
-
Published(发布时间)来自卡片
time元素的发布时间戳,采用 ISO 日期格式。 - Section(版块)报道所属的版块或话题,例如 "Technology"。
请注意刻意缺失的内容:文章正文、摘要和任何媒体资源。本爬虫仅收集链接和元数据,绝不采集每个标题背后受版权保护的正文内容。
为什么普通请求在 Bloomberg 上会失败
如果你用裸 HTTP 客户端请求 Bloomberg 版块 URL,很少能拿到可用的列表。两个因素对你不利。首先,Bloomberg 在浏览器中通过 JavaScript 构建版块页面,因此初始 HTML 几乎是空壳,直到页面脚本运行后报道卡片才会填充进来。其次,Bloomberg 对自动化流量的检测极为积极:数据中心 IP 和不像真实浏览器的请求模式,在到达渲染后的标题之前就会遭到验证、限流或封锁。
因此,一个能正常工作的 Bloomberg 爬虫需要在单次请求中同时具备两点:能够实际渲染页面的浏览器,以及平台认为是真实访客的 IP。你可以自己用无头浏览器加上轮换住宅代理池来实现,但将这些拼凑在一起并保持其健康运行才是大部分工作所在。Crawling API 将这两者合并为一次调用:你发送 URL,它在可信 IP 后面渲染页面,返回可供你用 Cheerio 解析的完整 HTML。
Crawling API 提供两种 token:普通 token 和 JavaScript token。Bloomberg 需要在真实浏览器中渲染页面,因此本指南中的每次请求都请使用你的 JavaScript token。普通 token 返回未渲染的外壳,你的选择器将一无所获。
前置条件
在编写任何代码之前,你需要准备好以下几件事。每件都不会花太长时间。
基础 JavaScript 和 Node.js 知识。你应该熟悉编写和运行 Node 脚本,具备 DOM 概念,并能使用 npm 安装包。如果你是 Node 新手,官方文档和任何入门课程都能让你达到本教程所假设的水平。更完整的教程请参阅我们的用 Node.js 构建网络爬虫指南,涵盖基础知识。
Node.js 16 或更高版本。使用 node --version 确认你的版本。如果没有,请从 Node.js 官网安装,或通过 nvm 等版本管理器安装。
Crawlbase 账号和 token。注册后,打开控制台,从账号文档页面复制你的 JavaScript token。免费套餐最多提供 20,000 次请求,无需信用卡,且仅对成功的请求收费。请像对待密码一样保管好 token:它用于验证你的请求,不要将其提交到版本控制系统。
项目设置
创建项目文件夹,初始化并安装爬虫所需的两个库。
node --version mkdir bloomberg-scraper && cd bloomberg-scraper npm init -y npm install crawlbase cheerio
两个依赖各司其职:crawlbase 是 Crawling API 的官方 Node 客户端,cheerio 以 jQuery 风格的 API 解析返回的 HTML,让你能通过 CSS 选择器提取各个字段。在该文件夹中创建名为 scraper.js 的文件,并添加以下各步骤的代码。
第一步:获取渲染后的版块页面
从获取完整页面开始。导入 CrawlingAPI 类,用你的 JavaScript token 初始化它,并请求 Bloomberg 公开版块 URL。旧版教程使用的是技术版块,我们沿用相同选择。在解析之前检查状态码,可以让失败情况明显暴露而不是悄然无声。
const { CrawlingAPI } = require('crawlbase'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); const bloombergPageURL = 'https://www.bloomberg.com/technology'; api .get(bloombergPageURL) .then((response) => { if (response.statusCode === 200) { console.log(response.body.slice(0, 500)); } }) .catch((error) => console.error('API request error:', error));
用 node scraper.js 运行脚本,你应该在正文顶部看到真实的 Bloomberg 版块标记,而不是精简外壳。这确认了渲染在你编写任何选择器之前就已经正常工作。Crawling API 使用你提供的 JavaScript token 在真实浏览器中渲染页面,因此你拿到的 HTML 中报道卡片已经存在。
第一次请求就返回了完整渲染的 Bloomberg 技术版块页面,你这边无需无头浏览器,也无需代理。Crawling API 在真实浏览器中运行页面,在服务端轮换住宅 IP,并处理 Bloomberg 对爬虫发出的各种验证挑战,因此你只需一次调用就能拿到完整的 HTML。先在免费套餐上指向一个公开版块页面,然后再添加解析器。
第二步:用 Cheerio 解析每个标题
拿到渲染后的 HTML,将其加载到 Cheerio 中并遍历报道链接。在 Bloomberg 版块页面上,每篇文章都通过 href 指向 /news/articles/ 路径的锚元素可以访问,版块标题位于列表上方的眉线(eyebrow)元素中。旧版教程从 .Eyebrow_sectionTitle-Wew2fboZsjA- a 读取版块,从页面 time 元素的 datetime 属性读取发布日期,我们沿用这两个方式,为每条报道链接收集一条记录。对每个字段进行防御性读取,可以防止一个缺失值导致整次运行崩溃。
const cheerio = require('cheerio'); function parseDataFromHTML(html) { const $ = cheerio.load(html); const seen = new Set(); const results = { section: '', articles: [], }; // Section / topic title from the eyebrow element results.section = $('.Eyebrow_sectionTitle-Wew2fboZsjA- a').first().text().trim() || 'Technology'; // One record per public article link on the listing $('a[href*="/news/articles/"]').each((_, element) => { const anchor = $(element); const headline = anchor.text().replace(/\n\s+/g, ' ').trim(); let link = anchor.attr('href'); if (!headline || !link) return; if (link.startsWith('/')) { link = new URL(link, 'https://www.bloomberg.com').href; } if (seen.has(link)) return; // skip duplicate links seen.add(link); // Published timestamp from a nearby time element, if present const timeAttr = anchor .closest('article') .find('time') .attr('datetime'); const published = timeAttr ? timeAttr.split('T')[0] : ''; results.articles.push({ headline, link, published: published || 'Date not available', section: results.section, }); }); return results; }
以下几个细节让这段代码忠实于页面结构。版块标题来自 .Eyebrow_sectionTitle-Wew2fboZsjA- 锚元素,与旧版解析器的读取方式完全一致。每篇报道通过指向 /news/articles/ 的锚元素匹配,我们将相对路径解析为绝对的 bloomberg.com URL,使链接在页面之外也能正常使用。Set 去除了重复链接,因为同一篇文章通常会出现在版块页面的多个模块中。发布日期从附近 time 元素的 datetime 属性中读取,并通过 split('T')[0] 截取到 ISO 日期部分,这与原版处理文章时间戳的方式相同。
Bloomberg 生成的类名(如上面的 Eyebrow_* 后缀)会在无预告的情况下变化,版块页面也会频繁重新排列其模块。请将选择器视为起始模板,而非约定。当标题或版块返回空时,在浏览器开发者工具中重新检查实际页面,并更新选择器。定期维护选择器对任何生产爬虫来说都是正常的事,不代表哪里出了问题。
第三步:组装完整脚本并导出 JSON 和 CSV
现在将获取和解析两个步骤串联成一个可运行的脚本,然后将记录写入磁盘,同时保存为 JSON 和 CSV。旧版指南将原始 HTML 保存到文件后再第二次解析;将获取和解析合并到单次运行中可以减少活动部件,且效果相同。
const fs = require('fs'); const { CrawlingAPI } = require('crawlbase'); const cheerio = require('cheerio'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); async function crawl(pageUrl) { const response = await api.get(pageUrl); if (response.statusCode === 200) return response.body; console.error(`Request failed: ${response.statusCode}`); return null; } function toCsv(rows) { const headers = ['headline', 'link', 'published', 'section']; const escape = (value) => `"${String(value).replace(/"/g, '""')}"`; const lines = [headers.join(',')]; for (const row of rows) { lines.push(headers.map((h) => escape(row[h])).join(',')); } return lines.join('\n'); } async function main() { const url = 'https://www.bloomberg.com/technology'; const html = await crawl(url); if (!html) return; const data = parseDataFromHTML(html); fs.writeFileSync('bloomberg.json', JSON.stringify(data, null, 2)); fs.writeFileSync('bloomberg.csv', toCsv(data.articles)); console.log(`Saved ${data.articles.length} headlines to JSON and CSV`); } main();
将第二步中的 parseDataFromHTML 函数粘贴到同一个文件中,使 main 可以调用它。用 node scraper.js 运行,你将得到两个文件:bloomberg.json 包含完整的结构化记录,bloomberg.csv 可直接在电子表格中打开。toCsv 辅助函数会对每个字段加引号,并将内嵌的引号双写,这一点在这里很重要,因为标题中频繁包含逗号。
输出结果示例
JSON 文件包含版块信息,以及每个标题的一个对象,各自附有标题文本、文章链接、发布日期和所属版块。下面展示的标题和链接为示例占位符,并非实时数据。
{ "section": "Technology", "articles": [ { "headline": "Chipmaker Delays Second Plant as Subsidies Stay in Flux", "link": "https://www.bloomberg.com/news/articles/example-chip-delay", "published": "2024-01-18", "section": "Technology" }, { "headline": "Cloud Provider Posts Record Quarter on AI Demand", "link": "https://www.bloomberg.com/news/articles/example-cloud-quarter", "published": "2024-01-17", "section": "Technology" } ] }
CSV 以相同的标题行反映了同样的数据,可直接导入 Excel、Google Sheets 或任何能读取分隔符文件的数据管道。
headline,link,published,section "Chipmaker Delays Second Plant as Subsidies Stay in Flux","https://www.bloomberg.com/news/articles/example-chip-delay","2024-01-18","Technology" "Cloud Provider Posts Record Quarter on AI Demand","https://www.bloomberg.com/news/articles/example-cloud-quarter","2024-01-17","Technology"
跨版块和页面扩展
单个版块页面只是演示;真正的任务会随时间追踪多个栏目。Bloomberg 公开了一系列版块 URL(technology、markets、economics、politics 等),你可以遍历它们,通过 Crawling API 分别获取,用同一个函数解析,再合并结果。由于每个版块页面共享相同的列表结构,你已经写好的解析器无需修改就能跨所有版块使用。
async function scrapeSections(sections) { const all = []; for (const path of sections) { const url = `https://www.bloomberg.com/${path}`; const html = await crawl(url); if (!html) continue; const { articles } = parseDataFromHTML(html); all.push(...articles); console.log(`${path}: ${articles.length} headlines`); // Pace requests so you stay under the rate limit await new Promise((r) => setTimeout(r, 2000)); } return all; } // scrapeSections(['technology', 'markets', 'economics']);
对于大量或重复运行,同样的获取再解析模式可以直接迁移到异步 Crawler,它可以将多个 URL 加入队列,并将结果推送回来,而无需阻塞在每个请求上。关于像这样的渲染密集型 JavaScript 页面的更多信息,请参阅我们的抓取 JavaScript 网站指南,以及有关大规模金融数据抓取的背景介绍。
保持不被封锁
即使渲染问题已经解决,Bloomberg 仍会监测爬虫特征的流量。以下几个习惯能让运行保持健康,适用于任何难度较高的商业目标。
- 控制请求节奏。在版块请求之间引入延迟,而不是在紧密循环中高频访问。分散请求是保持在 Bloomberg 速率限制之下最重要的单一因素。
- 依赖 IP 轮换。住宅 IP 池将请求分散到众多真实用户地址,确保没有单一地址触发限制或验证挑战。Crawling API 为你处理这一切;如果你自己搭建方案,这是最需要做好的部分。
- 读懂状态码。当运行开始返回验证挑战或非 200 响应时,说明当前速率或 IP 层级已不再足够。将其视为需要回退的信号,而不是可以忽略的噪声。
更宏观的操作手册请参阅如何抓取网站而不被封锁。
抓取 Bloomberg 是否合法?
抓取 Bloomberg 是否被允许,取决于 Bloomberg 的服务条款、你所在的司法管辖区,以及你对数据的使用方式。Bloomberg 的条款限制了自动化访问和对其内容的再利用,因此无论你的技术手段多么谨慎,抓取行为都可能与这些条款相抵触。这里的任何代码都不会改变这一点,它只是让技术层面的事情能够运作。请阅读 Bloomberg 的服务条款及其 robots.txt,遵守其中说明的任何速率预期,并将两者视为你采集内容的边界。关键是,绝不抓取登录墙或付费墙后面的任何内容:Bloomberg 的大量报道都是付费内容,绕过付费墙在条款上违规,在许多地区也构成法律违规。
本指南刻意将范围限定在公开的标题和链接元数据:文章标题、文章 URL、发布时间戳,以及报道所属版块,这些信息无需登录即可在版块页面上看到。这与文章本身截然不同。Bloomberg 的报道、分析和媒体资源是受版权保护的作品。切勿抓取、存储或再分发完整文章正文、摘要或图片,也不要组装一个复现 Bloomberg 编辑内容的衍生档案库。收集标题和链接供读者点击跳转至 bloomberg.com 原文,是一种正常的、以链接为导向的用途;复制该链接背后的正文则不然。如果你触及的任何字段涉及可识别的个人,隐私法(如 GDPR 和 CCPA)将在版权之上额外适用,这是坚守事实性列表元数据的又一理由。
如果你的项目需要超出公开标题范围的内容,正确的途径是合规渠道,而不是更聪明的爬虫。Bloomberg 提供官方和授权数据产品,包括 Bloomberg Terminal 和企业数据 feed,在明确的商业条款、归因规则和再利用权利下提供市场数据和内容。当你需要完整内容、保证结构、大量数据或再分发权利时,这些才是正确的工具。当你不确定某种用途是否被允许时,请获取许可或签署数据协议,而不是假设沉默即默许。关于合规选项的更广泛调查,请参阅我们对全球最佳金融数据提供商的综述。
核心要点
- Bloomberg 在客户端渲染列表,且封锁力度很强。普通请求返回空外壳或验证挑战,因此你必须使用 JavaScript token 在可信 IP 后面渲染页面,才能解析。
- Crawling API 一次调用完成所有工作。它在真实浏览器中渲染页面,轮换住宅 IP,处理验证挑战,返回可用 Cheerio 解析的完整 HTML。
-
Cheerio 提取公开字段。匹配每个
/news/articles/锚元素,读取标题、链接、发布时间戳和版块,按链接去重,并预期生成的类名会发生漂移。 - 扩展和导出。遍历 Bloomberg 的公开版块 URL,控制请求节奏,将结构化记录写入 JSON 和 CSV;当数量增大时改用异步 Crawler。
- 仅限标题和链接。Bloomberg 的文章正文和媒体受版权保护,因此切勿抓取或再分发正文,不要触碰登录或付费墙后面的任何内容,遵守服务条款和 robots.txt,并优先使用 Bloomberg 的官方或授权 feed 用于生产用途。
常见问题
用这个爬虫可以从 Bloomberg 收集哪些数据?
本指南仅收集公开列表元数据:文章标题、文章链接、发布时间戳,以及报道所属版块,这些信息无需登录即可在 Bloomberg 版块页面上看到。它不收集文章正文、摘要或媒体资源,因为这些内容受版权保护。输出的是一组链接和元数据,你可以用它来监测报道动态并点击跳转至原文。
为什么普通请求从 Bloomberg 返回不完整的数据?
因为 Bloomberg 通过 JavaScript 在客户端构建版块页面,并对自动化流量发起验证挑战。来自数据中心 IP 的原始 HTTP 请求通常返回空外壳或封锁页面,而不是报道卡片。要获取完整页面,你必须在可信 IP 后面渲染它,这正是当你使用 JavaScript token 时 Crawling API 为你处理的事情。
我可以抓取付费墙后面的 Bloomberg 文章吗?
不可以。本指南在这一点上有明确要求:绝不抓取登录墙或付费墙后面的任何内容。付费内容受 Bloomberg 条款约束,绕过付费墙可能还会带来法律风险。请坚守任何人无需账号即可在版块页面上看到的公开标题和链接,如需完整的付费内容,请使用 Bloomberg 的官方或授权产品。
我的选择器返回空值。发生了什么变化?
几乎可以肯定是 Bloomberg 的标记发生了变化。像 Eyebrow_sectionTitle-Wew2fboZsjA- 这样的生成类名会在无预告的情况下改变,版块页面也会频繁重新排列其模块,因此上个月还能用的选择器可能就此失效。在浏览器开发者工具中重新检查实际页面,更新 parseDataFromHTML 中的选择器,即可恢复正常。定期维护选择器对任何生产爬虫来说都是正常的事。
我可以用 JavaScript 以外的语言构建 Bloomberg 爬虫吗?
可以。本指南使用 JavaScript 和 Cheerio,但同样的方法适用于任何语言。Crawling API 为多种语言提供了库和 SDK,因此你以同样的方式获取渲染后的 HTML,再用你的技术栈偏好的 HTML 解析器(如 Python 中的 BeautifulSoup)解析。选择器和字段保持不变,只有解析语法会变。
Bloomberg 是否提供官方数据 feed?
是的。Bloomberg 提供官方和授权数据产品,包括 Bloomberg Terminal 和企业数据 feed,在明确的商业条款和再利用权利下提供市场数据和内容。如果你需要完整内容、大量数据、保证结构或再分发权利,合规渠道才是正确选择。这个公开元数据爬虫最适合研究、监测和链接收集场景,即不需要官方协议的用途。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。
