专栏首页python前行者利用 Python + Selenium 实现对页面的指定元素截图(可截长图元素)

利用 Python + Selenium 实现对页面的指定元素截图(可截长图元素)

对WebElement截图

WebDriver.Chrome自带的方法只能对当前窗口截屏,且不能指定特定元素。若是需要截取特定元素或是窗口超过了一屏,就只能另辟蹊径了。

WebDriver.PhantomJS自带的方法支持对整个网页截屏。

下面提供几种思路。

方式一

针对WebDriver.Chrome

通过WebDriver的js脚本注入功能,曲线救国。

  1. 注入第三方html转canvas的js库(见下方推荐)
  2. 获取元素html源码
  3. 将html转换为canvas
  4. 下载canvas

优点: 截取长图容易实现

缺点: 加载第三方库耗费时间,转换原理请参考这篇文章:

将 DOM 对象绘制到 canvas 中

方式二

针对WebDriver.Chrome

截取全图,自行裁剪、拼接

  1. 获取元素位置、大小
  2. 获取窗口大小
  3. 截取包含元素的窗口
  4. 进行相应的裁剪和拼接。

具体算法思路很清晰,但需要注意的细节较多。这里就不在赘述。示例代码请移步:

[Github]PythonSpiderLibs

优点: 不需太多js工作,python+少量js代码即可完成

缺点: 拼接等工作会被WebDriver的实现差异、图片加载速度等因素影响,需多加注意。 在保证截图质量的情况下,速度较慢

方式三

针对WebDriver.PhantomJS 由于接口实现的差异,PhantomJS相比于Chrome,可以截取到整个网页。所以获取指定元素的截图也就简单很多

  1. 截取网页全图
  2. 裁剪指定元素
driver = webdriver.Chrome()
driver.get('http://stackoverflow.com/')
driver.save_screenshot('screenshot.png')

left = element.location['x']
top = element.location['y']
right = element.location['x'] + element.size['width']
bottom = element.location['y'] + element.size['height']

im = Image.open('screenshot.png') 
im = im.crop((left, top, right, bottom))
im.save('screenshot.png')

优点: 实现简单

缺点: 对于高度太高的页面会导致文件过大,处理会有问题,我测试的最大图片尺寸是12.8M。

解决图片加载不完整的问题

参考: 利用 Python + Selenium 自动化快速截图

我们先在首页上执行一段 JavaScript 脚本,将页面的滚动条拖到最下方,然后再拖回顶部,最后才截图。这样可以解决像上面那种按需加载图片的情况。

# -*- coding: utf-8 -*-

from selenium import webdriver
import time


def take_screenshot(url, save_fn="capture.png"):
    # browser = webdriver.Firefox() # Get local session of firefox
    #谷歌浏览器截取当前窗口网页
    chromedriver = r"C:\soft\chromedriver2.31_win32\chromedriver.exe"
    browser = webdriver.Chrome(chromedriver)
    #phantomjs截取整张网页
    # browser = webdriver.PhantomJS()
    browser.set_window_size(1200, 900)
    browser.get(url) # Load page
    #将页面的滚动条拖到最下方,然后再拖回顶部
    browser.execute_script("""
        (function () {
            var y = 0;
            var step = 100;
            window.scroll(0, 0);

            function f() {
                if (y < document.body.scrollHeight) {
                    y += step;
                    window.scroll(0, y);
                    setTimeout(f, 100);
                } else {
                    window.scroll(0, 0);
                    document.title += "scroll-done";
                }
            }

            setTimeout(f, 1000);
        })();
    """)

    for i in xrange(30):
        if "scroll-done" in browser.title:
            break
        time.sleep(10)

    browser.save_screenshot(save_fn)
    browser.close()


if __name__ == "__main__":

    take_screenshot("http://codingpy.com")

如何截取某个网页元素

有时候我们只想截取某个网页元素的图片呢?比如说会动态变化的验证码。本来 Selenium 也提供了对元素截图的支持,只要在选中的元素上调用其 screenshot() 方法即可。

但是在实际使用时却遇到了 Unrecognized command 这个异常,经过一段时间检索也没有找到解决办法。所以,只能曲线救国,利用 Selenium 执行JS代码,将页面上不需要的元素一一删除,只保留我们希望留下的元素,然后再利用上面的窗口截屏功能。

例如,如果我们只截取编程派网站右侧的二维码,可以执行这样一段JQuery代码:

$('#main').siblings().remove();
$('#aside__wrapper').siblings().remove();
$('.ui.sticky').siblings().remove();
$('.follow-me').siblings().remove();
$('img.ui.image').siblings().remove();

代码执行完毕之后,就只剩下二维码的图片了。然后我们再截屏。不过这样有一点不好,就是截屏图片的下方会有大量空白内容。 - 代码

# -*- coding: utf-8 -*-

from selenium import webdriver
import time


def take_screenshot(url, save_fn="capture.png"):
    # browser = webdriver.Firefox() # Get local session of firefox
    chromedriver = r"C:\soft\chromedriver2.31_win32\chromedriver.exe"
    browser = webdriver.Chrome(chromedriver)
    # browser = webdriver.PhantomJS()
    browser.set_window_size(1200, 900)
    browser.get(url) # Load page
    #将页面的滚动条拖到最下方,然后再拖回顶部
    # browser.execute_script("""
    #     (function () {
    #         var y = 0;
    #         var step = 100;
    #         window.scroll(0, 0);
    # 
    #         function f() {
    #             if (y < document.body.scrollHeight) {
    #                 y += step;
    #                 window.scroll(0, y);
    #                 setTimeout(f, 100);
    #             } else {
    #                 window.scroll(0, 0);
    #                 document.title += "scroll-done";
    #             }
    #         }
    # 
    #         setTimeout(f, 1000);
    #     })();
    # """)
    # 
    # for i in xrange(30):
    #     if "scroll-done" in browser.title:
    #         break
    #     time.sleep(10)

    #只截取编程派网站右侧的二维码,可以执行这样一段JQuery代码:siblings().remove()移除兄弟姐妹元素
    browser.execute_script("""
        $('#main').siblings().remove();
        $('#aside__wrapper').siblings().remove();
        $('.ui.sticky').siblings().remove();
        $('.follow-me').siblings().remove();
        $('img.ui.image').siblings().remove();
        """)

    browser.save_screenshot(save_fn)
    browser.close()


if __name__ == "__main__":

    take_screenshot("http://codingpy.com/article/take-screenshot-of-web-page-using-selenium/")

不同wewbdriver对某些方法的实现不同

Chrome和PhantomJS 的接口差异

抓知乎时的坑,

  1. Chrome用WebElement.text可以正常得到值,用PhantomJS只能用 WebElement.get_attribute('innerHTML')
  2. WebDriver.Chrome截图只能截当前屏幕区域。WebDriver.PhantomJS截图可以获取整个页面的长图。

其它还有一些坑等待发现

推荐

  1. html2canvas库
  2. 将 DOM 对象绘制到 canvas 中
  3. 利用 Python + Selenium 自动化快速截图

文章参考:http://www.jianshu.com/p/7ed519854be7

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • tf.split()函数

    参考:https://blog.csdn.net/mls0311/article/details/82052472 https://www.w3cschool...

    周小董
  • urllib.request.urlretrieve()函数

    将URL表示的网络对象复制到本地文件。如果URL指向本地文件,则对象将不会被复制,除非提供文件名。返回一个元组()(filename,header),其中fil...

    周小董
  • mysql1062错误: Duplicate entry '...' for key 'PRIMARY

    Duplicate entry ‘…’ for key ‘PRIMARY,即插入数据时,要插入数据的主键数据(…)已经存在,不能再重复添加了。例:Duplica...

    周小董
  • 轻轻揭开 b*tree 索引结构的神秘面纱

    李翔宇 云和恩墨西区技术专家 大家好,我是云和恩墨的技术专家李翔宇,今天要为大家分享的主题是《轻轻揭开b*tree索引结构的神秘面纱》。 说到索引,大家应该...

    数据和云
  • django 学习笔记一

    文件里面配置了 DJANGO_SETTINGS_MODULE,也就是 设置(setting.py) 的路径

    onety码生
  • ALM问题解决笔记

    张树臣
  • 将 Discuz! 论坛远程附件存储到腾讯云对象存储COS上

    Discuz! 论坛可以通过配置远程附件功能将论坛的附件保存在腾讯云 COS 上,将论坛附件保存在 COS 上有以下好处:

    云存储
  • [android] 点击事件的四种写法

    /************************2016年4月23日 更新******************************/

    陶士涵
  • 博客园随笔中点击标签可以跳到当页指定位置的方法

      我们在写随笔的时候,如果内容比较多,那么我们就希望在最前面的索引中能自带跳转本文中的具体问之的功能。下面就简单介绍下载博客园中要实现这样的功能的方法。例如文...

    mukekeheart
  • 18.11 LVS DR模式搭建

    LVS DR模式搭建 DR模式搭建 – 准备工作 三台机器 分发器,也叫调度器(简写为dir) 133.130 rs1 133.132 rs2 133.133 ...

    运维小白

扫码关注云+社区

领取腾讯云代金券