2

我正在尝试网页抓取,因为我可以看到很多潜力可以用它来做有趣的事情。我花了几个小时研究我需要什么,我决定使用带有“request”和“cheerio”模块的 node.js 来执行抓取。

所以对于第一个项目,我想我会尝试从这个随机句子生成器站点获取一个随机句子:http ://watchout4snakes.com/wo4snakes/Random/RandomSentence

标记看起来相对简单,这是我感兴趣的一点:

<div class="resultBox">
    <table class="centeredResult">
        <tbody><tr>
            <td>
                <span id="result">An amateur regret slights the lust outside his contentious century.</span>
            </td>
        </tr>
    </tbody></table>

</div>

所以我想要的位是跨度(显然在检查实际页面时会有所不同),我编写了以下 Javascript 文件并在节点中运行它:

var request = require("./node_modules/request/");
    cheerio = require('./node_modules/cheerio/');

request('http://watchout4snakes.com/wo4snakes/Random/RandomSentence', function(err, resp, body){

    if(!err && resp.statusCode == 200){


        console.log("connected...\n");

        var $ = cheerio.load(body);


        console.log($('#result').html());
    }
    else console.log("Failed To Connect...");
});

我收到通知,所以我做了一些检查并确定我已经正确地抓取了页面的数据。所以现在我要做的就是选择#resultID 字段中的文本。但是,我只是给了一个空白空间,事实上,如果我让cheerio 模块打印该区域的实际标记,我会得到一个<span ID="result"></span>没有随机句子的内部。

我最初的猜测是,在随机句子脚本完成运行之前,节点正在抓取标记。但我不知道诊断发生了什么的方法,所以有人知道吗?

4

4 回答 4

1

是的,您的直觉是正确的,因为请求模块在随机句子脚本完成运行之前正在抓取标记。如果你打印出来body,你会看到它包含:

<table class="centeredResult">
    <tr>
        <td>
            <span id="result"></span>
        </td>
    </tr>
</table> 

事实上,请求模块永远不会在获取的页面上执行任何 JavaScript。

如果您需要 JavaScript 在您正在抓取的页面上运行,我建议您查看像phantomjs这样的无头浏览器,它使您能够通过 JavaScript API 与页面交互。

于 2014-08-11T23:12:14.407 回答
1

因此,在修改了我的脚本之后,这就是我最终得到的结果:

var page = require('webpage').create();

console.log("connecting...");   


page.open("http://watchout4snakes.com/wo4snakes/Random/RandomSentence", function(){

    console.log('connected');

    var content = page.content;

    var phrase = page.evaluate(function() {

        return document.getElementById("result").innerHTML;

    });

    console.log(phrase);

});

感谢 go-oleg 提示使用 phantomjs,看起来好像无头浏览器方法允许脚本在抓取 HTML 内容之前运行。然后我使用 page.evaluate() 从页面中提取句子。

看起来好像 Phantomjs 在我的系统上有一些问题。phantom.exit() 上没有任何进程退出,根据谷歌搜索,这与 Nvidia 图形驱动程序有关。此外,脚本相当慢,因为它等待页面的所有元素加载连接可能需要长达 10 秒,这对于迭代过程来说不是很好。但我设法得到了这个句子,所以我会从这里开始,谢谢你的信息!

于 2014-08-12T19:02:34.347 回答
1

在页面上看:

<script>
    (function ($) {
        $(document).ready(function () {
            var options = {
                target: '#result',
                beforeSubmit: function () {
                $('#result').empty();
                $.fnWait();
            },
            success: function () {
                $.unblockUI();
            }
        };
            $('#frmSentence').ajaxForm(options)
                             .find('input[type=submit]')
                             .click();
        });
    })(jQuery);

看起来好像#evidence span充满了AJAX。当您的库加载页面时,它不会执行 Javascript,因此它不会加载报价。

如果您只是尝试查询他们从中提取它的同一页面,这可能是最简单的。否则,您将需要使用能够为您执行页面上的 javascript 的东西——比如 Selenium 或类似的东西。

于 2014-08-11T23:13:37.077 回答
1

在浏览器中加载页面,然后查看网络请求。在cheerio 停止加载DOM 之后,您会看到该语句是异步加载的。有一个POSTtohttp://watchout4snakes.com/wo4snakes/Random/NewRandomSentence返回带有引号的纯文本字符串 (Content-Type:text/html; charset=utf-8),然后将其插入到 DOM 中。

我不知道cheerio,但是您可以(a)使用计时器等待几秒钟,或者(b)切换到显式等待某事的wd ,一旦加载该DOM元素就会触发。

于 2014-08-11T23:16:11.487 回答