You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何仅在数据变更时用Puppeteer爬取公开API

按需爬取产品数据的实现方案

方案一:利用HTTP缓存头(ETag/Last-Modified)

  • 原理:服务端通常会为API资源返回ETag(资源唯一标识)或Last-Modified(最后修改时间),下次请求时带上对应的请求头,若资源未变更,服务器返回304 Not Modified,此时无需解析和存储数据。
  • 落地步骤:
    1. 在SQLite中新增page_cache表,字段包括query_item、start、etag、last_modified、updated_at,用query_item和start作为联合主键。
    2. 每次请求分页URL前,从缓存表中查询该分页对应的ETag/Last-Modified值。
    3. 通过Puppeteer的page.setExtraHTTPHeaders设置请求头:
      • 有ETag时,设置If-None-Match: <ETag值>
      • 有Last-Modified时,设置If-Modified-Since: <Last-Modified值>
    4. 请求后判断响应状态:
      • 若返回304,直接跳过后续解析和存储操作
      • 若返回200,提取响应头中的ETag/Last-Modified更新缓存表,再正常处理数据

方案二:本地哈希缓存

  • 原理:对分页返回的原始JSON内容生成哈希值(如MD5),存储该哈希;下次爬取时先获取原始内容生成新哈希,对比一致则跳过处理。
  • 落地步骤:
    1. 新增page_cache表,字段包括query_item、start、content_hash、updated_at。
    2. 请求分页URL后,先获取原始的document.body.innerText,用哈希算法生成内容哈希。
    3. 查询缓存表中该分页的旧哈希,若新旧哈希一致则跳过解析存储;若不一致,更新缓存哈希并处理数据。
    4. 注意:如果API返回的JSON包含无关动态字段(如当前时间戳),需先过滤这些字段再生成哈希,避免误判。

方案三:数据库增量对比优化

  • 原理:先爬取分页的核心变更字段(productId、price、salePrice),与数据库现有数据对比,仅当核心字段变化时,再更新该产品的完整信息。
  • 落地步骤:
    1. 在fetchData中先提取精简数据:只保留productId、price、salePrice。
    2. 遍历精简数据,与数据库中对应productId的记录对比:
      • 价格无变化则跳过该产品
      • 价格变化则用当前分页的完整数据更新数据库
    3. 这种方式能减少不必要的数据库写入操作,降低资源消耗。

代码修改示例(结合ETag方案)

// 初始化缓存表(SQLite)
async function initCacheTable() {
  const db = await getDatabaseConnection(); // 替换成你的数据库连接方法
  await db.run(`
    CREATE TABLE IF NOT EXISTS page_cache (
      query_item TEXT,
      start INTEGER,
      etag TEXT,
      last_modified TEXT,
      updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
      PRIMARY KEY (query_item, start)
    )
  `);
  await db.close();
}

async function fetchData(browser, queryItem, start) {
    if (!browser) {
        console.error('Browser instance is not initialized');
        return;
    }
    const page = await browser.newPage();
    const db = await getDatabaseConnection();

    try {
        // 查询缓存的ETag/Last-Modified
        const cacheRow = await db.get(`
          SELECT etag, last_modified FROM page_cache 
          WHERE query_item = ? AND start = ?
        `, [queryItem, start]);

        // 设置请求头
        const extraHeaders = {};
        if (cacheRow?.etag) {
          extraHeaders['If-None-Match'] = cacheRow.etag;
        }
        if (cacheRow?.last_modified) {
          extraHeaders['If-Modified-Since'] = cacheRow.last_modified;
        }
        await page.setExtraHTTPHeaders(extraHeaders);

        const url = `${baseURL}?query=${queryItem}&start=${start}`;
        const response = await page.goto(url, { waitUntil: 'networkidle0' });

        // 检查响应状态,304则跳过
        if (response.status() === 304) {
          console.log(`分页${start}无变化,跳过处理`);
          return { totalCount: 0, items: [] };
        }

        // 更新缓存表
        const newEtag = response.headers()['etag'];
        const newLastModified = response.headers()['last-modified'];
        await db.run(`
          INSERT OR REPLACE INTO page_cache (query_item, start, etag, last_modified)
          VALUES (?, ?, ?, ?)
        `, [queryItem, start, newEtag, newLastModified]);

        // 解析并存储数据
        const data = await page.evaluate(() => JSON.parse(document.body.innerText));
        let filteredItems = []; // 修正原代码初始化为对象的错误
        if(data.raw.itemList.items){
            filteredItems = data.raw.itemList.items.map(item => ({
                link: item.link,
                displayName: item.displayName,
                productId: item.productId,
                price: item.price,
                salePrice: item.salePrice,
                salePercentage: item.salePercentage,
                imageUrl: item.image.src
            }));
        }     

        await saveToDatabase(filteredItems);

        return {
            totalCount: data.raw.itemList.count,
            items: filteredItems
        };
    } catch (error) {
        console.error('Error fetching data:', error);
    } finally {
        await page.close();
        await db.close();
    }
}

补充说明

  • 若API不支持ETag/Last-Modified,优先选用哈希缓存方案。
  • 建议每周执行一次全量爬取,避免缓存失效导致的漏更问题。

内容的提问来源于stack exchange,提问作者Nico

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 16:33:22