【问题标题】:Scraping multiple URLs by looping in PhantomJS通过在 PhantomJS 中循环来抓取多个 URL
【发布时间】:2015-12-06 17:30:01
【问题描述】:

我正在使用 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 文件,其中包含为我感兴趣的小桌子制作的页面异常的所有信息。

我试图从similar questions 收集一些信息。但是,也因为我找不到一个简单的可重现的例子,我不明白我做错了什么。

【问题讨论】:

  • 我会给出与您链接的答案相同的答案。也许你应该显示出了什么问题。由于 PhantomJS 是异步的,因此您不能真正以微不足道的方式使用循环。顺便说一句,你还没有说 this 代码出了什么问题。它不起作用这一事实不足以让我们确定发生了什么问题。
  • 我对此很陌生,主要问题是我不太了解。运行代码时我似乎没有收到任何错误,该脚本仅为第二个国家/地区创建了一个 html 文件,其中包含有关我感兴趣的小表的页面异常的所有信息。我将继续阅读其他问题,谢谢

标签: javascript for-loop web-scraping phantomjs


【解决方案1】:

主要问题似乎是您退出得太早了。您正在循环中创建多个 page 实例。由于 PhantomJS 是异步的,对 page.open() 的调用立即存在,并执行下一个 for 循环迭代。

for 循环非常快,但 Web 请求很慢。这意味着您的循环甚至在第一页加载之前就已完全执行。这也意味着加载的第一个页面也将退出 PhantomJS,因为您在每个 page.open() 回调中调用 phantom.exit()。我怀疑由于某种原因第二个 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.open() 回调中保留对正确 page 实例的引用。有关更多信息,请参阅此问题:Q: JavaScript closure inside loops – simple practical example

如果您不想在之后进行清理,那么您应该对所有这些 URL 使用相同的 page 实例。我已经有一个关于这样做的答案:A: Looping over urls to do the same thing

【讨论】:

  • 非常感谢,这帮助很大!我会等待接受答案,直到我有一个工作代码。
  • 我的代码中有一个愚蠢的错误: add = add1.concat(name1) 是错误的,应该是 add = add1.concat(country) 。正如我在评论中提到的,这就是唯一被抓取的网页中缺少信息的原因。我会尝试编辑它
【解决方案2】:

鉴于我对 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
}

【讨论】:

    猜你喜欢
    • 2018-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多