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 并了解您应该执行哪些额外的请求来获取所需的数据。
在您的情况下,这些是:
和
在两个请求中:
_
是decache参数,防止缓存。
jc
是 JS 包装函数的名称,应使用结果 (https://en.wikipedia.org/wiki/JSONP) 调用
因此,取消 http://www1.chineseshipping.com.cn/en/indices/cbcfinew.jsp 处的 table 模板并执行两个额外的 request
s,您将能够将它们组合成您在浏览器中看到的相同数据结构。
我看到了一些将被问者引向其他图书馆的答案(例如 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 并了解您应该执行哪些额外的请求来获取所需的数据。 在您的情况下,这些是:
和
在两个请求中:
_
是decache参数,防止缓存。jc
是 JS 包装函数的名称,应使用结果 (https://en.wikipedia.org/wiki/JSONP) 调用
因此,取消 http://www1.chineseshipping.com.cn/en/indices/cbcfinew.jsp 处的 table 模板并执行两个额外的 request
s,您将能够将它们组合成您在浏览器中看到的相同数据结构。