使用 Nodejs/Papa Parse 解析远程 CSV 文件?

Parse Remote CSV File using Nodejs / Papa Parse?

我目前正在解析来自 Node 应用程序的远程 csv 产品提要,并且想使用 Papa Parse 来执行此操作(因为我过去在浏览器中使用它取得过成功)。

解析爸爸Github:https://github.com/mholt/PapaParse

我最初的尝试和网络搜索都没有找到具体的实现方式。 Papa 自述文件说 Papa Parse 现在与 Node 兼容,因此 Baby Parse(用于提供某些 Node 解析功能)已被贬低。

这里有一个 link 文档的节点部分,供将来遇到此问题的任何人使用:https://github.com/mholt/PapaParse#papa-parse-for-node

从该文档段落看来,Node 中的 Papa Parse 可以解析可读流而不是文件。我的问题是;

Is there any way to utilize Readable Streams functionality to use Papa to download / parse a remote CSV in Node some what similar to how Papa in the browser uses XMLHttpRequest to accomplish that same goal?

为了未来的可见性 对于那些搜索该主题(并避免重复类似问题)尝试使用此处描述的远程文件解析功能的人:http://papaparse.com/docs#remote-files 将导致您的控制台出现以下错误:

"Unhandled rejection ReferenceError: XMLHttpRequest is not defined"

我已经在官方存储库上打开了一个问题,当我了解更多需要解决的问题时,我会更新这个问题。

我正在添加这个答案(并会随着我的进步而更新)以防其他人仍在调查这个问题。

以前的用户好像都是先下载文件再处理。这应该不是必需的,因为 Papa Parse 应该能够处理读取流,并且应该可以将 'http' GET 通过管道传输到该流。

这是某人讨论我正在尝试做的事情并返回下载文件然后解析它的一个实例:https://forums.meteor.com/t/processing-large-csvs-in-meteor-js-with-papaparse/32705/4

注意:在上面讨论了 Baby Parse,现在 Papa Parse 与 Node Baby Parse 一起工作已经被贬低了。

下载文件解决方法

While downloading and then Parsing with Papa Parse is not an answer to my question, it is the only workaround I have as of now and someone else may want to use this methodology.

我要下载然后解析的代码目前看起来像这样:

// Papa Parse for parsing CSV Files
var Papa = require('papaparse');
// HTTP and FS to enable Papa parse to download remote CSVs via node streams.
var http = require('http');
var fs = require('fs');

var destinationFile = "yourdestination.csv";

var download = function(url, dest, cb) {
  var file = fs.createWriteStream(dest);
  var request = http.get(url, function(response) {
    response.pipe(file);
    file.on('finish', function() {
      file.close(cb);  // close() is async, call cb after close completes.
    });
  }).on('error', function(err) { // Handle errors
    fs.unlink(dest); // Delete the file async. (But we don't check the result)
    if (cb) cb(err.message);
  });
};

download(feedURL, destinationFile, parseMe);

var parseMe = Papa.parse(destinationFile, {
  header: true,
  dynamicTyping: true,
  step: function(row) {
    console.log("Row:", row.data);
  },
  complete: function() {
    console.log("All done!");
  }
});

好的,所以我想我有一个答案。但我想只有时间才能证明一切。 请注意,我的文件是带有制表符分隔符的 .txt。

var fs = require('fs');
var Papa = require('papaparse');
var file = './rawData/myfile.txt';
// When the file is a local file when need to convert to a file Obj.
//  This step may not be necissary when uploading via UI
var content = fs.readFileSync(file, "utf8");

var rows;
Papa.parse(content, {
    header: false,
    delimiter: "\t",
    complete: function(results) {
        //console.log("Finished:", results.data);
    rows = results.data;
    }
});

实际上您可以使用 lightweight stream transformation library called scramjet - parsing CSV straight from http stream is one of my main examples. It also uses PapaParse 来解析 CSV。

您上面写的所有内容,以及其间的任何转换,只需几行即可完成:

const {StringStream} = require("scramjet");
const request = require("request");

request.get("https://srv.example.com/main.csv")   // fetch csv
    .pipe(new StringStream())                       // pass to stream
    .CSVParse()                                   // parse into objects
    .consume(object => console.log("Row:", object))  // do whatever you like with the objects
    .then(() => console.log("all done"))

在您自己的示例中,您将文件保存到磁盘,即使使用 PapaParse 也不需要这样做。

Http(s) 实际上有一个可读流作为回调中的参数,所以这是一个简单的解决方案

 try {
    var streamHttp = await new Promise((resolve, reject) =>
       https.get("https://example.com/yourcsv.csv", (res) => {
          resolve(res);
       })
    );
 } catch (e) {
    console.log(e);
 }

 Papa.parse(streamHttp, config);

经过大量修改后,我终于得到了一个使用异步流且没有其他库(fs/request 除外)的工作示例。它适用于远程和本地文件。

我需要创建一个数据流,以及一个 PapaParse 流(使用 papa.NODE_STREAM_INPUT 作为 papa.parse() 的第一个参数),然后将数据通过管道传输到 PapaParse 流中。需要为 PapaParse 流 上的 datafinish 事件 实现事件侦听器。然后,您可以在 finish 事件的处理程序中使用解析后的数据。

参见下面的示例:

const papa = require("papaparse");
const request = require("request");

const options = {/* options */};

const dataStream = request.get("https://example.com/myfile.csv");
const parseStream = papa.parse(papa.NODE_STREAM_INPUT, options);

dataStream.pipe(parseStream);

let data = [];
parseStream.on("data", chunk => {
    data.push(chunk);
});

parseStream.on("finish", () => {
    console.log(data);
    console.log(data.length);
});

对于 CSV 中的每一行,parseStream 的 data 事件发生 运行 一次(尽管我不确定这种行为是否得到保证)。希望这对某人有所帮助!

要使用本地文件而不是远程文件,您可以做同样的事情,除了 dataStream 将使用 fs 创建:

const dataStream = fs.createReadStream("./myfile.csv");

(您可能希望使用 path.join__dirname 来指定相对于文件所在位置的路径,而不是相对于文件所在位置的路径 运行)

const Papa = require("papaparse");
const { StringStream } = require("scramjet");
const request = require("request");

const req = request
  .get("https://example.com/yourcsv.csv")
  .pipe(new StringStream());

Papa.parse(req, {
  header: true,
  complete: (result) => {
    console.log(result);
  },
});