You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Node.js调用Python爬虫脚本获取数据延迟问题求助

问题分析与解决方案

这问题我之前帮朋友排查过类似的,核心就是你把异步操作当成同步逻辑来写了spawn启动Python脚本是异步执行的,你的crawlData函数直接返回temp的时候,Python脚本可能还在爬数据,temp自然是初始的undefined或者null

为什么当前代码失效?

  • py.stdout.on('data')on('end')都是异步事件回调,只有当Python脚本输出数据或者结束时才会触发。
  • 你的crawlData函数是同步执行的,执行到return temp时,这些回调根本还没运行,所以返回的是初始值undefined

下面给你两种常用的解决方案,从传统回调到现代的Promise/async-await都有:


方案一:使用回调函数(基础版)

修改crawling.js,让crawlData接受一个回调函数,当Python脚本完成并返回数据时,调用回调传递结果:

const { spawn } = require('child_process');
const encoding = require('your-encoding-module'); // 确保你正确引入了encoding模块

exports.crawlData = (callback) => {
  // 确保totalUrl和gubun是已定义的变量
  const data = [totalUrl, gubun];
  const py = spawn('python', ['dataCrawler.py']);

  // 监听Python的标准输出
  py.stdout.on('data', (result) => {
    try {
      // 转码并解析JSON
      const dataArr = JSON.parse(encoding.convert(result, 'utf-8').toString());
      callback(null, dataArr);
    } catch (err) {
      // 解析失败时传递错误
      callback(new Error(`解析数据失败: ${err.message}`), null);
    }
  });

  // 监听Python的错误输出,方便调试
  py.stderr.on('data', (errChunk) => {
    callback(new Error(`Python脚本报错: ${errChunk.toString()}`), null);
  });

  // 监听进程退出,处理异常退出情况
  py.on('close', (code) => {
    if (code !== 0) {
      callback(new Error(`Python进程异常退出,退出码: ${code}`), null);
    }
  });

  // 传递参数给Python脚本
  py.stdin.write(JSON.stringify(data));
  py.stdin.end();
};

然后在server.js里这样调用:

const crawler = require("./crawling");

crawler.crawlData((err, resultArr) => {
  if (err) {
    console.error('爬虫执行出错:', err);
    return;
  }
  console.log('nodeserver:', resultArr);
});

方案二:使用Promise + Async/Await(现代版)

用Promise包装异步操作,配合async/await让代码更像同步逻辑,可读性更好:

修改crawling.js

const { spawn } = require('child_process');
const encoding = require('your-encoding-module');

exports.crawlData = () => {
  return new Promise((resolve, reject) => {
    const data = [totalUrl, gubun];
    const py = spawn('python', ['dataCrawler.py']);
    let outputBuffer = '';

    // 收集所有输出(避免大输出被分片)
    py.stdout.on('data', (chunk) => {
      outputBuffer += encoding.convert(chunk, 'utf-8').toString();
    });

    // 处理Python脚本的错误输出
    py.stderr.on('data', (errChunk) => {
      reject(new Error(`Python脚本运行错误: ${errChunk.toString()}`));
    });

    // 进程结束后统一解析结果
    py.on('close', (code) => {
      if (code !== 0) {
        reject(new Error(`Python进程异常退出,退出码: ${code}`));
        return;
      }
      try {
        // 去除输出前后的空白字符,避免JSON解析失败
        const result = JSON.parse(outputBuffer.trim());
        resolve(result);
      } catch (parseErr) {
        reject(new Error(`解析Python输出失败: ${parseErr.message}`));
      }
    });

    py.stdin.write(JSON.stringify(data));
    py.stdin.end();
  });
};

修改server.js

const crawler = require("./crawling");

// 用async函数包裹,才能使用await
async function runCrawler() {
  try {
    const resultArr = await crawler.crawlData();
    console.log('nodeserver:', resultArr);
  } catch (err) {
    console.error('爬虫执行失败:', err);
  }
}

runCrawler();

必须修改的Python脚本细节

你的Python脚本当前的print(result)可能输出非标准JSON格式(比如Python列表的单引号),会导致Node端JSON解析失败,必须改成标准JSON输出:

import sys
import json # 一定要导入json模块

def crawling(url, gubun, page_count):
    idx = 0
    result = []
    jsonData = {}
    for i in range(1, page_count + 1):
        # ... 你的爬虫逻辑 ...
    return result

def main():
    lines = sys.stdin.readlines()
    paraFromServer = json.loads(lines[0])
    url = paraFromServer[0]
    gubun = paraFromServer[1]
    page_count = 5 # 这里确保page_count有定义,或者从参数传入
    result = crawling(url, gubun, page_count)
    # 用json.dumps输出标准JSON字符串
    print(json.dumps(result))

if __name__ == "__main__":
    main()

额外的调试与优化建议

  • 超时处理:如果爬虫可能长时间运行,可以给Promise加个定时器,超过N秒就终止Python进程并报错。
  • 参数校验:在Node端和Python端都对传入的参数做校验,避免无效参数导致的错误。
  • 日志记录:给关键步骤加日志,方便排查问题,比如Python脚本的输入输出、Node端的进程状态。

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

火山引擎 最新活动