如何写入并立即读取文件nodeJS

时间:2014-02-10 22:30:30

标签: json node.js web-scraping

我必须在特定页面中的脚本标记内获取一个json ...所以我不能使用常规的抓取技术,比如cheerio。 简单的出路,将文件(下载页面)写入服务器然后使用字符串操作读取它以提取json(有几个)工作并保存到我的数据库中。

问题是我对nodeJS来说太新了,并且无法使代码工作,我认为我在尝试完全写入之前读取该文件,并且如果在获取之前读取它的时间[对象对象] ......

这是我到目前为止所拥有的......

var http = require('http');

var fs = require('fs');
var request = require('request');

var localFile = 'tmp/scraped_site_.html';
var url = "siteToBeScraped.com/?searchTerm=foobar"

// writing
var file = fs.createWriteStream(localFile);

var request = http.get(url, function(response) {
    response.pipe(file);
});

//reading
var readedInfo = fs.readFileSync(localFile, function (err, content) {
    callback(url, localFile);
    console.log("READING: " + localFile);
    console.log(err);
});

3 个答案:

答案 0 :(得分:2)

首先,我认为你应该明白出了什么问题。

http请求操作是异步的。这意味着http.get()中的回调代码将在未来的某个时间运行,但fs.readFileSync由于其同步特性将在http请求实际发送到将执行的后台线程之前执行并完成它,因为它们都是在通常所说的(相同)刻度中调用的。此外,fs.readFileSync返回一个值,不使用回调。

即使用fs.readFile替换fs.readFileSync,代码仍可能无法正常工作,因为在从套接字完全读取http响应并将其写入磁盘之前,readFile操作可能会执行。

我强烈建议您阅读:stackoverflow question和/或Understanding the node.js event loop

调用文件读取的正确位置是响应流已完成写入文件时的情况,如下所示:

var request = http.get(url, function(response) {
    response.pipe(file);
    file.once('finish', function () {            
        fs.readFile(localFile, /* fill encoding here */, function(err, data) {
            // do something with the data if there is no error
        });         
    });
});

当然,这是一种非常原始且不推荐的编写异步代码的方法,但这是另一种讨论。

话虽如此,如果您下载文件,将其写入磁盘然后再将其全部读回内存进行操作,您可以放弃文件部分并立即将响应读入字符串。您的代码将看起来像这样(这可以通过多种方式实现):

var request = http.get(url, function(response) {
    var data = '';

    function read() {       
        var chunk;
        while ( chunk = response.read() ) {
            data += chunk;      
        }       
    }

    response.on('readable', read);

    response.on('end', function () {
        console.log('[%s]', data);
    });
});

你应该做什么IMO是创建一个转换流,它将从响应中删除你需要的所有数据,同时不消耗太多内存并产生这个看起来更优雅的代码:

var request = http.get(url, function(response) {
    response.pipe(yourTransformStream).pipe(file)
});

然而,实现此转换流可能会稍微复杂一些。因此,如果你是一个节点初学者并且你不打算下载大文件或许多小文件而不是将整个内容加载到内存中并对它进行字符串操作可能会更简单。

有关转换流的更多信息:

最后,看看你是否可以使用任何已经存在的百万个node.js抓取工具:-)在npm上查看these search results

答案 1 :(得分:0)

根据http module帮助'get'不会返回响应正文

这是从同一页面上的请求示例中修改的

您需要做的是在传递给http.request的回调(函数)中处理响应,以便在准备就绪时调用它(异步)

var http = require('http')
var fs = require('fs')

var localFile = 'tmp/scraped_site_.html'
var file = fs.createWriteStream(localFile)

var req = http.request('http://www.google.com.au', function(res) {
  res.pipe(file)
  res.on('end', function(){
    file.end()

    fs.readFile(localFile, function(err, buf){
      console.log(buf.toString())
    })

  })
})

req.on('error', function(e) {
  console.log('problem with request: ' + e.message)
})

req.end();

EDIT 我更新了示例,以便在创建文件后读取该文件。这通过在响应的结束事件上进行回调来关闭管道然后它可以重新打开文件以进行读取。或者你可以使用

 req.on('data', function(chunk){...})

在数据到达时处理数据而不将其放入临时文件

答案 2 :(得分:0)

我的印象是,您通过从下载包含HTML的文件的流中读取js对象将其序列化为JSON。这是可行的但很难。很难知道你何时找到了搜索表达式,因为如果你进行解析,那么你就不会知道你是否只收到了上下文而你却找不到你要找的东西,因为它被分成2个或很多部分从未作为一个整体进行过分析。

您可以尝试这样的事情:

http.request('u/r/l',function(res){
   res.on('data',function(data){
      //parse data as it comes in
   }
});

这允许您在数据进入时读取。如果您将脚本标记中的内容累积到单个字符串中,然后解析其中的对象,则可以处理它以保存到光盘,数据库,甚至解析它。