Как загрузить файл с кукловодом с помощью безголового: true?

У меня был следующий код для загрузки файла csv с веб-сайта http://niftyindices.com/resources/holiday-calendar:

const puppeteer = require('puppeteer');

(async () => {
const browser = await puppeteer.launch({headless: true});
const page = await browser.newPage();

await page.goto('http://niftyindices.com/resources/holiday-calendar');
await page._client.send('Page.setDownloadBehavior', {behavior: 'allow', 
downloadPath: '/tmp'})
await page.click('#exportholidaycalender');
await page.waitFor(5000);
await browser.close();
})();

с headless: false он работает, он загружает файл в /Users/user/Downloads. с headless: true он НЕ работает.

Я запускаю это на macOS Sierra (MacBook Pro), используя версию кукольника 1.1.1, которая вытаскивает версию Chromium 66.0.3347.0 в каталог .local-chromium/ и использует npm init и npm i --save puppeteer для ее настройки.

Любая идея, что не так?

Заранее благодарим за ваше время и помощь,

Ответ 1

Эта страница загружает csv, создавая строку с разделителями-запятыми и заставляя браузер загружать ее, задав таким образом тип данных

let uri = "data:text/csv;charset=utf-8," + encodeURIComponent(content);
window.open(uri, "Some CSV");

Это на chrome открывает новую вкладку.

Вы можете использовать это событие и физически загружать содержимое в файл. Не уверен, что это лучший способ, но хорошо работает.

const browser = await puppeteer.launch({
  headless: true
});
browser.on('targetcreated', async (target) => {
    let s = target.url();
    //the test opens an about:blank to start - ignore this
    if (s == 'about:blank') {
        return;
    }
    //unencode the characters after removing the content type
    s = s.replace("data:text/csv;charset=utf-8,", "");
    //clean up string by unencoding the %xx
    ...
    fs.writeFile("/tmp/download.csv", s, function(err) {
        if(err) {
            console.log(err);
            return;
        }
        console.log("The file was saved!");
    }); 
});

const page = await browser.newPage();
.. open link ...
.. click on download link ..

Ответ 2

Проблема в том, что браузер закрывается до окончания загрузки.

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

Это пример:

const filename = <set this with some regex in response>;
const dir = <watch folder or file>;

// Download and wait for download
    await Promise.all([
        page.click('#DownloadFile'),
       // Event on all responses
        page.on('response', response => {
            // If response has a file on it
            if (response._headers['content-disposition'] === 'attachment;filename=${filename}') {
               // Get the size
                console.log('Size del header: ', response._headers['content-length']);
                // Watch event on download folder or file
                 fs.watchFile(dir, function (curr, prev) {
                   // If current size eq to size from response then close
                    if (parseInt(curr.size) === parseInt(response._headers['content-length'])) {
                        browser.close();
                        this.close();
                    }
                });
            }
        })
    ]);

Даже то, что способ поиска в ответ может быть улучшен, хотя я надеюсь, что вы найдете это полезным.

Ответ 3

Вчера я потратил часы на просмотр этой темы и, пытаясь выяснить, как заставить Puppeteer загрузить CSV файл, щелкнув ссылку для загрузки в режиме без заголовка во время аутентифицированного сеанса. Принятый ответ здесь не сработал в моем случае, потому что загрузка не инициировала targetcreated, а следующий ответ по какой-либо причине не сохранил аутентифицированный сеанс. Эта статья спасла день. Короче, fetch. Надеюсь, это поможет кому-то еще.

const res = await this.page.evaluate(() =>
{
    return fetch('https://example.com/path/to/file.csv', {
        method: 'GET',
        credentials: 'include'
    }).then(r => r.text());
});

Ответ 4

Мне нужно было скачать файл из-за входа, который обрабатывал Puppeteer. targetcreated не запускается. В конце я загрузил с помощью request, после копирования файлов cookie из экземпляра Puppeteer.

В этом случае я передаю файл, но вы можете просто сохранить его.

    res.writeHead(200, {
        "Content-Type": 'application/octet-stream',
        "Content-Disposition": `attachment; filename=secretfile.jpg`
    });
    let cookies = await page.cookies();
    let jar = request.jar();
    for (let cookie of cookies) {
        jar.setCookie(`${cookie.name}=${cookie.value}`, "http://secretsite.com");
    }
    try {
        var response = await request({ url: "http://secretsite.com/secretfile.jpg", jar }).pipe(res);
    } catch(err) {
        console.trace(err);
        return res.send({ status: "error", message: err });
    }

Ответ 5

У меня есть другое решение этой проблемы, так как ни один из ответов здесь не работал для меня.

Мне нужно было зайти на сайт и загрузить некоторые отчеты .csv. С головой было хорошо, без головы не получалось, что бы я ни пытался. Глядя на сетевые ошибки, загрузка прерывается, но я не могу (быстро) определить причину.

Итак, я перехватил запросы и использовал node-fetch, чтобы сделать запрос за пределами кукловода. Это потребовало копирования параметров выборки, тела, заголовков и добавления в файл cookie доступа.

Удачи.