Puppeteer基础入门--元素操作基础

元素操作基础

课程目标

  • ✅ 掌握元素定位的多种方式
  • ✅ 提取元素属性与文本内容
  • ✅ 实现百度热搜实时抓取
  • ✅ 理解DOM操作的安全边界

元素定位方法论

1. 选择器类型对比

类型示例适用场景
CSS选择器#main .title常规静态元素定位
XPath//div[@class="hot"]复杂层级关系定位
Text选择器text/="热搜"模糊文本匹配(需启用实验功能)

2. 核心API详解

// 单元素查询(返回ElementHandle)
const element = await page.$('selector')

// 多元素查询(返回数组)
const elements = await page.$$('.list-item')

// XPath定位
const xpathElement = await page.$x('//div[contains(@class, "hot")]')

// 结合eval的快捷方式
const textContent = await page.$eval('#title', el => el.textContent)

实战:百度热搜抓取

步骤分解

const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  
  try {
    // 导航到百度热搜页
    await page.goto('https://top.baidu.com/board', {
      waitUntil: 'networkidle2'
    });

    // 等待目标元素加载
    await page.waitForSelector('.c-single-text-ellipsis', {
      timeout: 5000
    });

    // 提取热搜文本
    const hotSearchList = await page.$$eval('.c-single-text-ellipsis', 
      elements => elements.map(el => ({
        title: el.textContent.trim(),
        link: el.href
      }))
    );

    console.log('实时热搜榜:', JSON.stringify(hotSearchList, null, 2));

    // 保存数据到文件
    const fs = require('fs');
    fs.writeFileSync('./data/hot-search.json', JSON.stringify(hotSearchList));

  } catch (err) {
    console.error('抓取失败:', err);
  } finally {
    await browser.close();
  }
})();

关键技术点解析

  1. waitForSelector:确保目标元素加载完成
  2. $$eval:批量处理元素集合
  3. DOM环境隔离:注意无法直接访问外部变量

元素操作安全指南

1. 防御性编程实践

// 使用try-catch处理元素不存在情况
try {
  const element = await page.waitForSelector('#not-exist', { timeout: 3000 });
} catch (err) {
  console.log('元素未找到,执行备用方案');
}

// 元素存在性检测
const isExist = await page.$('selector') !== null;

2. 内存管理

// 及时释放元素句柄
const element = await page.$('#large-element');
await element.dispose(); // 显式释放内存

// 避免循环内累积句柄
for (const item of list) {
  const el = await page.$(item.selector);
  // 处理元素...
  await el.dispose();
}

常见问题排查

❌ Element is not attached to the DOM

  • 原因:元素被动态移除

  • 解决方案:使用page.waitForFunction检测元素稳定性

    await page.waitForFunction(
      selector => document.querySelector(selector).offsetParent !== null,
      {},
      '#dynamic-element'
    );
    

❌ Execution context was destroyed

  • 原因:页面跳转后访问旧元素

  • 解决方案:在回调中重新获取元素

    page.on('framenavigated', async () => {
      currentElement = await page.$('#refresh-element');
    });
    

❌ Node is either not visible or not an HTMLElement

  • 原因:元素不可交互

  • 解决方案:滚动到可视区域

    await element.scrollIntoView();
    await element.click();
    

课后任务

  1. 抓取知乎热榜前10话题的标题与关注数
  2. 实现微博热搜的自动翻页抓取
  3. 设计元素超时重试机制(最多3次)

🛠️ 高级挑战:尝试用XPath重写百度热搜抓取逻辑


下节预告:表单交互与截图 - 学习自动化填写表单与页面快照技巧 📸

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Penk是个码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值