天天看點

基于phantomjs與robot對網頁截屏

在爬蟲開發過程中,或者其他方面有時候會有這種需求,截取網頁圖檔,作為一種快照資訊進行存儲,在最近開發過程中也剛好碰到了這種需求,需要将爬蟲過程中的網頁進行快照資訊儲存,是以檢視了一部分文檔,現提供以下兩種方式進行快照截圖。

Python版本

python需要安裝selenium,通過pip方式便可安裝,期中下面有三種方式:

1. 調用Chrome或者FireFox浏覽器方式,這種都需要打開本地一個無頭浏覽器,而這個浏覽器需要自己單獨下載下傳,否則會報異常,網上例子很多,不再描述,下載下傳後放置與python.exe同級目錄便可運作,這兩種方式都隻能截取浏覽器部分截圖,如果該頁面有滾動條,那麼下面頁面是無法截取到的。

2. 調用PhantomJS方式,這種就是解決滾動截屏問題,可以自動将目前浏覽器頁面全部内容截取,并且不打開浏覽器,不用額外安裝其他東西,至于其他帶cookie,post,get不同請求請查閱其他資料。

from selenium import webdriver

# 使用PhantomJS擷取浏覽器截圖,會截取整個網頁資訊,包含滾動條以下部分
br = webdriver.PhantomJS()
br.maximize_window()
br.get("http://www.baidu.com")
br.save_screenshot("./baidu01.png")

# 使用chrome浏覽器擷取浏覽器截圖,隻會截取展現部分
br = webdriver.Chrome()
br.maximize_window()
br.get("http://www.baidu.com")
br.save_screenshot("./baidu02.png")

# 使用Firefox浏覽器擷取浏覽器截圖,隻會截取展現部分
br = webdriver.Firefox()
br.maximize_window()
br.get("http://www.baidu.com")
br.save_screenshot("./baidu03.png")
           

JAVA版本

Java主要兩種方式:

一種是Robot方式,這種截圖不但可以截圖,還可以實作一起操控類,截圖隻是它一個小功能,例子中也隻是簡單打開浏覽器,截取目前顯示部分,然後儲存成圖檔,關閉浏覽器,是以對于存在滾動條的,無法全部截取,因為要模拟操控滾動條,形成一個輸入流,循環讀取,顯得太過于麻煩,不太通用。不過Robot最主要的并不是截圖,而是操控,沒準需要固定點選的可以用該方式模拟操作呢。

public class CutPicture {
    /**
     * 圖像截取并儲存
     * @param url: 請求連結
     * @throws MalformedURLException
     * @throws IOException
     * @throws URISyntaxException
     * @throws AWTException
     */
    public static void saveHtmlImg(String url) throws IOException, URISyntaxException, AWTException {
        Desktop.getDesktop().browse(new URL(url).toURI());
        Robot robot = new Robot();
        robot.delay(2000);
        Dimension d = new Dimension(Toolkit.getDefaultToolkit().getScreenSize());
        int width = (int) d.getWidth();
        int height = (int) d.getHeight();
        // 最大化浏覽器
        robot.keyRelease(KeyEvent.VK_F11);
        robot.delay(2000);
        /**
         * 所有操作浏覽器方法都在此設定
         * 如設定滑鼠滾軸向下:robot.mouseWheel(KeyEvent.VK_DOWN);
         * 每項設定後都需增加延遲robot.delay()
         * Robot 對螢幕截取隻是一點小小的功能,而實際它可以自定義很多方法去實際操控電腦,是以慎用
         */
        Image image = robot.createScreenCapture(new Rectangle(0, 0, width, height));
        BufferedImage bi = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = bi.createGraphics();
        /**
         * x:截圖x軸起點
         * y: 截圖y軸起點,防止截取浏覽器搜尋框,是以設定為-100,相應height+100
         */
        g.drawImage(image, 0, -100, width, height+100, null);
        // 儲存圖檔
        File fileOut = new File("./imgHtml");
        fileOut.mkdirs();
        ImageIO.write(bi, "jpg", new File("./imgHtml/001.jpg"));
        //關閉浏覽器
        closeBrowse();
    }

    /**
     * 關閉浏覽器
     */
    public static void closeBrowse(){
        try {
            Runtime.getRuntime().exec("taskkill /F /IM chrome.exe");
            Runtime.getRuntime().exec("taskkill /F /IM iexplorer.exe");
            Runtime.getRuntime().exec("taskkill /F /IM 360se.exe");
            Runtime.getRuntime().exec("taskkill /F /IM firefox.exe");
            Runtime.getRuntime().exec("taskkill /F /IM iexplorer.exe");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) throws AWTException, IOException, URISyntaxException {
        String url = "http:www.baidu.com";
        String url2 = "http://top.baidu.com/?fr=mhd_card";
        saveHtmlImg("http:www.baidu.com");

    }

}
           

另一種則是PhantomJs方式,其實就是python版本的Java對應,但是需要提前下載下傳phantomjs,網上連結很多,通過這種方式的截屏,則可以實作滾動截屏,但是截屏,截取多少,怎麼截取,其實靠它的配置檔案一個js檔案控制,phantomjs官方js檔案怎麼寫,網上demo很多,其實java隻是調用,至于怎麼截取,怎麼用,完全靠js檔案編寫。

public class PhantomTools {

    /**
     * 本地連結中文轉Unicode碼
     * @param url
     * @return
     */
    public static String chineseToUnicode(String url){
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < url.length(); i++) {
            char c = url.charAt(i);
            if (c >= 0 && c <= 255) {
                sb.append(c);
            } else {
                byte[] b;
                try {
                    b = String.valueOf(c).getBytes("utf-8");
                } catch (Exception ex) {
                    System.out.println(ex);
                    b = new byte[0];
                }
                for (int j = 0; j < b.length; j++) {
                    int k = b[j];
                    if (k < 0)
                        k += 256;
                    sb.append("%" + Integer.toHexString(k).toUpperCase());
                }
            }
        }
        return sb.toString();
    }

    /**
     * 圖檔截取,以下兩個路徑根據自己工程目錄更改
     * phantomjsPath:phantomjs.exe路徑
     * cutPictureJsPath:截圖javascript腳本的路徑
     * @param url
     * @throws IOException
     */
    public static void  cutPicturl(String url) throws IOException {
        String basePath = System.getProperty("user.dir")+"\\";
        String BLANK = "  ";
        //phantomjs.exe路徑
        String phantomjsPath = basePath+"pictureHtmlSpider\\src\\PhantomCutPicture\\staticPhantomJs\\phantomjs.exe" + BLANK;
        //截圖javascript腳本的路徑 E:\JAVA_PROJECT\imgHtmlSpider\pictureHtmlSpider\src\PhantomCutPicture\staticPhantomJs\rasterize.js
        String cutPictureJsPath = basePath+"pictureHtmlSpider\\src\\PhantomCutPicture\\staticPhantomJs\\rasterize.js" + BLANK;
        //url位址
        String urlPath = chineseToUnicode(url)+BLANK;
        // 儲存圖檔
        String filePath = "./imgHtml";
        /**這裡如果要改變檔案夾名字imgHTML,需要同時更改cutPictureMain()方法中的if判斷*/
        File fileOut = new File(filePath);
        fileOut.mkdirs();
        //輸出目錄
        Random random = new Random();
        String savePath = filePath+"/"+(random.nextInt(10)+1000)+".png";
        System.out.println(savePath);
        /**
         * 調用phantomjs
         * phantomjsPath:phantomjs路徑
         * cutPictureJsPath:rasterize.js路徑,全屏截圖配置就在此編寫,定制化截屏也需更改此檔案
         *   網頁帶cookie請求也需要編寫此js檔案,便可帶cookie通路,适用于爬蟲開發
         * urlPath:網頁請求
         * savePath:圖檔儲存路徑
         */
        Process process = Runtime.getRuntime().exec(phantomjsPath
                + cutPictureJsPath
                + urlPath
                + savePath);

        InputStream is = process.getInputStream();
        BufferedReader br = new BufferedReader(new InputStreamReader(is));
        int tmp=0;
        while (br.readLine() != null)
        {
            tmp+=1;
            System.out.println("==> Start cutPicture :"+tmp+" page");
        }
    }
    public static void main(String[] args) throws IOException {
        String url = "http:www.baidu.com";
        String url2 = "http://top.baidu.com/?fr=mhd_card";
        cutPicturl(chineseToUnicode(url2));
    }
}
           

它的配置rasterize.js檔案:

"use strict";
var page = require('webpage').create(),
    system = require('system'),
    address, output, size;
    page.settings.userAgent = 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36';

if (system.args.length < 3 || system.args.length > 5) {
    console.log('Usage: rasterize.js URL filename [paperwidth*paperheight|paperformat] [zoom]');
    console.log('  paper (pdf output) examples: "5in*7.5in", "10cm*20cm", "A4", "Letter"');
    console.log('  image (png/jpg output) examples: "1920px" entire page, window width 1920px');
    console.log('                                   "800px*600px" window, clipped to 800x600');
    phantom.exit(1);
} else {
    address = system.args[1];
    output = system.args[2];
    page.viewportSize = { width: 800, height: 900 };
    if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") {
        size = system.args[3].split('*');
        page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' }
            : { format: system.args[3], orientation: 'portrait', margin: '1cm' };
    } else if (system.args.length > 3 && system.args[3].substr(-2) === "px") {
        size = system.args[3].split('*');
        if (size.length === 2) {
            pageWidth = parseInt(size[0], 10);
            pageHeight = parseInt(size[1], 10);
            page.viewportSize = { width: pageWidth, height: pageHeight };
            page.clipRect = { top: 0, left: 0, width: pageWidth, height: pageHeight };
        } else {
            console.log("size:", system.args[3]);
            pageWidth = parseInt(system.args[3], 10);
            pageHeight = parseInt(pageWidth * 3/4, 10); // it's as good an assumption as any
            console.log ("pageHeight:",pageHeight);
            page.viewportSize = { width: pageWidth, height: pageHeight };
        }
    }
    if (system.args.length > 4) {
        page.zoomFactor = system.args[4];
    }
    page.open(address, function (status) {
        if (status !== 'success') {
            console.log('Unable to load the address!');
            phantom.exit(1);
        } else {
            window.setTimeout(function () {
                page.render(output);
                phantom.exit();
            }, 200);
        }
    });
}
           

以上例子都經過測試,可以對浏覽器中包含滾動條的全部截屏。