Node.js 如何使用headless下载木偶师的文件:true?

Node.js 如何使用headless下载木偶师的文件:true?,node.js,chromium,puppeteer,Node.js,Chromium,Puppeteer,为了从网站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.

为了从网站
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)上运行这个程序,使用Puppeter版本
1.1.1
,它将Chromium版本
66.0.3347.0
拉入
本地Chromium/
目录,并使用
npm init
npm I--save puppeter
进行设置

知道怎么了吗


提前感谢您的时间和帮助,

此页面通过创建逗号分隔字符串并通过如下设置数据类型强制浏览器下载csv来下载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 ..

我需要从一个登录名后面下载一个文件,该登录名由木偶演员处理<代码>目标已创建未被触发。最后,在从Puppeter实例复制cookies之后,我使用
请求
下载

在本例中,我将通过流传输文件,但您也可以轻松地保存它

    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 });
    }

昨天我花了几个小时仔细研究了堆栈溢出问题,试图找出如何让Puppeter在经过身份验证的会话中单击无头模式下的下载链接来下载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());
});

问题是浏览器在下载完成之前关闭

您可以从响应中获取文件大小和文件名,然后使用监视脚本检查下载文件中的文件大小,以关闭浏览器

这是一个例子:

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();
                    }
                });
            }
        })
    ]);
constfilename=;
常数dir=;
//下载并等待下载
等待承诺([
页面。单击(“#下载文件”),
//关于所有答复的事件
第页('response',response=>{
//如果响应上有一个文件
if(响应._头['content-disposition']=`attachment;filename=${filename}`){
//知道尺寸了吗
log('Size del header:',response._headers['content-length']);
//监视下载文件夹或文件上的事件
fs.watchFile(dir,function(curr,prev){
//如果当前大小相等于响应大小,则关闭
if(parseInt(curr.size)==parseInt(response.\u头['content-length'])){
browser.close();
这个。关闭();
}
});
}
})
]);

即使是搜索响应的方式也可以改进,尽管我希望您会发现这很有用。

我有另一个解决这个问题的方法,因为这里的答案对我都不起作用

我需要登录一个网站,下载一些.csv报告。Headed很好,headless不管我怎么做都失败了。查看网络错误,下载被中止,但我无法(很快)确定原因

因此,我截获了请求,并使用节点获取在Puppeter之外发出请求。这需要复制获取选项、正文、标题并添加到access cookie中


祝你好运。

我找到了一种等待浏览器功能下载文件的方法。其思想是使用谓词等待响应。在我的例子中,URL以“/data”结尾

我只是不喜欢将文件内容加载到缓冲区中

await page._client.send('Page.setDownloadBehavior', {
    behavior: 'allow',
    downloadPath: download_path,
});

await frame.focus(report_download_selector);
await Promise.all([
    page.waitForResponse(r => r.url().endsWith('/data')),
    page.keyboard.press('Enter'),
]);

setDownloadBehavior
适用于
headless:true
模式,文件最终会下载,但完成后会引发异常,因此对于我来说,一个简单的包装器有助于忘记这个问题并完成工作:

const fs = require('fs');    
function DownloadMgr(page, downloaddPath) {
    if(!fs.existsSync(downloaddPath)){
        fs.mkdirSync(downloaddPath);
    }
    var init = page.target().createCDPSession().then((client) => {
        return client.send('Page.setDownloadBehavior', {behavior: 'allow', downloadPath: downloaddPath})
    });
    this.download = async function(url) {
        await init;
        try{
            await page.goto(url);
        }catch(e){}
        return Promise.resolve();
    }
}

var path = require('path');
var DownloadMgr = require('./classes/DownloadMgr');
var downloadMgr = new DownloadMgr(page, path.resolve('./tmp'));
await downloadMgr.download('http://file.csv');

我发现的一种方法是使用
addScriptTag
方法。可在无头模式下工作
False
True

使用此功能,可以下载任何类型的网页。现在考虑到该网页会打开一个类似以下内容的链接:

网页,意味着将使用以下脚本下载mp4文件

    await page.addScriptTag({'content':'''
    function fileName(){
        link = document.location.href
        return link.substring(link.lastIndexOf('/')+1);
    }
    async function save() {
        bl = await fetch(document.location.href).then(r => r.blob()); 
        var a = document.createElement("a");
        a.href = URL.createObjectURL(bl);
        a.download = fileName();
        a.hidden = true;
        document.body.appendChild(a);
        a.innerHTML = "download";
        a.click();
    }
    save()
    '''
    })

我有一个更困难的变化,使用木偶演员夏普。在开始下载之前,我需要设置标题Cookies

本质上,在点击按钮之前,我必须处理多个响应,并通过下载处理单个响应。一旦我得到了那个特定的响应,我就必须为远程服务器附加头和cookie,以便在响应中发送可下载的数据

await using (var browser = await Puppeteer.LaunchAsync(new LaunchOptions { Headless = true, Product = Product.Chrome }))
await using (var page = await browser.NewPageAsync())
{
    ...
    // Handle multiple responses and process the Download
    page.Response += async (sender, responseCreatedEventArgs) =>
    {
        if (!responseCreatedEventArgs.Response.Headers.ContainsKey("Content-Type"))
            return;

        // Handle the response with the Excel download
        var contentType = responseCreatedEventArgs.Response.Headers["Content-Type"];
        if (contentType.Contains("application/vnd.ms-excel"))
        {
            string getUrl = responseCreatedEventArgs.Response.Url;

            // Add the cookies to a container for the upcoming Download GET request
            var pageCookies = await page.GetCookiesAsync();
            var cookieContainer = BuildCookieContainer(pageCookies);

            await DownloadFileRequiringHeadersAndCookies(getUrl, fullPath, cookieContainer, cancellationToken);
        }
    };

    await page.ClickAsync("button[id^='next']");

    // NEED THIS TIMEOUT TO KEEP THE BROWSER OPEN WHILE THE FILE IS DOWNLOADING!
    await page.WaitForTimeoutAsync(1000 * configs.DownloadDurationEstimateInSeconds);
}
按如下方式填充Cookie容器:

private CookieContainer BuildCookieContainer(IEnumerable<CookieParam> cookies)
{
    var cookieContainer = new CookieContainer();
        
    foreach (var cookie in cookies)
    {
        cookieContainer.Add(new Cookie(cookie.Name, cookie.Value, cookie.Path, cookie.Domain));
    }

    return cookieContainer;
}
私有CookieContainer构建CookieContainer(IEnumerable cookies)
{
var cookieContainer=新的cookieContainer();
foreach(cookies中的var cookie)
{
添加(新Cookie(Cookie.Name、Cookie.Value、Cookie.Path、Cookie.Domain));
}
返回cookieContainer;
}

下载文件RequiringHeaders和Cookie的详细信息如下。如果您需要下载文件更简单,您可能可以使用此线程或链接线程中提到的其他方法。

我在创建
浏览器
对象时使用了
启用日志记录
,我在下载过程中看到了这一点:
[0313/104723.451228:VERBOSE1:navigator\u impl.cc(200)]临时加载失败:数据:应用程序/csv;字符集=utf-8,%22SR.%20NO。。。。错误\u描述:,显示\u repost\u interstitial:0,帧\u id:4
完美!作品这也不需要
page.\u客户机
在场。这可能适用于这样的情况