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

9jyewag0  于 2023-01-15  发布在  其他
关注(0)|答案(7)|浏览(181)

我目前正在解析一个节点应用程序的远程csv产品提要,并希望使用Papa Parse来完成此任务(因为我过去在浏览器中成功地使用了它)。
吉土伯爸爸:https://github.com/mholt/PapaParse
我最初的尝试和网络搜索还没有找到确切的方法。Papa自述文件说Papa Parse现在与Node兼容,因此Baby Parse(曾经提供一些Node解析功能)已经贬值。
这里有一个指向文档节点部分的链接,供将来遇到此问题的任何人使用:https://github.com/mholt/PapaParse#papa-parse-for-node
从这个文档段落来看,节点中的Papa Parse似乎可以解析一个可读的流,而不是一个文件。
是否有任何方法可以利用Readable Streams功能来使用Papa在Node中下载/解析远程CSV,这与浏览器中的Papa使用XMLHttpRequest来实现相同目标的方式类似?

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

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

gajydyqb

gajydyqb1#

经过大量的修改,我终于得到了一个使用异步流的工作示例,它不需要额外的库(除了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);
});

parseStream的data事件碰巧为CSV中的每一行运行一次(尽管我不确定这种行为是否得到保证)。

    • 要使用本地文件**而不是远程文件,您可以执行相同的操作,只是dataStream将使用fs创建:
const dataStream = fs.createReadStream("./myfile.csv");

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

kfgdxczn

kfgdxczn2#

好的,我想我有答案了。但是我想只有时间能证明一切。注意我的文件是带有制表符分隔符的.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;
    }
});
rqqzpn5f

rqqzpn5f3#

实际上你可以使用lightweight stream transformation library called scramjet-直接从http流解析CSV是我的一个主要例子,它也使用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也不需要这样做。

ddhy6vgd

ddhy6vgd4#

我正在添加这个答案(并将随着我的进展更新它),以防其他人仍在研究这个问题。
看起来以前的用户已经结束了下载文件,然后处理它。这应该是没有必要的,因为爸爸解析应该能够处理读流,它应该可以管道'http' GET到该流。
下面是一个例子,有人讨论我正在尝试做什么,并回落到下载文件,然后解析它:https://forums.meteor.com/t/processing-large-csvs-in-meteor-js-with-papaparse/32705/4

  • 注意:在上面的婴儿解析是讨论,现在爸爸解析与节点婴儿解析已经贬值。*

下载文件解决方法

虽然下载然后用Papa Parse解析并不能回答我的问题,但这是我目前唯一的解决方法,其他人可能想使用这种方法。
我的代码下载,然后解析目前看起来像这样:

// 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!");
  }
});
093gszye

093gszye5#

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);
dsf9zpds

dsf9zpds6#

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);
  },
});
piok6c0g

piok6c0g7#

大卫Liao的解决方案对我很有效,我使用本地文件时做了一些调整。他没有提供如何解决节点中的文件访问的示例,如果您在控制台中收到Error: ENOENT: no such file or directory消息。
为了测试您的实际工作目录并了解您必须将控制台日志的路径指向何处,下面的代码使我更好地了解了文件位置:console.log(process.cwd()).

const fs = require('fs');
const papa = require('papaparse');
const request = require('request');
const path = require('path');

const options = {
  /* options */
};

const fileName = path.resolve(__dirname, 'ADD YOUR ABSOLUTE FILE LOCATION HERE');
const dataStream = fs.createReadStream(fileName);
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);
});

相关问题