使用Chrome來做一張長截圖

Chrome

遇見一篇有意思的文章,或者是在購物時發(fā)現(xiàn)了好東西木人,需要分享信柿。

不想發(fā)送鏈接,也許搞一個長截圖是個很好的選擇醒第。

Selenium

Selenium是一種自動化測試工具渔嚷,它支持各種瀏覽器,包括 Chrome稠曼,Safari形病,F(xiàn)irefox 等主流界面式瀏覽器,如果你在這些瀏覽器里面安裝一個 Selenium 的插件霞幅,那么便可以方便地實(shí)現(xiàn)Web界面的測試漠吻。換句話說叫 Selenium 支持這些瀏覽器驅(qū)動。Selenium支持多種語言開發(fā)司恳,比如 Java途乃,C,Ruby, Python等等抵赢。

在這個案例中欺劳,我們使用基于Python3的運(yùn)行環(huán)境進(jìn)行演示。

使用pip3安裝selenium


$ pip3 install selenium

獲取chrome-driver

在淘寶npm鏡像中可以找到Chrome版本對應(yīng)的驅(qū)動: http://npm.taobao.org/mirrors/chromedriver/

初始化瀏覽器


    def init_browser():
        chrome_options = Options()
        # --headless參數(shù)表示铅鲤,Chrome將不會有一個可視化的圖形界面
        # chrome_options.add_argument("--headless")
        chrome_options.add_argument("--disable-gpu")
        chrome_options.add_argument("--disable-web-security")
        # 以iPhone 6的屏幕寬度作為基準(zhǔn)
        mobile_emulation = { "deviceName": "iPhone 6" }
        chrome_options.add_experimental_option("mobileEmulation", mobile_emulation)
        return webdriver.Chrome("./chromedriver",
                                chrome_options=chrome_options)

    browser = inti_browser()

調(diào)用Webdriver提供的API, 獲取網(wǎng)頁基本信息


    url = "https://code.evink.me"

    # browser是我們剛剛初始化的瀏覽器實(shí)例
    # 設(shè)置頁面渲染超時
    browser.set_page_load_timeout(30)
    # 設(shè)置腳本執(zhí)行超時
    browser.set_script_timeout(100)
    # 獲取網(wǎng)頁資源
    browser.get(url)
    # 給瀏覽器設(shè)置一個默認(rèn)的初始寬高(非必要)
    browser.set_window_size(375, 1000)

Chrome在iPhone 6的模擬環(huán)境下進(jìn)行網(wǎng)頁渲染划提,其寬是一定的,所以可以通過調(diào)用運(yùn)行于webdriver內(nèi)部的javascript代碼邢享,獲得我們想要的內(nèi)容鹏往。

webdrive提供了豐富的接口供我們?nèi)轿徊倏剡@個小小的瀏覽器。


    # browser是我們剛剛初始化的瀏覽器實(shí)例
    body_height = browser.body.get_attribute("clientHeight")

或者骇塘,這里還有一個更加可操作的方法伊履,直接在webdriver里跑js代碼。Selenium提供了execute_script(str)接口款违,可以讓用戶通過自己更為熟悉的方式唐瀑,得到自己想要的內(nèi)容。


    # browser是我們剛剛初始化的瀏覽器實(shí)例
    body_height = browser.execute_script(get_script("body_height")))

    # get_script(str) -> str
    def get_script(_type):
        if _type == "body_height":
            return """
                    // get body_height
                    return document.getElementsByTagName("body")[0].clientHeight;
            """

滾屏截圖

webdriver提供了一個保存當(dāng)前瀏覽器窗口截圖的接口save_screenshot(path)

我們只需要讓網(wǎng)頁沿著一個預(yù)設(shè)定的高度滾動就好插爹。

這里哄辣,這個高度是667(基于我們以iPhone 6的虛擬環(huán)境初始化的瀏覽器)。設(shè)定大于667的高度赠尾,每次也并不會截取到更多的頁面內(nèi)容力穗,而小于667的高度,會讓你最后進(jìn)行圖片處理的時候非常頭疼气嫁。


    dir_path = "tmp_screenshots"
    filename = "evink" + int(datetime.now().timestamp())
    paging_list = []

    def take_screenshot():
        # loop_times由網(wǎng)頁高度和單屏高度計(jì)算而來
        for i in range(loop_times):
            browser.execute_script(get_script("scroll_window") % (i + 1))
            # 截取屏幕
            path = "%s/%s_%s.png"%(dir_path, filename, i)
            browser.save_screenshot(path)
            paging_list.append(path)
        return paging_list


    def get_script(_type):

        if _type == "scroll_window":
            return """
                    // 滾動的次數(shù)
                    var m = %s;
                    // 屏幕range
                    var begin = 0;
                    var end = 667;
                    // 滾屏
                    window.scrollTo(begin, end * m);
                """
        if _type == "scroll_y":
            return """
                return window.scrollY;
            """

合成圖片

上一步結(jié)束之后当窗,我們在/tmp_screenshots目錄下會發(fā)現(xiàn)若干png格式的圖片,利用Python中的PIL Image庫寸宵,可以很方便的對圖片做處理崖面。


   def paste_imgs(self):
       # loop_times由網(wǎng)頁高度和單屏高度計(jì)算而來
       for i in range(loop_times):
           path = "%s/%s_%s.png"%(dir_path, filename, i)
           if os.path.exists(path):
               continue
           else:
               # 記錄存在的最大圖片張數(shù)
               max_screen = i
       # 根據(jù)總圖片張數(shù)計(jì)算合成的單張圖片高度
       page_total_height = 667 * 2 * loop_times
       # 聲明一張空白的圖片實(shí)例
       image = Image.new(
           'RGB', (375 * 2, page_total_height), (255, 255, 255))

       for i in range(loop_times):
           path = "%s/%s_%s.png"%(dir_path, filename, i)
           from_img = Image.open(path)
           # 粘貼圖片
           image.paste(from_img, (0, 1334 * ( i + 1 )))

       path = "%s/%s_part_%s.jpg"%(dir_path, filename, part+1)
       # 保存圖片 以JPEG格式元咙,60質(zhì)量
       image.save(path,format='JPEG', quality=60)
       return "%s/%s.jpg"%(dir_path, filename)

處理細(xì)節(jié)

上一步結(jié)束之后,我們獲得了一張完整的圖片嘶朱,但是蛾坯,你一定會發(fā)現(xiàn)很多小細(xì)節(jié)沒有處理光酣。

圖片未被加載

如果你要截取的網(wǎng)頁采用了圖片懶加載模式(可以提升訪問速度)疏遏,你會發(fā)現(xiàn)所截取的網(wǎng)頁的圖片都被灰色的色塊所替代。

我們可以分析網(wǎng)頁中救军,未被加載的圖片是否有什么共同點(diǎn)财异。比如說,是否含有特定的class唱遭,是否有自定義的屬性值戳寸。


    # 假設(shè)圖片未加載時,此網(wǎng)頁圖片的class會有 "img_loading"

    # 獲取所有的圖片元素
    imgs = browser.find_elements_by_tag_name("img")
    img_load_start = datetime.now().timestamp()
    while True:
        # 已加載的圖片數(shù)
        img_loadeds = 0
        for img in imgs:
            # 拿到class屬性
            clz = img.get_attribute("class")
            if clz.find("img_loading") == -1:
                img_loadeds += 1
        print("已經(jīng)加載完畢的圖像數(shù):%s"%img_loadeds)
        if img_loadeds == len(wait_load_imgs):
            print("all imgs loaded")
            break
        # 給他設(shè)置一個超時
        if datetime.now().timestamp() - img_load_start > 60:
            print("imgs loading timeout")
            break
        sleep(0.1)

圖片底部 顯示不完全 / 留有大量的空白 / 拼接不完美

造成這個原因拷泽,無非是高度和留余問題疫鹊。

網(wǎng)頁高度

某些網(wǎng)頁上,在滾屏完畢(即所有圖片都被加載后)的高度和網(wǎng)頁初始化后的高度并不一致司致。

高度的錯誤會導(dǎo)致生成圖片時拋出異常


    # 媽媽讓我再滾一次
    def scroll_window(need_renew_height=False):

        for pre_scroll in range(loop_times + 1):
            self.browser.execute_script(self.get_script("scroll_window")%pre_scroll)
            sleep(0.5)

        print("-- 已經(jīng)滾完 --")

        if need_renew_height:
            # 重新錄入高度
            body = browser.find_element_by_id('activity-detail')
            body_height = int(body.get_attribute("clientHeight"))
            loop_times = math.ceil(body_height / height)

    def get_script(self, _type):

        if _type == "scroll_window":
            return """
                    // 滾動的次數(shù)
                    var m = %s;
                    // 取出所有圖片
                    var imgs = document.querySelectorAll('img');
                    // 屏幕range
                    var begin = 0;
                    var end = 667;
                    // 滾屏
                    window.scrollTo(begin, end * m);
                    // 圖片的相對距離
                    for(var i = 0;i < imgs.length;i++){
                        var y = imgs[i].getBoundingClientRect()["y"];
                        if(y >= begin && y <= end){
                            imgs[i].setAttribute("type", "wait_load");
                        }
                    }
            """

處理好收尾工作

在生成滾屏?xí)r拆吆,最后一張圖片含有兩種狀態(tài)。

  • 完美占據(jù)一屏的空間 (375 * 667)
  • 只占據(jù)部分空間脂矫,底部含有留白

處理好最后一屏圖片和倒數(shù)第二屏的關(guān)系枣耀,就可以避免出現(xiàn)圖片拼接不完美的情況。

圖片 黑屏 / 損壞

圖片過長庭再,嘗試按照固定的屏幕數(shù)捞奕,將一張圖切成幾張小圖。


原文地址: https://code.evink.me/2018/07/post/python-use-chrome-to-make-a-long-page-screenshot/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拄轻,一起剝皮案震驚了整個濱河市颅围,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌恨搓,老刑警劉巖院促,帶你破解...
    沈念sama閱讀 217,509評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異奶卓,居然都是意外死亡一疯,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評論 3 394
  • 文/潘曉璐 我一進(jìn)店門夺姑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來墩邀,“玉大人,你說我怎么就攤上這事盏浙∶级茫” “怎么了荔茬?”我有些...
    開封第一講書人閱讀 163,875評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長竹海。 經(jīng)常有香客問我慕蔚,道長,這世上最難降的妖魔是什么斋配? 我笑而不...
    開封第一講書人閱讀 58,441評論 1 293
  • 正文 為了忘掉前任孔飒,我火速辦了婚禮,結(jié)果婚禮上艰争,老公的妹妹穿的比我還像新娘坏瞄。我一直安慰自己,他們只是感情好甩卓,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評論 6 392
  • 文/花漫 我一把揭開白布鸠匀。 她就那樣靜靜地躺著,像睡著了一般逾柿。 火紅的嫁衣襯著肌膚如雪缀棍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,365評論 1 302
  • 那天机错,我揣著相機(jī)與錄音爬范,去河邊找鬼。 笑死毡熏,一個胖子當(dāng)著我的面吹牛坦敌,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播痢法,決...
    沈念sama閱讀 40,190評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼狱窘,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了财搁?” 一聲冷哼從身側(cè)響起蘸炸,我...
    開封第一講書人閱讀 39,062評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎尖奔,沒想到半個月后搭儒,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,500評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡提茁,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評論 3 335
  • 正文 我和宋清朗相戀三年淹禾,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片茴扁。...
    茶點(diǎn)故事閱讀 39,834評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡铃岔,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出峭火,到底是詐尸還是另有隱情毁习,我是刑警寧澤智嚷,帶...
    沈念sama閱讀 35,559評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站纺且,受9級特大地震影響盏道,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜载碌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評論 3 328
  • 文/蒙蒙 一猜嘱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧恐仑,春花似錦泉坐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽孤钦。三九已至歧斟,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間偏形,已是汗流浹背静袖。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留俊扭,地道東北人队橙。 一個月前我還...
    沈念sama閱讀 47,958評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像萨惑,于是被迫代替她去往敵國和親捐康。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評論 2 354