如何在CasperJS中下载多个PDF文件

时间:2015-05-25 10:44:52

标签: pdf phantomjs casperjs

我想使用CasperJS从网站下载.pdf文件列表。它(大多数)工作,但最后几个PDF文件被截断。以下是相关的代码段:

casper.then(function a09() {
    for (var index = bill_count-1; index>=0; --index) {
        casper.then(downloadOnePdf(index));
    };
});

function downloadOnePdf(index) {
    return function() {
        var selector = 'div#myAjaxDiv tbody tr:nth-child(' + (index+1) + ') form a'
        casper.log('click ' + selector, 'info');
        casper.click(selector);
        // casper.waitForResource(/\/Document/); -- see note
        // casper.waitForText('%%EOF'); -- see note
    };
};

casper.run();

通过Web代理执行此代码,我可以看到.pdf文件作为响应主体到达。但是,最后两个或三个文件没有完全下载 - 它们被截断 - 我在响应标题'客户端关闭连接之前看到一条消息,然后才收到整个响应'。

这支持了我的预感,即在完全下载pdf之前casperjs代码退出。我尝试添加

casper.waitForResource(/\/Document/)

在我的代码中,但没有帮助。我也尝试过:

casper.waitForText('%%EOF')

但即使我能在回复正文中看到'%% EOF',它也会超时。

所以问题是:确保整个.pdf到达响应主体的正确方法是什么?

ps:细心的读者会注意到我实际上并没有保存.pdf数据。这是另一天的问题......

1 个答案:

答案 0 :(得分:1)

事实证明,原始帖子中的截断文件只是较大问题的一小部分,但解决方案并不困难。让我解释一下......

CasperJS无法直接访问响应正文,因此OP casper.click()在返回响应中PDF数据的表单上的方法不胜一筹到处都是。 AFAIK,无法在本地文件系统上实际保存这些数据。

相反,您需要调用casper.download()来POST您在单击表单时获得的相同表单。这种方法的关键点很简单,尽管记录很少:

  • 以适当的方式加载包含该表的DOM 对于这种情况。
  • 使用CSS的nth-child()伪类从表中选择单个行。
  • 使用casper.getFormValues()构建一个POSTable表单。
  • 使用casper.download()发布表单并保存结果数据。

相关代码摘录如下。我希望有人会觉得这很有用(即使从现在起几个月后我就有人了)。

// ========== helpers
// The following helpers assume that the current DOM contains the table with the download forms

// Return the number of PDFs available for download.  
function countPDFs() {
    return casper.getElementsAttribute('div#myAjaxDiv tbody tr form input[name="id"]', 'value').length
}

// Get the invoice ID of the index'th invoice: 0 <= index < countPDFs().
function getInvoiceID(index) {
    return casper.getElementAttribute('div#myAjaxDiv tbody tr:nth-child(' + (index+1) + ') form input[name="id"]', 'value');
}

// Return the index'th form for downloading a .pdf: 0 <= index < countPDFs().
function getDownloadForm(index) {
    return casper.getFormValues('div#myAjaxDiv tbody tr:nth-child(' + (index+1) + ') form')
}

// Download the index'th PDF file, saving it to <target_directory>/<invoiceID>.pdf.  
// 0 <= index < countPDFs().
function downloadOnePDF(index, target_directory) {
    var 
      url = 'https://example.com/Invoice',
      target = target_directory + '/' + getInvoiceID(index) + '.pdf',
      data = getDownloadForm(index);

    casper.then(function d01() {
        casper.log('downloading pdf ' + index + ' to ' + url);
        casper.download(url, target, 'POST', data);
    });
}

// ========== casper agenda items

// (initial steps omitted)

// Click on "Invoice" button to bring up the Invoice page
var invoice_link_css = 'a#mnuInvoiceSubmit'
casper.then(function a06() {
    casper.click(invoice_link_css)
});

// Make sure the Invoice page has loaded, as evidenced by the presence of the
// bill history table.
casper.then(function a07() {
    casper.waitForSelector('div#myAjaxDiv tbody');
});

// Download each .pdf file referenced in the bill history table.
casper.then(function a08() {
    var pdf_count = countPDFs();

    casper.echo('found ' + pdf_count + ' past bill' + ((pdf_count == 1) ? '' : 's'));
    for (var index = pdf_count-1; index>=0; --index) {
        downloadOnePDF(index, target_directory);
    }
});

casper.run();

此方法将每个.pdf文件保存到本地文件系统,并且不会在OP中出现任何截断问题。