>
一个用于从Meta平台提取和分析竞争对手广告的两阶段技能。
阶段 1: Playwright 爬虫(无需 API 密钥)
└── facebook.com/ads/library → 广告创意、文案、状态、平台、日期
阶段 2: Meta Graph API(需要访问令牌)
└── graph.facebook.com/v23.0/ads_archive → 支出范围、展示次数、人口统计数据
分析层:Claude 综合两个来源的洞察
使用时机:始终作为第一步,或当用户没有 API 令牌时。
获取内容:广告创意(图片/视频 URL)、广告文案、CTA 文本、页面名称、开始日期、活跃状态、平台(Facebook/Instagram)、广告格式(轮播、视频、静态)。
无法获取:支出范围、展示次数、人口统计细分(这些需要阶段 2)。
bash
pip install playwright --break-system-packages
playwright install chromium
pip install asyncio --break-system-packages
将此写入 /tmp/metaadscraper.py:
python
import asyncio
import json
import re
import sys
from playwright.asyncapi import asyncplaywright
async def scrapeadlibrary(
search_query: str = None,
page_id: str = None,
country: str = ALL,
adtype: str = all, # all | politicalandissueads | housing_ads
active_status: str = active, # active | inactive | all
media_type: str = all, # all | image | meme | video | none
max_ads: int = 50
) -> list[dict]:
抓取 Meta 广告库以获取竞争对手广告。
必须提供 searchquery 或 pageid 之一。
results = []
# 构建 URL
base = https://www.facebook.com/ads/library/?
params = {
activestatus: activestatus,
adtype: adtype,
country: country,
mediatype: mediatype,
}
if search_query:
params[q] = search_query
params[searchtype] = keywordunordered
elif page_id:
params[viewallpageid] = pageid
params[search_type] = page
url = base + &.join(f{k}={v} for k, v in params.items())
async with async_playwright() as p:
browser = await p.chromium.launch(
headless=True,
args=[
--no-sandbox,
--disable-blink-features=AutomationControlled,
--disable-dev-shm-usage,
]
)
context = await browser.new_context(
viewport={width: 1440, height: 900},
useragent=Mozilla/5.0 (Macintosh; Intel Mac OS X 1015_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36,
locale=en-US,
)
# 隐身:隐藏 webdriver
await context.addinitscript(
Object.defineProperty(navigator, webdriver, { get: () => undefined });
)
page = await context.new_page()
print(f[阶段 1] 导航至: {url})
await page.goto(url, wait_until=networkidle, timeout=30000)
await page.waitfortimeout(3000)
# 滚动以加载更多广告
ads_loaded = 0
scroll_attempts = 0
while adsloaded < maxads and scroll_attempts < 20:
await page.evaluate(window.scrollTo(0, document.body.scrollHeight))
await page.waitfortimeout(2000)
# 统计广告卡片数量
adcards = await page.queryselectorall([data-testid=ad-card], .7jvw, [class*=x8t9es0])
adsloaded = len(adcards)
scroll_attempts += 1
if scroll_attempts % 5 == 0:
print(f[阶段 1] 已加载 {ads_loaded} 个广告...)
# 通过 JavaScript 提取广告数据
ads_data = await page.evaluate(
() => {
const ads = [];
// Meta 广告库在 div 中渲染广告;提取所有可见文本/图片数据
// 查找包含库 ID 的广告存档链接
const links = document.querySelectorAll(a[href*=ads/archive]);
const seen_ids = new Set();
links.forEach(link => {
const href = link.href;
const id_match = href.match(/id=(\d+)/);
if (idmatch && !seenids.has(id_match[1])) {
seenids.add(idmatch[1]);
// 向上查找广告容器
let container = link;
for (let i = 0; i < 8; i++) {
container = container.parentElement;
if (!container) break;
}
const getText = (el, fallback=) => el ? el.innerText.trim() : fallback;
const getAttr = (el, attr, fallback=) => el ? el.getAttribute(attr) || fallback : fallback;
ads.push({
adarchiveid: id_match[1],
adsnapshoturl: href,
page_name: getText(container?.querySelector([class*=page-name], strong)),
ad_body: getText(container?.querySelector([data-ad-preview=message], [class*=body])),
ad_title: getText(container?.querySelector([class*=title])),
cta_text: getText(container?.querySelector([class*=cta], button)),
image_url: getAttr(container?.querySelector(img[src*=fbcdn]), src),
started_running: getText(container?.querySelector([class*=started-running])),
platforms: Array.from(container?.querySelectorAll([class*=platform]) || []).map(el => el.innerText.trim()).filter(Boolean),
raw_text: container?.innerText?.substring(0, 500) || ,
});
}
});
return ads;
}
)
# 同时捕获网络请求以获取更丰富的数据
print(f[阶段 1] 从 DOM 中提取了 {len(ads_data)} 个广告)
results = adsdata[:maxads]
await browser.close()
return results
async def main():
query = sys.argv[1] if len(sys.argv) > 1 else Nike shoes
ads = await scrapeadlibrary(searchquery=query, maxads=20)
print(json.dumps(ads, indent=2, ensure_ascii=False))
if name == main:
asyncio.run(main())
bash
python /tmp/metaadscraper.py 竞争对手品牌名称
或者从 Python 内部运行(用于页面 ID 查找):
python
ads = await scrapeadlibrary(pageid=434174436675167, activestatus=active)
| 筛选条件 | 值 | 说明 |
|---|---|---|
| activestatus | active, inactive, all | active = 当前正在运行 |
| adtype |
使用时机:阶段 1 之后,或当用户需要支出/展示次数/人口统计数据时。
要求:Meta 开发者账户 + 访问令牌(参见下方设置)。
获取内容:支出范围、展示次数范围、人口统计分布(欧盟/政治类)、按地区投放、广告创意详情、预估受众规模。
该技能支持在以下平台通过对话安装:
帮我安装 SkillHub 和 meta-ad-spy-1775930532 技能
设置 SkillHub 为我的优先技能安装源,然后帮我安装 meta-ad-spy-1775930532 技能
skillhub install meta-ad-spy-1775930532
文件大小: 13.09 KB | 发布时间: 2026-4-12 10:34