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

Puppeteer写入MySQL报connection.query is not a function错误

报错核心原因
  • 你引入的是mysql2/promise异步Promise版客户端,mysql.createConnection()调用后返回的是Promise对象,没有加await等待连接实例创建完成,拿到的是未resolve的Promise,自然不存在query方法,调用就会触发你看到的报错。
  • Promise版mysql2的query方法不支持回调函数写法,你现在混用了回调版API的调用方式,就算连接正常也会出问题。
  • 采集值返回null有两个原因:一是你写的page.evaluate内部语句多了冗余括号存在语法错误;二是没有做元素存在性判断,选择器匹配不到元素时直接取innerText/src属性就会返回null。
  • 你没有合理管理数据库连接生命周期,既没有在爬取前确认连接建立,也没有在任务结束后关闭连接,长期运行会占满数据库连接数。
修复方案

按以下步骤改代码即可正常入库:

  1. 把数据库连接初始化移到整个爬取逻辑的最外层,加await等待连接建立,整个爬取任务复用同一个连接即可,不要在循环里重复创建连接。
  2. 所有page.evaluate内的DOM取值逻辑先判断元素是否存在,匹配不到就返回空字符串,避免null值入库,修正原来evaluate里多写的冗余括号语法错误。
  3. 把connection.query改成Promise版的await调用写法,去掉回调函数,加try/catch捕获插入异常。
  4. 所有爬取任务结束后,先关闭数据库连接,再退出脚本。
修复后可运行的代码参考
const mysql = require("mysql2/promise")
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth')
puppeteer.use(StealthPlugin())
const AdblockerPlugin = require('puppeteer-extra-plugin-adblocker')
puppeteer.use(AdblockerPlugin({ blockTrackers: true }))
const UserAgent = require('user-agents');

(async () => {
  // 初始化数据库连接,等待连接建立完成
  const connection = await mysql.createConnection({
    host: "localhost",
    port: 3306,
    user: "...",
    password: "...",
    database: "..."
  });
  console.log("数据库连接成功");

  for (let step = 0; step <= 5; step++) {
    const userAgent = new UserAgent({
      deviceCategory: "desktop"
    });
    const cleanUA = userAgent.toString();
    const browser = await puppeteer.launch({
      headless: false,
      defaultViewport: {width: 1920, height: 1080},
      args: ['--disable-infobars', '--user-agent=' + cleanUA]
    });
    const page = await browser.newPage();
    console.log("正在抓取第" + (step+1) + "页");
    const url = 'https://example/test?page=' + (step+1);
    await page.goto(url, {waitUntil: 'domcontentloaded'});
    await page.waitForSelector("/* 替换成你实际要等待加载的选择器 */");
    // 点击分段的逻辑替换成你自己的选择器
    await page.$$eval("/* 替换成你实际的点击目标选择器 */", els => els.forEach(el => el.click()));

    for (let i = 0; i <= 35; i++){
      // 所有取值逻辑加元素存在判断,修正语法错误
      const title = await page.evaluate((i) => {
        const el = document.querySelector("/* 替换成title对应的选择器 */");
        return el ? el.innerText.trim() : '';
      },i); 
      console.log('title:', title)

      const subtitle = await page.evaluate((i) => {
        const el = document.querySelector("/* 替换成subtitle对应的选择器 */");
        return el ? el.innerText.trim() : '';
      },i); 
      console.log('subtitle:', subtitle)

      const item_price = await page.evaluate((i) => {
        const el = document.querySelector("/* 替换成价格对应的选择器 */");
        return el ? el.innerText.trim() : '';
      },i); 
      console.log('price:', item_price)

      const ros_info = await page.evaluate((i) => {
        const el = document.querySelector("/* 替换成ros_info对应的选择器 */");
        return el ? el.innerText.trim() : '';
      },i); 
      console.log('ros_info:', ros_info)

      const img = await page.evaluate((i) => {
        const el = document.querySelector("/* 替换成图片对应的选择器 */");
        return el ? el.src : '';
      },i); 
      console.log('img:', img)

      const p = await page.evaluate((i) => {
        const el = document.querySelector("/* 替换成p字段对应的选择器 */");
        return el ? el.innerText.trim() : '';
      },i); 
      console.log('p:', p)

      const info_items = await page.evaluate((i) => {
        const el = document.querySelector("/* 替换成info_items对应的选择器 */");
        return el ? el.innerText.trim() : '';
      },i); 
      console.log('info_items:', info_items)

      const link = await page.evaluate((i) => {
        const el = document.querySelector("/* 替换成链接对应的选择器 */");
        return el ? el.href : '';
      },i); 
      console.log('link:', link)

      const theopeninfo2 = await page.evaluate((i) => {
        const els = Array.from(document.querySelectorAll("/* 替换成openinfo对应的选择器 */"));
        return els.map(x => x.innerText.trim()).join("\n");
      },i);
      console.log('theopeninfo2:', theopeninfo2)

      // 数据库插入用await写法,加异常捕获
      try {
        const [results] = await connection.query(
          "INSERT INTO `b_info`(`title`, `subtitle`, `item_price`, `ros_info`, `img`, `p`, `info_items`, `link`, `theopeninfo2`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
          [title , subtitle , item_price , ros_info , img , p , info_items , link , theopeninfo2 ]
        );
        console.log("数据插入成功,插入ID:", results.insertId);
      } catch (err) {
        console.error("数据插入失败:", err);
      }
    }
    await browser.close();
  }
  // 所有任务结束关闭数据库连接
  await connection.end();
  console.log("全部爬取任务完成");
})();
注意事项
  • 代码里所有标注/* 替换成XX对应的选择器 */的位置,换成你实际业务里的CSS选择器即可。
  • 如果爬取量比较大,可以在循环里加适当的延时,避免请求太频繁被站点封IP。
  • 提前确认数据库表的字段类型、长度和你采集的内容匹配,避免长文本插入时触发字段长度超限报错。

内容的提问来源于stack exchange,提问作者Gilad Alexander Kaveh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 14:54:23