最近发现百度文库改用canvas来展示内容了,以前收藏的小工具都没法用。

思路

思路非常简单,主要是用canvas.toDataURL将canvas内容获取为base64编码,然后保存图像。略微麻烦的是,百度文库只渲染当前可视范围附近的页面,那么我们需跳转到对应页面的位置,然后又因页面每次加载并渲染的时间可能不一致,设置固定的等待加载渲染时间可能导致意外捕获到空白图像;为了保险起见,通过监控捕获图像的大小以及是否变化来判断是否抓取图像或者等待加载渲染完成。

web-security问题

如果直接使用toDataURL会出现The operation is insecure.警告,貌似是因为canvas被污染了。目前的解决方案是禁用web-security,方法是找到Chrome的快捷方式,将目标最后加上:

 --disable-web-security --user-data-dir=C:\Users\Administrator\Documents\chrome

这时候再通过快捷方式打开浏览器,就不会出现错误了。要注意的是,如果不设置--user-data-dir的话,设置是不会生效的。

图像格式问题

toDataURL可以选择输出格式,目前来看,输出png图像体积太大,输出jpeg图像会出现奇怪线条,或者大面积图像变成黑色,输出webp相对而言效果较好。可以通过格式工厂将所有图像转换为pdf。

下面是JS代码

(function(){
    baiduwk_caper_var_img_size = 0;
    baiduwk_caper_var_timeout = 0;
    baiduwk_caper = function (i = 1, end = null){
        var dom;
        dom = document.querySelector('.read-all');
        if (dom) dom.click();
        dom = document.querySelector("#pageNo-"+i+" canvas");
        if (!dom){
            if (end === null){
                console.log("End at page: "+(i-1));
            }else{
                console.error("ERROR: End at page: "+(i-1)+", "+(end - i + 1)+" remaining.");
            }
            return;
        }
        dom.scrollIntoView();
        setTimeout(function(){
            var data, link;
            // data = dom.getContext("2d").getImageData(0, 0, dom.width, dom.height);
            data = dom.toDataURL("image/webp");
            if (baiduwk_caper_var_img_size < 5000 || data.length != baiduwk_caper_var_img_size){ // 等待图像体积稳定
                baiduwk_caper_var_img_size = data.length;
                setTimeout(function(){
                    if (baiduwk_caper_var_timeout++ > 8){ // 超时自动停止
                        console.error("Loading timeout at page: "+i+", stopped.");
                        alert("Error occurred.");
                        return;
                    }else{
                        baiduwk_caper(i, end);
                    }
                }, 200);
                return;
            }
            console.log('Next page: '+i);
            link = document.createElement("a");
            link.download = "page_"+i;
            link.href = data;
            link.click();
            baiduwk_caper_var_img_size = 0;
            baiduwk_caper_var_timeout = 0;
            // 范围判断 是否继续抓取
            if (end === null || i < end){
                baiduwk_caper(++i, end);
            }else{
                console.log("End at page:"+i);
            }
        }, 200);
    }
    baiduwk_caper(1);
})();

使用说明

通过Chrome快捷键打开浏览器,打开百度文库后,打开控制台,执行上方附带的代码,即可。
若要指定抓取页面范围,可调用函数baiduwk_caper(s, p),其中s为起始页面,p为截止页面,抓取的页码n满足s<=n<=p,若不指定p,则满足s<=n

如果想要抓取其他网站的文档,可以修改代码第8行的匹配规则。