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

含async函数仍报await只能在async内使用错误的JS爬虫代码排查

解决"await can only be declared inside async function"报错问题

嘿,我看到你遇到的问题了!虽然你把最外层的函数声明成了async,但问题出在**client.query的回调函数**上——这个回调函数是普通函数,不是async的,而你在里面用了await,所以才会触发这个报错。

问题根源拆解

你的代码结构大概是这样的:

(async () => {
  // 这层是async函数没问题
  client.query('SQL语句', (err, res) => {
    // 👉 这个回调函数是普通函数!不是async的!
    // 所以里面的await都会报错
    await puppeteer.launch(); 
  });
})();

await必须直接在async函数内部使用,而回调函数是一个独立的作用域,和外层的async IIFE没关系。


两种解决方案

方案1:把回调函数改成async函数

最简单的修改是给client.query的回调加上async关键字,这样回调内部就可以正常用await了:

(async () => {
  client.query("SELECT DISTINCT url,article_id FROM public.content_paraarticle", async (err, res, fields) => {
    if (err) throw err;
    
    for(var i=0;i<res.rows.length;i++) {
      // 现在这里的await就合法了
      const browser = await puppeteer.launch({headless: false});
      const page = await browser.newPage();
      page.setViewport({ width: 1280, height: 926 });
      
      var str = res.rows[i].article_id + '.png';
      var url = res.rows[i].url;
      console.log(str, 'taken');

      await Promise.race([
        page.goto(url, {waitUntil: 'load'}), // 注意:原代码这里写死了一个url,要改成循环里的url!
        new Promise(x => setTimeout(x, 60000)),
      ]);

      const image = await page.$('body > div.container-main > div.jscroll > div > div > div > section > div > div > div > div:nth-child(2) > div.lead-img-cont > div > picture > img');
      console.log('screenshot started to get taken');
      
      if (image) { // 加个判断,避免元素找不到报错
        await image.screenshot({ 
          path: str, 
          omitBackground: true, 
        });
        console.log('screenshot taken');
      } else {
        console.log(`No image found for article ${res.rows[i].article_id}`);
      }

      await browser.close();
    }
    client.end();
  });
})();

方案2:用Promise化写法(更推荐,适合新手学习现代JS)

回调嵌套容易陷入"回调地狱",更优雅的方式是把client.query转成Promise风格的函数,用await替代回调。Node.js自带的util.promisify可以帮我们做到这一点:

const util = require('util');
const puppeteer = require('puppeteer');

(async () => {
  try {
    // 把client.query转成返回Promise的函数
    const queryAsync = util.promisify(client.query).bind(client);
    // 用await执行SQL查询,不用写回调
    const res = await queryAsync("SELECT DISTINCT url,article_id FROM public.content_paraarticle");
    
    // 用for...of循环更简洁,避免var的作用域问题
    for(const row of res.rows) {
      // 解构赋值,让代码更清晰
      const { article_id, url } = row;
      const str = `${article_id}.png`; // 模板字符串比字符串拼接更方便
      
      console.log(str);
      console.log('taken');

      const browser = await puppeteer.launch({headless: false});
      const page = await browser.newPage();
      page.setViewport({ width: 1280, height: 926 });

      await Promise.race([
        page.goto(url, {waitUntil: 'load'}), // 这里用循环里的url,不是硬编码的固定值
        new Promise(x => setTimeout(x, 60000)),
      ]);

      const image = await page.$('body > div.container-main > div.jscroll > div > div > div > section > div > div > div > div:nth-child(2) > div.lead-img-cont > div > picture > img');
      console.log('screenshot started to get taken');
      
      if (image) {
        await image.screenshot({ 
          path: str, 
          omitBackground: true, 
        });
        console.log('screenshot taken');
      } else {
        console.log(`No image found for article ${article_id}`);
      }

      await browser.close();
    }
  } catch (err) {
    // 统一处理所有错误,避免程序崩溃
    console.error('程序出错了:', err);
  } finally {
    // 不管成功还是失败,都关闭数据库连接
    client.end();
  }
})();

额外的新手友好提示

  1. 避免硬编码URL:原代码里page.goto写死了一个固定URL,这应该是个失误,要改成循环里的row.url才能爬取对应文章的页面。
  2. 用const/let替代var:var有作用域提升的问题,容易引发bug,现代JS推荐用const(值不变时)或let(值需要改变时)。
  3. 错误处理很重要:用try/catch包裹异步逻辑,能捕获所有步骤的错误,方便排查问题;finally块确保数据库连接一定会关闭,避免资源泄漏。
  4. 元素存在性判断:如果页面找不到你指定的选择器,page.$会返回null,直接调用screenshot会报错,所以加个if (image)的判断很有必要。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 07:44:37