You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用CasperJS遍历指定可点击元素并保存对应加载页面的HTML?

解决CasperJS遍历并点击含特定关键词的元素,保存加载后页面的问题

你的核心问题在于CasperJS的主上下文和浏览器DOM上下文是完全分离的——你在evaluate()里拿到的jQuery元素对象没法直接在Casper主代码里操作,得先把元素的可追踪信息(比如数量、索引)提取出来,再在Casper中逐个处理。下面是完整的解决方案,我会拆解关键细节:

完整修改代码

var casper = require('casper').create({ 
    clientScripts: ["/var/www/html/project/public/js/jquery-3.3.1.min.js"],
    waitTimeout: 10000 // 增加超时时间,确保页面有足够加载时长
}); 
var fs = require('fs'); 
var utils = require('utils'); // 用于日志输出,方便调试

// 工具函数:确保目标文件夹存在,避免写入失败
function ensureFolderExists(path) {
    if (!fs.exists(path)) {
        fs.makeDirectory(path, {recursive: true});
    }
}

casper.start('https://m.1xbet.co.ke/en/line/Football/', function () {
    this.echo('初始页面加载完成,开始定位目标元素...');
    
    // 第一步:在浏览器上下文获取符合条件的元素总数
    var targetElementCount = this.evaluate(function () {
        // 定义不区分大小写的contains选择器
        $.expr[":"].contains = $.expr.createPseudo(function (arg) {
            return function (elem) {
                return $(elem).text().toUpperCase().indexOf(arg.toUpperCase()) >= 0;
            };
        });
        // 返回符合条件的元素数量,后续用索引逐个定位
        return $("#events-betting").find("li.events__item_head:contains(World cup)").length;
    });

    this.echo(`找到 ${targetElementCount} 个含"World cup"的赛事板块`);

    // 第二步:遍历每个目标元素,执行点击+保存操作
    for (var i = 0; i < targetElementCount; i++) {
        // 用立即执行函数保存循环索引,避免异步回调中索引值异常
        (function(currentIndex) { 
            casper.then(function() {
                // 在浏览器上下文触发第currentIndex个元素的点击事件
                this.evaluate(function(idx) {
                    var $targetItem = $("#events-betting")
                        .find("li.events__item_head:contains(World cup)")
                        .eq(idx);
                    $targetItem.click(); // 触发div的内联点击监听
                }, currentIndex);

                // 等待页面加载完成(替换成点击后出现的专属元素选择器)
                this.waitForSelector('.events__item_content', function() {
                    this.echo(`第 ${currentIndex+1} 个板块加载完成,开始保存`);
                    
                    // 生成日期格式的保存路径
                    var date = new Date();
                    var year = date.getFullYear();
                    var month = date.getMonth() + 1;
                    var day = date.getDate();
                    var folderName = `${year}-${month}-${day}`;
                    var baseSavePath = `destination/${folderName}/1xbet`;
                    ensureFolderExists(baseSavePath);
                    var saveFilePath = `${baseSavePath}/worldcup-${currentIndex+1}.html`;

                    // 保存当前页面的完整HTML
                    fs.write(saveFilePath, this.getHTML(), "w");
                    this.echo(`已成功保存到:${saveFilePath}`);

                    // 回到赛事列表页,准备处理下一个元素
                    this.back();
                    // 等待列表页重新加载完成
                    this.waitForSelector("#events-betting", function() {
                        this.echo('回到赛事列表,准备处理下一个元素');
                    });
                }, function() {
                    this.echo(`第 ${currentIndex+1} 个板块加载超时,跳过该条目`);
                });
            });
        })(i);
    }
});

casper.run(function() {
    this.echo('所有任务执行完毕!');
    this.exit();
});

关键细节说明

  • 上下文分离处理:evaluate()中的代码运行在浏览器环境,Casper主代码运行在Node环境,两者无法直接共享DOM对象。所以我们先获取元素总数,再通过索引逐个定位元素。
  • 异步循环问题:Casper的then()是异步执行的,必须用立即执行函数保存循环的currentIndex,否则所有回调都会使用循环的最后一个索引值。
  • 点击触发方式:对于带内联JavaScript监听的div,直接在浏览器上下文调用$target.click()是最可靠的,比Casper自带的click()方法更适配自定义监听逻辑。
  • 页面加载等待:使用waitForSelector()等待点击后页面出现的专属元素,确保页面完全加载后再保存HTML,避免保存未渲染完成的内容。
  • 文件夹预创建:添加ensureFolderExists函数自动创建层级文件夹,防止因路径不存在导致的写入失败。
  • 返回原页面:如果点击后跳转到新页面,处理完成后用back()回到列表页;如果是局部刷新,可调整为等待原列表元素重新出现。

你可以根据目标页面的实际加载情况,修改waitForSelector()中的选择器(比如替换成点击后出现的赛事详情元素),确保等待逻辑准确。

内容的提问来源于stack exchange,提问作者Cepheus

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:53:15