Apple App Store 是开放网络上最大的公开软件目录之一。每个应用页面都包含一块结构一致的详细信息:应用名称、开发者、所属类别、公开星级评分及评分数量、价格,以及规范 URL。开发者用这些数据来对标竞争对手,分析师研究类目趋势,产品团队则追踪评分随时间的变化。所有信息都呈现在公开的产品页面上,无需登录即可查看,布局规律可循。
本指南将向你展示如何使用 JavaScript 和 Node.js 结合 Cheerio 抓取 Apple App Store 数据。你将构建一个可运行的小型爬虫,通过 Crawling API 获取公开应用页面,解析公开的元数据字段,并将结果导出为 JSON。整个演示范围限定在公开的应用元数据,不收集或分析个人评论者信息。结尾处的法律说明并非套话,请在将此方案用于任何真实规模前仔细阅读。
你将构建什么
一个 Node.js 脚本,接受一个公开的 App Store 产品 URL,通过 Crawling API 获取渲染后的 HTML,并提取应用公开元数据的结构化记录。我们以 Google Authenticator 为示例,提取以下字段:
- 应用名称 页面顶部显示的产品标题。
- 开发者 发布该应用的销售商或工作室名称。
- 类别 该应用所属的 App Store 类别,例如"Utilities"。
- 评分 Apple 为该应用显示的公开平均星级评分。
- 评分数量 该平均分背后的公开评分总数。
- 价格 标价,或者当应用无需预付费用时显示"Free"。
- 应用 URL 产品页面的规范公开 URL。
为什么普通请求在 App Store 上会失败
如果你用裸 HTTP 客户端请求 App Store 产品 URL,很少能拿到可用的标记语言。两个因素会对你不利。首先,Apple 在浏览器中渲染大部分产品页面内容,因此初始 HTML 只是一个薄壳,直到页面脚本运行并填充标题、评分和元数据块之后才完整。其次,App Store 会标记自动化流量:来自数据中心 IP 的、或请求模式不像真实浏览器的流量,会在到达渲染内容之前遭到限速或封锁。
因此,一个可用的 App Store 爬虫需要在单次请求中同时具备两点:能实际渲染页面的浏览器,以及平台认为是真实访客的 IP。你可以自行组合无头浏览器加上轮换住宅代理池,但将它们拼合在一起并保持健康运行,本身就是大部分工作量所在。Crawling API 将两者合并为一次调用:你发送 URL,它在可信 IP 后面渲染页面,并返回已渲染的 HTML 供你用 Cheerio 解析。
Crawling API 为你提供两种 token:普通 token 和 JavaScript token。App Store 产品页面需要在真实浏览器中渲染内容,因此本指南中的每个请求都请使用 JavaScript token。普通 token 返回未渲染的薄壳,你的选择器将无法匹配任何内容。
前置条件
在编写任何代码之前,需要准备好以下几样东西,都不需要太长时间。
基础的 JavaScript 和 Node.js 知识。 你应该能够编写和运行 Node 脚本,并用 npm 安装包。如果你是 Node 新手,官方文档和任何入门课程都能帮你达到本教程所需的水平。更完整的入门,可以参考我们的指南 使用 Node.js 构建网络爬虫。
Node.js 16 或更高版本。 使用 node --version 确认版本。如果尚未安装,请从 Node.js 官网或通过 nvm 等版本管理器安装。
Crawlbase 账号和 token。 注册后打开控制台,从账号文档页面复制你的 JavaScript token。免费套餐提供 1,000 次请求且无需绑定信用卡,且仅对成功请求计费。请像对待密码一样保管 token:它用于验证你的请求,因此不要放入版本控制系统。
搭建项目
创建项目文件夹,初始化,并安装爬虫所需的两个库。
node --version mkdir appstore-scraper && cd appstore-scraper npm init -y npm install crawlbase cheerio
两个依赖各司其职:crawlbase 是 Crawling API 的官方 Node 客户端,cheerio 使用类 jQuery 的 API 解析返回的 HTML,让你可以通过 CSS 选择器提取各个字段。在此文件夹中新建一个名为 scraper.js 的文件,并按以下步骤添加代码。
第 1 步:获取渲染后的应用页面
从获取完整页面开始。导入 CrawlingAPI 类,用你的 JavaScript token 初始化,并请求一个公开的 App Store 产品 URL。在解析前检查状态码,可以让错误显而易见而不是悄无声息。
const { CrawlingAPI } = require('crawlbase'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); const appURL = 'https://apps.apple.com/us/app/google-authenticator/id388497605'; api .get(appURL) .then((response) => { if (response.statusCode === 200) { console.log(response.body.slice(0, 500)); } }) .catch((error) => console.error('API request error:', error));
运行 node scraper.js,你应该能在 body 开头看到真实的 App Store 产品标记,而不是精简的薄壳。这在你编写任何选择器之前就确认了渲染的有效性。Crawling API 使用你提供的 JavaScript token 在真实浏览器中渲染页面,因此你拿到的 HTML 中已经包含标题、评分和元数据块。
第一次请求就返回了一个完整渲染的 App Store 产品页面,而你无需在自己这边运行无头浏览器或代理。Crawling API 在真实浏览器中运行页面,在服务端轮换住宅 IP,并处理 App Store 对爬虫的封锁,让你从一次调用中就能获得已渲染的 HTML。先从免费套餐开始,将其指向一个公开应用页面,然后再添加解析器。
第 2 步:用 Cheerio 解析公开元数据
拿到渲染后的 HTML 后,将其加载到 Cheerio,从标题和评分块中读取各字段。产品标题栏包含应用名称、开发者、类别和价格;评分组件包含平均星级和评分数量。防御性地读取每个字段,避免单个缺失值导致运行崩溃。
const cheerio = require('cheerio'); function parseAppMetadata(html, sourceUrl) { const $ = cheerio.load(html); // App name lives in the product header title let name = $('.app-header__title').text().trim(); const titleBadge = $('.badge--product-title').text().trim(); if (titleBadge) name = name.replace(titleBadge, '').trim(); // Developer / seller const developer = $('.app-header__identity').text().trim(); // Category, parsed from the "... in <Category>" header item let category = null; try { category = $('.product-header__list__item a.inline-list__item') .text() .trim() .split('in')[1] .trim(); } catch { category = null; } // Price, or "Free" when there is no upfront cost const price = $('.app-header__list__item--price').text().trim(); // Public average rating from the star widget's aria-label const rating = $('.we-star-rating').attr('aria-label') || null; // Public rating count, after the "•" separator let ratingCount = null; try { ratingCount = $('.we-rating-count') .text() .trim() .split('•')[1] .trim(); } catch { ratingCount = null; } return { name, developer, category, rating, ratingCount, price, appUrl: sourceUrl, }; }
几个细节保证了此函数对页面的忠实还原。应用名称来自 .app-header__title,并去除了小型产品标题徽章,只保留名称本身。开发者从 .app-header__identity 读取,类别从 .product-header__list__item a.inline-list__item 的文本中解析,其格式类似于"in Utilities"中"in"之后的部分。价格来自 .app-header__list__item--price。对于评分块,平均分从 .we-star-rating 组件的 aria-label 读取,公开评分数量从 .we-rating-count 的分隔符之后取得。每个字段都采用防御性读取方式,评分或价格缺失时返回 null 而不是抛出异常。
Apple 的类名(上述 app-header__* 和 we-rating-* 选择器)属于会随时间变化的布局。将这些选择器视为起始模板,而非固定契约。当某个字段返回为空时,在浏览器开发者工具中重新检查实时页面并更新选择器。定期维护选择器对任何生产级爬虫来说都是正常的,不代表哪里出了问题。
第 3 步:组装完整脚本并导出 JSON
现在将获取和解析两部分整合为一个可运行的脚本,然后将记录以 JSON 格式写入磁盘。使用简洁的脚本保持各部件最少化,后续如有需要可以封装成端点。
const fs = require('fs'); const { CrawlingAPI } = require('crawlbase'); const cheerio = require('cheerio'); const api = new CrawlingAPI({ token: 'YOUR_CRAWLBASE_TOKEN' }); async function crawl(appUrl) { const response = await api.get(appUrl); if (response.statusCode === 200) return response.body; console.error(`Request failed: ${response.statusCode}`); return null; } async function main() { const appUrl = 'https://apps.apple.com/us/app/google-authenticator/id388497605'; const html = await crawl(appUrl); if (!html) return; const app = parseAppMetadata(html, appUrl); fs.writeFileSync('app.json', JSON.stringify(app, null, 2)); console.log(`Saved metadata for ${app.name}`); } main();
将第 2 步中的 parseAppMetadata 函数粘贴到同一个文件中,以便 main 可以调用它。运行 node scraper.js,你会得到一个包含完整结构化记录的 app.json 文件。crawl 辅助函数会检查状态码,请求失败时返回 null,因此 main 会干净地停止,而不是解析一个损坏的页面。
输出结果示例
JSON 文件包含一个对象,记录了该应用的公开元数据:名称、开发者、类别、平均评分、评分数量、价格和规范 URL。
{ "name": "Google Authenticator", "developer": "Google LLC", "category": "Utilities", "rating": "4.7 out of 5", "ratingCount": "1.2M Ratings", "price": "Free", "appUrl": "https://apps.apple.com/us/app/google-authenticator/id388497605" }
这条记录是进一步分析、报告或可视化的坚实基础。如果将这类记录随时间积累存储,你就可以追踪一款应用的评分和评分数量的变化趋势,而这往往正是此类工作的目标所在。
扩展到多个应用
抓取一个应用页面只是演示;实际工作往往需要抓取一批应用。App Store 没有公开的全量应用索引,因此你需要自己整理一份产品 URL 列表,然后逐个循环,通过 Crawling API 获取每个页面,用同一个函数解析,并收集记录。由于每个产品页面共用相同的标题和评分结构,你已经写好的解析器无需任何修改即可适用所有页面。
async function scrapeMany(appUrls) { const records = []; for (const url of appUrls) { const html = await crawl(url); if (!html) continue; records.push(parseAppMetadata(html, url)); console.log(`Parsed ${url}`); // Pace requests so you stay under the rate limit await new Promise((r) => setTimeout(r, 2000)); } return records; }
这些良好习惯适用于任何目标:首先整理干净的 URL 列表,用同一个函数解析每个页面,并在请求之间加入短暂延迟,避免频繁轰炸网站。关于像本文中这样需要渲染的 JavaScript 页面,可参考我们的指南 爬取 JavaScript 网站。
保持不被封锁
即使渲染问题已处理好,App Store 仍会监控爬虫形态的流量。以下几个习惯可保持抓取任务健康运行,适用于任何难度较高的商业目标。
- 控制请求速率。 在页面获取之间加入延迟,而不是在紧密循环中频繁请求。分散请求是保持在速率限制以内最关键的单一因素。
- 依赖 IP 轮换。 住宅 IP 池将请求分散到多个真实用户地址,使单个地址不会触发限制或封锁。Crawling API 为你处理了这一点;如果你自行搭建,这是最需要做好的环节。
- 关注状态码。 如果运行开始返回非 200 的响应,说明当前速率或 IP 套餐已不够用。将此视为退出的信号,而非可忽略的噪音。
完整的操作手册,请参阅如何在不被封锁的情况下抓取网站。如果你需要针对这个具体目标的现成工具,我们的 Apple App Store 爬虫教程从不同角度覆盖了同样的内容。
抓取 Apple App Store 合法吗?
抓取 App Store 是否被允许,取决于 Apple 的条款、你所在的司法管辖区,以及你对数据的使用方式。Apple 的服务条款限制了自动化访问,因此无论你的工具多么谨慎,抓取行为都可能违反这些条款。本文中的任何代码都不会改变这一点;它们只是让技术层面的工作变得可行。请阅读 Apple 的条款和 App Store 的 robots.txt,遵守其中关于速率的要求,将请求量保持在合理范围内,并将二者视为你采集内容的边界。
本指南刻意将范围限定在公开的应用元数据:任何人无需登录即可在产品页面上看到的应用名称、开发者、类别、公开平均评分、公开评分数量、价格和规范 URL。这与平台上的个人数据有本质区别。个人评论及其作者属于个人数据。请将评分数量和平均分作为关于某款应用的聚合信号,切勿对个人评论者建立档案,也不要将某人的评论与其身份绑定后再发布。任何涉及 Apple 账号、大规模抓取或可识别个人信息的操作,都将引入 GDPR 和 CCPA 等隐私法律,这明确超出了本文讨论的范围。将截图、图标和描述文本视为 Apple 及开发者的财产,而非可随意再发布的内容。
如果你的项目需要的不止是公开元数据,正确的路径是官方渠道,而非更聪明的爬虫。Apple 为此提供了官方程序。App Store Connect 以开发者身份向你开放你自己应用的数据,而公开的 iTunes Search API 在有据可查的条款下返回结构化的应用元数据,包括名称、开发者、类别、价格和评分。当你需要大量数据、有保证的结构,或商业再利用权时,这些官方 API 才是正确的工具。如果不确定某种用法是否被允许,请取得许可或使用官方 API,而不是把沉默当作默许。
核心要点
- App Store 在客户端渲染内容并屏蔽自动化流量。 普通请求返回薄壳或被封锁,因此必须先在可信 IP 后面使用 JavaScript token 渲染页面,再进行解析。
- Crawling API 一次调用完成两件事。 它在真实浏览器中渲染页面,轮换住宅 IP,并处理封锁,返回你用 Cheerio 解析的已渲染 HTML。
- Cheerio 提取公开字段。 从标题和评分块中读取应用名称、开发者、类别、评分、评分数量、价格和应用 URL,并预期类名会随时间漂移。
- 通过循环 URL 列表进行扩展。 整理你自己的产品 URL 列表,用同一个函数解析每个页面,控制请求速率,并将结构化记录写入 JSON。
- 只采集公开元数据。 仅收集公开的应用元数据,将个人评论和评论者视为个人数据,遵守 Apple 的条款和 robots.txt,商业或大量使用时优先选择 Apple 的 App Store Connect 和 iTunes Search API。
常见问题
我可以抓取 App Store 上的任何应用吗?
只要你有 URL,就可以获取任何应用的公开产品页面。Apple 没有发布包含所有应用的完整公开索引,因此你需要从搜索结果、榜单或已有的链接中整理自己的产品 URL 列表,然后逐一循环。请保持合理的请求量,并仅采集本文涵盖的公开元数据字段。
为什么普通请求从 App Store 返回的数据不完整?
因为 Apple 在浏览器中渲染了产品页面的大部分内容,并且会对自动化流量发起挑战。来自数据中心 IP 的原始 HTTP 请求通常只返回薄壳,而不包含标题和评分内容。要获得完整的页面,必须在可信 IP 后面渲染它,而这正是 Crawling API 在你使用 JavaScript token 时为你处理的事情。
我的选择器返回空值。是什么改变了?
几乎可以肯定是 Apple 修改了标记语言。像 app-header__title 和 we-rating-count 这样的类名属于会随时间变化的布局,上个月还能用的选择器可能就此失效。在浏览器开发者工具中重新检查实时页面,更新 parseAppMetadata 中的选择器,就可以恢复正常。定期维护选择器是任何生产级爬虫的正常操作。
我可以抓取 App Store 的个人评论和评论者姓名吗?
这超出了本指南的范围,且有充分理由。个人评论及其作者属于个人数据,涉及 GDPR 和 CCPA 等隐私法律。请将公开的评分数量和平均分作为关于某款应用的聚合信号,不要建立个人评论者的档案,也不要将某人的评论与其身份绑定后再发布。超出公开元数据范围的需求,请使用 Apple 的官方 API。
Apple 有针对应用数据的官方 API 吗?
有。App Store Connect 让开发者可以访问自己应用的数据,而公开的 iTunes Search API 在有据可查的条款下返回应用的结构化元数据,包括名称、开发者、类别、价格和评分。如果你需要大量数据、有保证的结构,或数据的商业再利用权,这些官方渠道才是正确的选择。本文的公开元数据爬虫最适合研究、原型验证,以及不需要官方协议的小规模分析场景。
我可以用 JavaScript 以外的语言构建 App Store 爬虫吗?
可以。本指南使用 JavaScript 结合 Cheerio,但同样的方法适用于任何语言。Crawling API 提供了多种语言的库和 SDK,因此你可以用同样的方式获取渲染后的 HTML,并用你技术栈偏好的 HTML 解析器(例如 Python 中的 BeautifulSoup)进行解析。选择器和字段保持不变,只有解析语法有所不同。
大规模爬取任何站点,无需与基础设施对抗。
Crawlbase 负责处理代理、指纹和 CAPTCHA,让你的团队专注于交付数据流水线,而非维护爬取管道。1,000 次请求免费,无需信用卡。

