Node.js:请求页面并允许页面在抓取之前构建

Node.js: requesting a page and allowing the page to build before scraping

我看到了一些将被问者引向其他图书馆的答案(例如 phantom.js),但我想知道是否完全有可能在 [=44= 中做到这一点]?

考虑下面的代码。它使用 request 请求网页,然后使用 cheerio 探索 dom 到 scrape 页面以获取数据。它运行完美,如果一切按计划进行,我相信它会输出我脑海中想象的文件。

问题page I am requesting 为了抓取,构建 table 我正在使用 ajax 或 jsonp 异步查看,我不完全确定 .jsp 页面是如何工作的。
所以在这里我试图找到一种方法来 "wait" 在我为我的新文件抓取数据之前加载这些数据。

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

// Go to the page in question
request({
    method: 'GET',
    url: 'http://www1.chineseshipping.com.cn/en/indices/cbcfinew.jsp'
}, function(err, response, body) {
    if (err) return console.error(err);
    // Tell Cherrio to load the HTML
    $ = cheerio.load(body);


    // Create an empty object to write to the file later    
    var toSort = {}

    // Itterate over DOM and fill the toSort object
    $('#emb table td.list_right').each(function() {
        var row = $(this).parent();     
        toSort[$(this).text()] = {
            [$("#lastdate").text()]: $(row).find(".idx1").html(),
            [$("#currdate").text()]: $(row).find(".idx2").html()            
        }
    });

    //Write/overwrite a new file
    var stream = fs.createWriteStream("/tmp/shipping.txt"); 
    var toWrite = "";

    stream.once('open', function(fd) {
        toWrite += "{\r\n"
        for(i in toSort){ 
            toWrite += "\t" + i + ": { \r\n";
                for(j in toSort[i]){
                    toWrite += "\t\t" + j + ":" + toSort[i][j] + ",\r\n";               
                }               
            toWrite += "\t" + "}, \r\n";
        }
        toWrite += "}"

        stream.write(toWrite)
        stream.end();
    });
});

预期结果是一个文本文件,其中的信息格式类似于 JSON 对象。

它应该看起来像这个的不同实例

"QINHUANGDAO - GUANGZHOU (50,000-60,000DWT)": { 
     "2016-09-29": 26.7,
     "2016-09-30": 26.8,
}, 

但由于名称是唯一不异步加载的东西,(日期和值是异步的)我得到了一个混乱的对象。

我试过了 实际上只是在代码的各个地方设置了一个setTimeout。如果脚本失败几次,只有能够承受 运行 脚本几次的开发人员才会接触该脚本。因此,虽然不理想,但即使是 setTimeout(最多 5 秒)也足够了。

事实证明设置超时不起作用。我怀疑一旦我请求该页面,当我收到它时,我就会被页面快照 "as is" 卡住,而且我实际上并没有在看我可以等待加载其动态内容的实时内容.

我想知道如何在包到来时拦截它们,但我对 HTTP 的了解还不够,不知道从哪里开始。

setTimeout 即使增加到一个小时也没有任何区别。这里的问题是您正在针对此 url 提出请求: http://www1.chineseshipping.com.cn/en/indices/cbcfinew.jsp

和他们的服务器 returns 支持 html 并且在这个 html 中有 js 和 css 导入。您的案例到此结束,您只需要 html 就可以了。相反,浏览器知道如何使用和解析 html 文档,因此它能够理解 javascript 脚本并 execute/run 它们,这正是您的问题。您的程序无法理解与 HTML 内容有关的内容。您需要找到或编写一个能够 运行 javascript 的抓取工具。我刚刚在 Whosebug 上发现了这个类似的问题: Web-scraping JavaScript page with Python

那里的人建议 https://github.com/niklasb/dryscrape,看来这个工具能够 运行 javascript。虽然它是用 python 写的。

您正在尝试抓取不包含您需要的数据的原始页面。 加载页面时,浏览器会评估其中包含的 JS 代码,并且此代码知道从何处以及如何获取数据。

第一个选项是评估相同的代码,就像 PhantomJS 那样。

另一个(您似乎对此感兴趣)是调查页面的网络 activity 并了解您应该执行哪些额外的请求来获取所需的数据。 在您的情况下,这些是:

http://index.chineseshipping.com.cn/servlet/cbfiDailyGetContrast?SpecifiedDate=&jc=jsonp1475577615267&_=1475577619626

http://index.chineseshipping.com.cn/servlet/allGetCurrentComposites?date=Tue%20Oct%2004%202016%2013:40:20%20GMT+0300%20(MSK)&jc=jsonp1475577615268&_=1475577620325

在两个请求中:

因此,取消 http://www1.chineseshipping.com.cn/en/indices/cbcfinew.jsp 处的 table 模板并执行两个额外的 requests,您将能够将它们组合成您在浏览器中看到的相同数据结构。