想快速获取 App Store 应用的内购详情?本文带你一步步搭建一个免费、稳定的爬虫方案,基于 Node.js 和 Puppeteer,轻松应对反爬策略,还能通过 Cloudflare Worker 实现全球加速!✨
🌟 为什么要自己搭建爬虫?
在开发或研究 App Store 应用时,获取内购信息(如价格、订阅详情)是关键需求。然而,苹果官方 API 限制重重,第三方服务费用高昂且额度有限。自建爬虫不仅免费,还能完全掌控数据抓取逻辑,灵活应对频繁查询和反爬挑战。让我们探索如何用 Node.js 实现高效、稳定的内购信息抓取!
📊 获取 App Store 内购信息的渠道对比
以下是四种常见渠道的详细对比,帮你快速选择最适合的方案:
💡 综合建议
渠道三:直接爬取 App Store 网页 是满足“Node.js 实现、免费、频繁查询内购详情”需求的最佳选择。虽然需要处理反爬策略(如请求频率控制、模拟浏览器行为)和维护页面结构变化,但它免费、可控,且能稳定获取内购列表。
渠道二 适合快速获取基础信息,内购详情需结合渠道三的爬虫逻辑。
渠道四 适合低频查询或有预算的场景,免费额度难以支撑高频需求。
渠道一 因无法提供内购列表,不推荐.
推荐方案:基于 Node.js 实现渠道三,结合反爬策略,打造稳定高效的内购爬虫!🚀
🛠 方法一:一键部署
想快速上手?运行以下命令,自动完成环境配置和爬虫部署:
bash <(curl -fsSL https://raw.githubusercontent.com/qww2014/fetchIAP/refs/heads/main/deploy-fetchiap.sh)
💡 提示:一键 deployment 适合初学者,但建议了解逐步搭建流程以便灵活调整!
🛠 方法二:逐步搭建爬虫服务
以下是详细的逐步搭建指南,带你从零打造一个基于 Node.js 和 Puppeteer 的 App Store 内购爬虫,并通过 Cloudflare Worker 实现全球加速。
📌 步骤 1:搭建 VPS 版 Puppeteer API 服务
1. 准备环境
在你的 VPS 上执行以下命令,安装 Node.js 和必要工具:
sudo apt update
sudo apt install -y nodejs npm
# 注意:后续步骤需要 pnpm
sudo npm install -g pnpm
1.1 检查 Node.js 版本
确保 Node.js 版本 ≥ 18。若低于此版本,升级到最新 LTS 版本:
sudo npm install -g n
sudo n lts
n # 选择更换 Node 版本
node -v # 确认版本
# 如果版本未更新,刷新 shell 缓存
hash -r
2. 安装 Puppeteer 和 Express
创建项目目录并安装依赖:
mkdir -p /opt/fetchIAP-server
cd /opt/fetchIAP-server
pnpm init
pnpm add puppeteer express
# 生产环境建议安装 pm2
npm add pm2 -g
# 安装浏览器(根据架构选择)
npx puppeteer browsers install chrome # x86
apt install -y chromium # ARM
3. 创建 server.js
在 /opt/fetchIAP-server
目录下创建 server.js
:
nano server.js
粘贴以下代码:
/*
* @Author: Lao Qiao
* @Date: 2025-04-28
* @FilePath: /fetchIAP-multi/server.js
* 小美出品,必属精品 ✨
*/
const express = require('express');
const { fetchIAP } = require('./fetchIAP');
const app = express();
const port = 3000;
const TIMEOUT_PER_COUNTRY = 30000; // 每个国家超时时间(ms)
app.use(express.json());
// 健康检查接口
app.get('/', (req, res) => {
res.send('✨ FetchIAP Server 正常运行中!');
});
// 单国家查询,附带超时保护
const fetchIAPWithTimeout = (params, timeoutMs = 30000) => {
return Promise.race([
fetchIAP(params),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('抓取超时')), timeoutMs)
),
]);
};
app.post('/iap', async (req, res) => {
const { appId, countries = [], slug = '' } = req.body;
if (!appId || !Array.isArray(countries) || countries.length === 0) {
return res.status(400).json({ success: false, error: '请求必须包含 appId 和 countries 列表!' });
}
const isValidCountryCode = (code) => /^[a-z]{2}$/i.test(code);
const invalidCountries = countries.filter(c => !isValidCountryCode(c));
if (invalidCountries.length > 0) {
return res.status(400).json({ success: false, error: `国家代码格式错误:${invalidCountries.join(', ')}` });
}
const results = {};
try {
for (const country of countries) {
console.log(`✨ 查询 ${country.toUpperCase()}...`);
try {
const items = await fetchIAPWithTimeout({ appId, country, slug }, TIMEOUT_PER_COUNTRY);
results[country] = items;
} catch (err) {
console.error(`⚠️ 查询 ${country.toUpperCase()} 失败:${err.message}`);
results[country] = { error: err.message };
}
}
res.json({ success: true, data: results });
} catch (err) {
console.error('❌ 总体查询失败:', err);
res.status(500).json({ success: false, error: '服务器内部错误', details: err.message });
}
});
// 启动服务器
app.listen(port, () => {
console.log(`🚀 FetchIAP Server 已启动,监听端口 ${port}`);
});
保存并退出:
Ctrl + O, 回车, Ctrl + X
4. 创建 fetchIAP.js
在同一目录下创建 fetchIAP.js
:
nano fetchIAP.js
粘贴以下 code:
/*
* @Author: Lao Qiao
* @Date: 2025-04-28
* 小美出品,必属精品 ✨
*/
const puppeteer = require('puppeteer');
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
const purchaseLabelMap = {
us: 'In-App Purchases',
cn: 'App 内购买项目',
jp: 'アプリ内課金有り',
kr: '앱 내 구입',
fr: 'Achats intégrés',
de: 'In‑App‑Käufe',
it: 'Acquisti In-App',
es: 'Compras dentro de la app',
ru: 'Встроенные покупки',
};
async function autoScrollUntil(page, selector, timeout = 10000) {
const start = Date.now();
while ((Date.now() - start) < timeout) {
const found = await page.evaluate(sel => !!document.querySelector(sel), selector);
if (found) break;
await page.evaluate(() => window.scrollBy(0, window.innerHeight / 2));
await sleep(100);
}
}
async function fetchIAP({ appId, country = 'us', slug = '' }) {
const url = slug
? `https://apps.apple.com/${country}/app/${slug}/id${appId}`
: `https://apps.apple.com/${country}/app/id${appId}`;
const browser = await puppeteer.launch({
headless: 'new',
args: ['--no-sandbox', '--disable-setuid-sandbox'],
});
const page = await browser.newPage();
try {
await page.setExtraHTTPHeaders({ 'Accept-Language': 'en-US,en;q=0.9' });
await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36');
await page.goto(url, { waitUntil: 'domcontentloaded', timeout: 60000 });
await autoScrollUntil(page, 'dt.information-list__item__term');
await sleep(500);
const purchaseLabel = purchaseLabelMap[country.toLowerCase()] || 'In-App Purchases';
const items = await page.evaluate(label => {
const sections = Array.from(document.querySelectorAll('dt.information-list__item__term'));
let matchedSection = null;
for (const dt of sections) {
if (dt.textContent.trim() === label) {
matchedSection = dt.closest('.information-list__item');
break;
}
}
if (!matchedSection) return [];
const results = [];
matchedSection.querySelectorAll('li.list-with-numbers__item').forEach(li => {
const name = li.querySelector('.list-with-numbers__item__title')?.textContent.trim();
const price = li.querySelector('.list-with-numbers__item__price')?.textContent.trim();
if (name && price) results.push({ name, price });
});
return results;
}, purchaseLabel);
return items;
} finally {
await browser.close();
}
}
module.exports = { fetchIAP };
保存并退出。
5. 启动服务器
开发环境直接运行:
node server.js
生产环境使用 pm2
运行并设置开机自启:
pm2 start server.js --name fetchIAP-server
pm2 save
pm2 startup
现在,服务器通过 POST http://your-vps-ip:3000/iap
接受查询请求,返回 JSON 格式的内购数据!
📌 步骤 2:部署 Cloudflare Worker 作为 API 代理
为提升访问速度并隐藏 VPS IP,部署一个 Cloudflare Worker 作为透明代理。
1. 登录 Cloudflare
访问 Cloudflare 仪表板,进入 Workers 页面。
2. 创建 Worker
创建一个新的 Worker,粘贴以下 code:
export default {
async fetch(request, env, ctx) {
const url = new URL(request.url);
const targetUrl = 'http://your-vps-ip:3000' + url.pathname; // 注意!替换为你的 VPS 公网 IP
const modifiedRequest = new Request(targetUrl, {
method: request.method,
headers: request.headers,
body: request.body,
});
return fetch(modifiedRequest);
},
};
💡 Worker 作用:客户端请求 Worker,Worker 转发到 VPS,VPS 返回结果后再通过 Worker 返回客户端。全球访问加速,VPS IP 完全隐藏!
🎯 最终效果:快速查询内购信息
查询示例
发送以下请求到 Worker:
POST https://your-worker-subdomain.workers.dev/iap
Content-Type: application/json
{
"appId": "6448311069",
"slug": "chatgpt",
"countries": ["us", "jp", "kr"]
}
返回结果
{
"success": true,
"data": {
"us": {
"success": true,
"time_ms": 3452,
"data": [
{ "name": "ChatGPT Plus", "price": "$19.99" },
{ "name": "ChatGPT Pro", "price": "$200.00" }
]
},
"jp": {
"success": false,
"time_ms": 30020,
"error": "抓取超时"
},
"kr": {
"success": true,
"time_ms": 2750,
"data": [
{ "name": "ChatGPT Plus", "price": "₩29,000" },
{ "name": "ChatGPT Pro", "price": "₩299,000" }
]
}
}
}
工作流程
客户端请求 Worker。
Worker 转发请求到 VPS 上的 Puppeteer 爬虫。
Puppeteer 启动浏览器,抓取指定国家/地区的内购数据。
数据通过 Worker 返回客户端。
优势:
全球访问速度快,延迟低。
VPS IP 隐藏,安全性高。
免费、可控、支持多国家内购查询。
🛠 故障排除:缺少 Chrome 依赖库
如果服务器提示缺少 Chrome 依赖的共享库,运行以下命令安装:
sudo apt update
sudo apt install -y \
libnss3 \
libatk-bridge2.0-0 \
libcups2 \
libx11-xcb1 \
libxcomposite1 \
libxdamage1 \
libxrandr2 \
libasound2 \
libpangocairo-1.0-0 \
libgtk-3-0
然后重启服务:
pm2 reload fetchIAP-server
🎉 总结
通过本教程,你已学会如何用 Node.js 和 Puppeteer 搭建一个高效的 App Store 内购信息爬虫,并通过 Cloudflare Worker 实现全球加速。无论是开发者还是数据分析师,这个方案都能帮你快速获取内购详情,助力项目成功!
接下来做什么?
测试你的爬虫,优化请求频率以规避反爬限制。
扩展功能,比如批量查询多个应用。
分享你的成果,加入技术社区讨论!🚀
💬 有问题? 在评论区留言,我会尽快解答!
评论区