如何使用PhantomJS下载CSV文件

18

当我使用普通浏览器(Chrome)浏览网站A时,如果我点击网站A上的链接,Chrome会立即以CSV文件的形式下载报告。

当我检查服务器响应头时,我得到以下结果:

Cache-Control:private,max-age=31536000
Connection:Keep-Alive
Content-Disposition:attachment; filename="report.csv"
Content-Encoding:gzip
Content-Language:de-DE
Content-Type:text/csv; charset=UTF-8
Date:Wed, 22 Jul 2015 12:44:30 GMT
Expires:Thu, 21 Jul 2016 12:44:30 GMT
Keep-Alive:timeout=15, max=75
Pragma:cache
Server:Apache
Transfer-Encoding:chunked
Vary:Accept-Encoding
现在,我想使用PhantomJS下载并解析这个文件。我设置了pageonResourceReceived监听器,以查看Phantom是否会接收/下载该文件。
clientRequests.phantomPage.onResourceReceived = function(response) {
    console.log('Response (#' + response.id + ', stage "' + response.stage + '"): ' + JSON.stringify(response));
};
当我使用Phantom请求下载文件(即page.open('文件的URL'))时,我可以在Phantom日志中看到文件已被下载。以下是日志:
"contentType": "text/csv; charset=UTF-8",
    "headers": {
        "name": "Date",
        "value": "Wed, 22 Jul 2015 12:57:41 GMT"
    },
    "name": "Content-Disposition",
    "value": "attachment; filename=\"report.csv\"",
    "status":200,"statusText":"OK"

我已经收到了文件和它的内容,但是如何访问文件数据呢?当我打印当前PhantomJS的page对象时,我得到了页面A的HTML代码,而我不想要那个,我需要CSV文件,我需要使用JavaScript进行解析。


可能是在PhantomJs中下载作为POST请求响应附件的文件的重复问题。 - Artjom B.
卧槽,如果我告诉我的同事们给我每篇帖子点赞,我在 StackOverflow 和其他网络上这几年就会有600多个积分。当我看到一个小时内有3个赞时,我也感到惊讶,但这是好的不是坏的。如果你调查这个问题,太多人都面临着同样的问题,我想知道是否有人找到了好的解决方案。 - MrD
在写完我的评论后,我查看了您的帖子历史记录,并发现这里不太可能存在投票欺诈。尽管如此,我仍然觉得奇怪的是,在像[phantomjs]和[casperjs]这样的低投票标签中,您在不到10分钟内收到了3个赞成票。可能是因为[http],但我还是有点怀疑。 - Artjom B.
关于重复问题,我的链接选错了,但它仍然包含一个对你的问题有用的答案,只是被包裹在CasperJS代码中。我指的是PhantomJS分支的page.onFileDownload - Artjom B.
经过数天的调查,使用PhantomJS几乎是不可能完成的。虽然有一些解决方案,但并不那么优雅。只花了3个小时在CasperJS上,我就做到了,所以请使用CasperJS,不仅因为这个问题,CasperJS更直观、更易于使用。 - MrD
3个回答

12

我找到了解决PhantomJS的方法。在阅读这个讨论后,我发现一个jsfiddle通过jQuery的ajax方法下载url并将文件编码为base64。

我想要下载的文件是纯文本(CSV),所以我删除了编码函数。我的目标页面已经包含了jQuery,所以我不需要将jQuery注入到目标页面中

我的代码假设您已经使用PhantomJS打开了要从中下载文件的页面,并且该页面中有jQuery。在我这种情况下,我首先需要登录网站才能获得下载链接。

var fs = require('fs');

var page=this;

var result = page.evaluate(function() {

    var out;
    $.ajax({
        'async' : false,
        'url' : 'fullurltodownload.csv',
        'success' : function(data, status, xhr) {
            out = data;
        }
    });
    return out;

});

fs.write('mydownloadedfile.csv', result);

1
我认为CasperJS中的解决方案也基于这种方法,但它被封装在美丽的“download”和“base64encode”函数中。我很高兴你找到了一个好的解决方案,希望你的答案能帮助其他人。 - MrD
1
是的,不幸的是,由于Selenium和C#的要求,我被困在PhantomJS中。 - Matthew Lock
3
Ajax不支持cookies,因此这个解决方案在检查cookie的服务器端脚本上不起作用。 伤心 :d - davyzhang
这可能有助于关于cookies的问题:https://dev59.com/YWEi5IYBdhLWcg3wx-xs - Matthew Lock

8
经过多日的调查,我必须说有一些解决方案:
  • 在您的评估函数中,您可以进行AJAX调用以下载和编码您的文件,然后将此内容返回到幽灵脚本
  • 您可以使用一些可在GitHub页面上找到的自定义Phantom库
如果您需要使用PhantomJS下载文件,则应该远离PhantomJS并使用CasperJS。CasperJS基于PhantomJS,但它具有更好和更直观的语法和程序流程。
这里有一篇很好的文章解释了“为什么CasperJS比PhantomJS更好”。在这篇文章中,您可以找到关于文件下载的部分。
如何使用CasperJS下载CSV文件(即使服务器发送标题Content-Disposition:attachment; filename='file.csv也可以正常工作)
在这里,您可以找到一些可供下载的自定义csv文件:http://captaincoffee.com.au/dump/items.csv 为了使用CasperJS下载此文件,请执行以下代码:
var casper = require('casper').create();

casper.start("http://captaincoffee.com.au/dump/", function() {
    this.echo(this.getTitle())
});
casper.then(function(){
    var url = 'http://captaincoffee.com.au/dump/csv.csv';
    require('utils').dump(this.base64encode(url, 'get'));
});

casper.run();

上面的代码将下载http://captaincoffee.com.au/dump/csv.csv CSV文件,并将结果作为base64字符串打印出来。这样,您甚至不需要将数据下载到文件中,您可以将数据作为base64字符串保存。如果您明确想要将文件下载到文件系统中,您可以使用CasperJS中提供的download函数。

我刚刚安装了Casper的PHP封装程序:https://github.com/alwex/php-casperjs - 有什么想法可以在PHP中执行上述操作并下载CSV文件吗? - Wesley Jeftha

1

之前的两个答案假设您可以提前知道最终CSV文件的URL。如果链接指向一个HTML页面,该页面执行Javascript计算重定向到文件,并且您不想在PhantomJS之外评估该Javascript,则不会出现这种情况。那么您的选择是:

  1. 将PhantomJS放在上游代理后面,并使用该上游代理拦截下载URL(以及其预期的Cookie和Referer标头)- 但您必须小心地确定真实的下载URL,而不是一些随机数据'blob',如果页面也进行二进制XMLHttpRequests;
  2. 不使用PhantomJS,而是使用Headless Chrome,它可以自动保存下载的文件(或使用PyVirtualDisplay的Firefox,也可以设置为这样做,或等待Headless Firefox),并监视下载目录 - 但您必须能够自己弄清楚何时下载已完成(或使用上游代理监视其完成情况,但Headless Chrome / Firefox目前无法设置为忽略SSL证书,这意味着如果网站变得“安全”,则监视Headless Chrome / Firefox的请求比监视PhantomJS的请求困难得多,至少要等到Chromium issue 721739修复;您可以观察CONNECT请求,但如果它保持活动状态,您将无法确定传输是否已完成);
  3. 将PhantomJS放在上游代理后面,将所有未知内容类型更改为text/plain并删除Content-Disposition标头,以便您可以按正常方式从PhantomJS读取文件 - 这对于CSV文件有效,但对于其中包含0字节的二进制文件无效。

这些选项中的第一个(PhantomJS + 上游代理)如果上游代理能够监视PhantomJS发送到远程站点的Accept头,则会更加容易。至少在PhantomJS版本2.1.1中,主请求具有Accept:text / html,application / xhtml + xml,application / xml; q = 0.9,*/*;q=0.8,样式表请求具有Accept:text / css,*/*;q=0.1,所有其他请求(图像、脚本、XMLHttpRequest)默认为Accept:*/*,尽管可以被使用XMLHttpRequest.setRequestHeader()的站点覆盖。因此,如果上游代理看到一个包含text/htmlAccept头的请求,并且将请求传递给服务器会导致CSV文件或其他非HTML文档,则很有可能这是要保存的文档。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接