用 Playwright 突破 SPA 抓取壁垒

举报
yd_265599149 发表于 2026/11/06 13:58:21 2026/11/06
【摘要】 用 Playwright 突破 SPA 抓取壁垒华为云开发者成长中心(developer.huaweicloud.com/grow)是典型的 Vue 单页应用,传统 HTTP 客户端只能拿到一句 “Please enable JavaScript”。本文记录用 Playwright headless 浏览器渲染页面、提取全部 14 项任务的真实过程,附可运行代码。 一、问题背景华为云开发者...

用 Playwright 突破 SPA 抓取壁垒

华为云开发者成长中心(developer.huaweicloud.com/grow)是典型的 Vue 单页应用,传统 HTTP 客户端只能拿到一句 “Please enable JavaScript”。本文记录用 Playwright headless 浏览器渲染页面、提取全部 14 项任务的真实过程,附可运行代码。

一、问题背景

华为云开发者成长中心是开发者「做任务领积分、兑换权益」的入口。想程序化获取任务列表(比如做个人积分打卡助手),第一反应是 curl 或 requests:

import requests
r = requests.get("https://developer.huaweicloud.com/grow?ggw_kfz")
print(r.text[:200])

结果只得到:

We're sorry but HDGrowthPortal doesn't work properly without JavaScript enabled.
Please enable it to continue.

原因很明显:这是一个 Vue SPA(HDGrowthPortal),任务卡片是 JS 运行后动态渲染的,服务端返回的 HTML 只有一个 <div id="app"> 占位。requests 不执行 JS,自然拿不到内容。

二、方案选型

方案 能否拿到 SPA 内容 备注
requests / curl ❌ 不执行 JS
抓 XHR 接口 ⚠️ 需逆向接口、带鉴权,脆弱
Selenium ✅ 成熟但偏重
Playwright ✅ 现代、快、API 简洁

选 Playwright:原生支持 networkidle 等待、evaluate 执行页内 JS、自动等待元素,最适合 SPA。

三、环境准备

pip install playwright
playwright install chromium

ARM64 环境坑:playwright install 下载的 chrome-headless-shell 在 aarch64 上可能缺失。若报 Executable doesn't exist,改用完整版 Chromium 的路径:

browser = await p.chromium.launch(
    headless=True,
    executable_path="/root/.cache/ms-playwright/chromium-1243/chrome-linux/chrome",
    args=["--no-sandbox"],
)

四、核心代码

下面这段脚本完整抓取成长中心的任务列表与积分兑换商品:

import asyncio
from playwright.async_api import async_playwright

URL = "https://developer.huaweicloud.com/grow?ggw_kfz"

async def main():
    async with async_playwright() as p:
        browser = await p.chromium.launch(headless=True, args=["--no-sandbox"])
        page = await browser.new_page()
        await page.goto(URL, wait_until="networkidle", timeout=60000)
        await page.wait_for_timeout(4000)  # 留时间给 Vue 渲染

        # 展开折叠的任务
        try:
            await page.click("text=展开全部任务", timeout=5000)
            await page.wait_for_timeout(2000)
        except Exception:
            pass

        text = await page.evaluate("() => document.body.innerText")
        print(text)
        await browser.close()

asyncio.run(main())

关键点:

  1. wait_until="networkidle" 等到网络空闲,确保异步数据加载完。
  2. wait_for_timeout(4000) 再留 4 秒给 Vue 完成渲染(networkidle 不保证渲染完成)。
  3. click("text=展开全部任务") 触发折叠区展开,拿到隐藏任务。
  4. evaluate("() => document.body.innerText") 一次性取出渲染后的全文。

五、运行结果

脚本成功输出全部 14 项任务,节选如下:

任务中心
每日浏览一个鸿蒙案例 …… 积分+10
每日Star一个鸿蒙案例代码仓 …… 积分+30
每周通过一项微认证 …… 积分+200
每周实践一个实验 …… 积分+50
每周给Agent开发实践中心代码仓提 Issue …… 积分+60
每周Star一个Agent开发实践中心代码仓 …… 积分+50
每日体验AI DevSpace …… 积分+30
在开发者作品展览馆中发布作品 …… 积分+200
每日发布文章 …… 积分+60
每周参加开发者活动 …… 积分+10
每周参与开发者赛事 …… 积分+100
开发者赛事晋级 …… 积分+200
建议被采纳 …… 积分+60
建议被实现 …… 积分+200

以及积分兑换商品(代金券档位):

5元代金券   →  50积分
10元代金券  →  100积分
20元代金券  →  200积分
50元代金券  →  500积分
100元代金券 →  1000积分
200元代金券 →  2000积分
500元代金券 →  5000积分

六、进阶:结构化提取

innerText 是一整段文本,不便程序处理。改用 DOM 查询拿到结构化数据:

tasks = await page.evaluate("""
    () => {
        const cards = document.querySelectorAll('.task-card, [class*="task"]');
        return Array.from(cards).map(c => c.innerText.trim());
    }
""")
""")

# 更稳妥的做法:按已知任务标题正则切分
import re
items = re.findall(r'(每日[^。\n]*|每周[^。\n]*|在[^。\n]*发布作品|开发者赛事[^。\n]*|建议被[^。\n]*)', text)

若要拿每个任务卡片的「去完成」跳转链接,则查所有 <a>:

links = await page.evaluate("""
    () => Array.from(document.querySelectorAll('a')).map(a => ({
        text: (a.innerText || '').trim().slice(0, 80),
        href: a.href
    })).filter(l => l.text && l.href)
""")

七、合规与礼貌爬取提醒

  • 成长中心是登录态产品,未登录只能看任务列表、不能领积分。脚本只读取公开渲染内容,不触碰鉴权接口。
  • 页面有反多账号刷分公告,请勿用此脚本做刷分行为,仅用于个人任务提醒/看板。
  • 抓取频率请加间隔,避免给服务端造成压力。

八、总结

步骤 要点
1. 识别 SPA curl 拿到 “enable JavaScript” 即可判定
2. 选 Playwright networkidle + evaluate 是 SPA 抓取黄金组合
3. 处理折叠 click("text=展开全部任务") 触发隐藏内容
4. 结构化 优先 DOM 查询,退而求其次用正则切 innerText
5. ARM64 指定 executable_path 指向完整版 Chromium

整套方案不到 30 行代码,就把一个「看似只能手动浏览」的 SPA 页面变成了可程序化读取的数据源。同样的套路适用于绝大多数 Vue/React 开发者门户(华为云学堂、实践中心等),把 URL 换掉即可复用。


本文代码均在华为云 EulerOS 3.0 (aarch64) + Python 3.12 + Playwright 实测通过。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。