загрузка файла, который приходит в виде вложения в ответ на запрос POST в PhantomJs

Я хочу загрузить файл CSV, он генерируется нажатием кнопки через запрос POST. Я изо всех сил исследовал форумы casperJs и phantomJS и вернулся с пустыми руками. В обычном браузере, таком как Firefox, диалоговое окно загрузки браузера появляется после почтового запроса. Как справиться с этим случаем в PhantomJS

TTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Vary: Accept-Encoding
Server: Microsoft-IIS/7.5
Content-disposition: attachment;filename=ExportData.csv
X-AspNet-Version: 2.0.50727
X-Powered-By: ASP.NET
Date: Fri, 19 Apr 2013 23:26:40 GMT
Content-Length: 65183

person vumaasha    schedule 22.04.2013    source источник


Ответы (4)


Я нашел способ сделать это с помощью casperjs (он должен работать только с phantomjs, если вы реализуете функцию загрузки с помощью XMLHttpRequest, но я не пробовал).

Я оставлю вам рабочий пример, который пытается загрузить самый последний PDF-файл с эта страница. Когда вы нажимаете ссылку для загрузки, запускается некоторый код javascript, который генерирует некоторые скрытые поля ввода, которые затем отправляются в POST.

Что мы делаем, так это заменяем функцию onsubmit формы, чтобы она отменяла отправку и получала назначение формы (действие) и все ее поля. Мы используем эту информацию позже, чтобы сделать фактическую загрузку.

var casper=require('casper').create();
casper.start("https://sede.gobcan.es/tributos/jsf/publico/notificaciones/comparecencia/ultimosanuncios.jsp", function() {

    var theFormRequest = this.page.evaluate(function() {
        var request = {}; 
        var formDom = document.forms["resultadoUltimasNotif"];
        formDom.onsubmit = function() {
            //iterate the form fields
            var data = {};
            for(var i = 0; i < formDom.elements.length; i++) {
               data[formDom.elements[i].name] = formDom.elements[i].value;
            }
            request.action = formDom.action;
            request.data = data;
            return false; //Stop form submission
        }

        //Trigger the click on the link.
        var link = $("table.listado tbody tr:first a");
        link.click();

        return request; //Return the form data to casper
    });

    //Start the download
    casper.download(theFormRequest.action, "downloaded_file.pdf", "POST", theFormRequest.data);
});

casper.run(); 

Примечание: вы должны запустить его с параметром --ignore-ssl-errors, так как используемый ими ЦС отсутствует в списке ЦС по умолчанию в вашем браузере.

casperjs --ignore-ssl-errors=true downloadscript.js
person julianjm    schedule 07.05.2014
comment
спасибо, мне наконец удалось загрузить CSV для моего банка, чтобы он работал с вашим подходом - person vinzenzweber; 04.01.2015
comment
Это сработало для меня, но в моем случае нажатие на ссылку не отправило форму, потому что она была обработана javascript. Мне пришлось вызвать $(#theForm).sumit(), чтобы форма действительно отправилась. - person Michael J. Lee; 30.07.2016

Вы можете прослушать page.resource.received событие и download() файл при получении:

casper.on('page.resource.received', function(resource) {
    if (resource.stage !== "end") {
        return;
    }
    if (resource.url.indexOf('ExportData.csv') > -1) {
        this.download(resource.url, 'ExportData.csv');
    }
});
person NiKo    schedule 09.05.2013
comment
Также не уверен, как это должно работать, если не упомянутый шаг 0 заключается в компиляции ветки разработки фантома вместо текущей? - person dtanders; 17.03.2014
comment
Нет, должен работать со стабильными фантомными файлами по умолчанию, так как метод download() реализован на стороне каспера (он использует XHR за сценой). - person NiKo; 25.03.2014
comment
Кажется, что resource.stage === 'end' нужно добавить (&&) в if для правильной работы. - person Stan; 08.09.2014
comment
Есть ли в любом случае ожидание этой функции около 4 секунд? - person Sentient07; 21.05.2015
comment
Привет @NiKo, это работает с кликами на основе javascript? Имеется в виду, что файл создается после клика по ссылке через javascript? Если нет, какой пример того, как этого можно достичь? Спасибо - person user1749672; 23.11.2015
comment
Это будет работать только для запросов, отправленных методом HTTP GET. Если щелчок запускается с помощью POST - как в вопросе OP - вам нужно сделать this.download(resource.url, 'ExportData.csv', POST). Но одного этого недостаточно, так как вам нужно отправить данные POST в качестве 4-го аргумента для загрузки (...) - person nchaud; 11.05.2016

@julianjm aproach - это почти решение, но в моем случае у меня не было правильного имени формы, чтобы заменить отправку формы.

Итак, я нашел другое решение, используя бета-версию phantomjs:

Существует бета-версия phantomjs 2.0, которая включает обработчик событий, решающий эту проблему.

Это все еще бета-версия, поэтому отладки нет.

Поэтому я разработал клики и обработку страниц в релизной версии, а затем изменил фантомную версию, чтобы загрузка работала.

 casper.start('http://www.website.com.br/', function() {
    this.page.onFileDownload = function(status){console.log('onFileDownload(' + status + ')'); 

//SYSTEM WILL DETECT THE DOWNLOAD, BUT YOU WILL HAVE TO NAME THE FILE BY YOURSLEF!!
return "ContactList_08-25-14.csv"; };

    });
      casper.then(function() {
        //DO YOUR STUFF HERE TO CLICK ON THE DOWNLOAD LINK. 
      });
    casper.run();

Загрузить: Phantom 2.0 BETA

Скачайте exe, переименуйте релизную версию phantom.exe в phantom.bkp.exe и вставьте эту версию 2.0 на место. Затем в casperjs вам нужно будет добавить несколько строк в начале casperjs/bin/bootstrap.js

 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 *
 */
var system = require('system');
    var argsdeprecated = system.args;
    argsdeprecated.shift();
    phantom.args = argsdeprecated;

также прокомментируйте проверку версии (тот же файл):

(function(version) {
        // required version check
      /*  if (version.major !== 1) {
            return __die('CasperJS needs PhantomJS v1.x');
        } if (version.minor < 8) {
            return __die('CasperJS needs at least PhantomJS v1.8 or later.');
        }
        if (version.minor === 8 && version.patch < 1) {
            return __die('CasperJS needs at least PhantomJS v1.8.1 or later.');
        } */
    })(phantom.version);

Помните, это настройка!!.

Таким образом, эти строки в начальной загрузке вызовут проблемы, если вы хотите запустить фантомную версию выпуска или slimerjs.

Итак, РАЗРАБОТАЙТЕ ПО ВЫПУСКНОЙ ВЕРСИИ, а затем настройте эту версию, чтобы ее можно было загрузить. Если вам нужно отладить, вам придется удалить строки bootstrap.js

person LeoPucciBr    schedule 29.06.2015

Мне приходится иметь дело с сайтом, написанным с какой-то инфраструктурой ASP.Net, которая отправляет значительное количество данных POST при каждом запросе (около 100 КБ данных, из которых около 95 никогда не меняются между запросами - очевидно, связано с состоянием окна просмотра) .

Однако ни один метод, который я мог найти, не работал у меня. Я просмотрел перехватывая XHR, я даже нашел кого-то, кто работа с той же структурой (по крайней мере, судя по селекторам), но с более простым случаем, вдохновленным этим вопросом. Я обнаружил, что в прошлом это было невозможно с помощью PhantomJS.

Моя проблема в том, что щелчок по кнопке запускает цепочку запросов AJAX, кульминацией которой является отправка этой огромной формы POST, на которую, наконец, сервер отвечает «Content-Disposition: вложение».

В конце концов, я нашел этот подход, который работает для меня, даже если он неэффективен для сети:

...setting up everything, until I just need to click on a button...

phantomData    = null;
phantomRequest = null;

// Here, I just recognize the form being submitted and copy it.

casper.on('resource.requested', function(requestData, request) {
    for (var h in requestData.headers) {
        if (requestData.headers[h].name === 'Content-Type') {
            if (requestData.headers[h].value === 'application/x-www-form-urlencoded') {
                phantomData         = requestData;
                phantomRequest      = request;
            }
        }
    }
});

// Here, I recognize when the request has FAILED because PhantomJS does
// not support straight downloading.

casper.on('resource.received', function(resource) {
    for (var h in resource.headers) {
        if (resource.headers[h].name === 'content-disposition') {
            if (resource.stage === 'end') {
                if (phantomData) {
                    // to do: get name from resource.headers[h].value
                    casper.download(
                        resource.url,
                        "output.pdf",
                        phantomData.method,
                        phantomData.postData
                    );
                } else {
                    // Something went wrong.
                }
                // Possibly, remove listeners?
            }
        }
    }
});

// Now, click on the button and initiate the dance.
casper.click(pdfLinkSelector);

Загрузка работает безупречно, даже если я вижу, что файл запрашивается (и отправляется) дважды.

[debug] [phantom] Navigation requested: url=https://somesite/SomePage.aspx, type=FormSubmitted, willNavigate=true, isMainFrame=true
[debug] [application] GOT FORM, REQUEST DATA SAVED
[warning] [phantom] Loading resource failed with status=fail (HTTP 200): https://somesite/SomePage.aspx
[debug] [application] END STAGE REACHED, PHANTOMDATA PRESENT
[debug] [application] ATTEMPTING CASPERJS.DOWNLOAD
[debug] [remote] sendAJAX(): Using HTTP method: 'POST'
[debug] [phantom] Downloaded and saved resource in output.pdf
[debug] [application] TERMINATING SUCCESSFULLY
[debug] [phantom] Navigation requested: url=about:blank, type=Other, willNavigate=true, isMainFrame=true
[debug] [phantom] url changed to "about:blank"

(Далее я, вероятно, изменю сценарий, чтобы попытаться вызвать request.abort() из прослушивателя resource.requested, установить семафор и снова вызвать загрузчик — я не смогу получить имя файла вложения, но это меня мало волнует).

person LSerni    schedule 14.09.2017