我正在使用PhantomJS来抓取一些网站,因此用r提取信息。我正在关注this教程。一切都适用于单个页面,但我找不到任何关于如何自动化多个页面的简单教程。 到目前为止我的实验:
var countries = [ "Albania" ,"Afghanistan"];
var len = countries.length;
var name1 = ".html";
var add1 = "http://www.kluwerarbitration.com/CommonUI/BITs.aspx?country=";
var country ="";
var name ="";
var add="";
for (i=1; i <= len; i++){
country = countries[i]
name = country.concat(name1)
add = add1.concat(name1)
var webPage = require('webpage');
var page = webPage.create();
var fs = require('fs');
var path = name
page.open(add, function (status) {
var content = page.content;
fs.write(path,content,'w')
phantom.exit();
});
}
运行代码时似乎没有任何错误,该脚本仅为第二个国家/地区创建一个html文件,其中包含有关我感兴趣的小表的页面例外的所有信息。 / p>
我尝试从similar questions收集一些信息。然而,也因为我无法找到一个简单的可重复的例子,我不明白我做错了什么。
答案 0 :(得分:4)
主要问题似乎是你太早退出。您正在循环中创建多个page
实例。由于PhantomJS是异步的,因此对page.open()
的调用立即存在,并且执行下一个for循环迭代。
for循环非常快,但Web请求很慢。这意味着即使加载第一页,您的循环也会完全执行。这也意味着加载的第一个页面也将退出PhantomJS,因为您在每个phantom.exit()
回调中调用了page.open()
。我怀疑第二个URL由于某种原因更快,因此总是写的。
var countFinished = 0,
maxFinished = len;
function checkFinish(){
countFinished++;
if (countFinished + 1 === maxFinished) {
phantom.exit();
}
}
for (i=1; i <= len; i++) {
country = countries[i]
name = country.concat(name1)
add = add1.concat(country)
var webPage = require('webpage');
var page = webPage.create();
var fs = require('fs');
var path = name
page.open(add, function (status) {
var content = page.content;
fs.write(path, content,'w')
checkFinish();
});
}
问题是你在没有清理的情况下创建了很多page
个实例。你完成后应该关闭它们:
for (i=1; i <= len; i++) {
(function(i){
country = countries[i]
name = country.concat(name1)
add = add1.concat(country)
var webPage = require('webpage');
var page = webPage.create();
var fs = require('fs');
var path = name
page.open(add, function (status) {
var content = page.content;
fs.write(path, content,'w');
page.close();
checkFinish();
});
})(i);
}
由于JavaScript具有功能级范围,因此您需要使用IIFE来保留对page
回调中正确的page.open()
实例的引用。有关详细信息,请参阅此问题:Q: JavaScript closure inside loops – simple practical example
如果您之后不想清理,则应在所有这些URL上使用相同的page
实例。我已经在这里做了一个答案:A: Looping over urls to do the same thing
答案 1 :(得分:0)
鉴于我对js的知识非常有限,我想到了解决这个问题的方法。我仍然对正确解决问题感兴趣,但我预计这将需要相当长的时间。
目前我通过在R中做一些实验性的东西得到了我想要的东西。而不是在js中运行循环,我用R来编写多个单js代码,因此绕过了“phantomjs是异步问题”。
技巧包括使用带参数quote = F的write.table导出js代码块,并使用.js作为文件扩展名,以便将其正确识别为js文件。我想这种解决方法对其他类似任务的适用性有限,但它可能会帮助某人。评论非常感谢。
countries <- c("Afghanistan", "Albania", "Algeria")
for (i in unique(countries)){
df <- data.frame(lines=character(11),
stringsAsFactors=FALSE)
outputline <- paste("var path = '", i, ".html'" , sep="")
inputline <- paste("page.open('http://www.kluwerarbitration.com/CommonUI/BITs.aspx?country=", i ,"', function (status) {", sep="")
df$lines[1] <- "var webPage = require('webpage');"
df$lines[2] <- "var page = webPage.create();"
df$lines[3] <- "var fs = require('fs');"
df$lines[4] <- ""
df$lines[5] <- outputline
df$lines[6] <- ""
df$lines[7] <- inputline
df$lines[8] <- " var content = page.content;"
df$lines[9] <- " fs.write(path,content,'w')"
df$lines[10] <- " phantom.exit();"
df$lines[11] <- "});"
write.table(df, paste(i, ".js", sep = ""), sep=" ", quote=F, row.names=F, col.names=F)
}
library(rvest)
library(stringr)
library(plyr)
library(dplyr)
library(ggvis)
library(knitr)
options(digits = 4)
#run all individual javascript files
index <- 1
for (i in countries){
javacode <- paste0("./phantomjs", sep=" ", countries, ".js")
system(javacode[index])
index <- index + 1
}