如何在Python中使用Selenium WebDriver进行局部截图?

92

我已经搜索了很多,但是找不到解决方案。这里有一个可能在Java中解决的类似问题

在Python中是否有类似的解决方案?

8个回答

164

除了Selenium,这个示例还需要PIL图像处理库。有时它会被作为标准库之一加入,有时候不会,但如果你没有它,你可以通过pip install Pillow来安装它。

from selenium import webdriver
from PIL import Image
from io import BytesIO

fox = webdriver.Firefox()
fox.get('http://stackoverflow.com/')

# now that we have the preliminary stuff out of the way time to get that image :D
element = fox.find_element_by_id('hlogo') # find part of the page you want image of
location = element.location
size = element.size
png = fox.get_screenshot_as_png() # saves screenshot of entire page
fox.quit()

im = Image.open(BytesIO(png)) # uses PIL library to open image in memory

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


im = im.crop((left, top, right, bottom)) # defines crop points
im.save('screenshot.png') # saves new cropped image

最后的输出是... Stackoverflow 的标志!!!

输入图像描述

现在,如果您只想获取静态图像,那么这可能有些过度了,但是,如果您想要获取需要使用Javascript才能到达的内容,那么这可能是一种可行的解决方案。


20
您也可以直接从内存中获取截图:img = Image.open(StringIO(base64.decodestring(driver.get_screenshot_as_base64()))) - ejk314
8
用于内存加载的另一种选择是 img = fox.get_screenshot_as_png(),然后使用 img = Image.open(StringIO(img)) 将其作为PIL图像进行加载。 - yellowcap
7
从@sukrit-gupta的NAA中提取的评论:*RandomPhobia,您的答案适用于不需要滚动的静态页面。如果有人需要从需要滚动的大页面获取图像,则应使用location_once_scrolled_into_view函数。因此,请将location = element.location替换为:location = img.location_once_scrolled_into_view另外,请确保您使用Chrome而不是Firefox,因为Chrome仅对可见区域进行屏幕截图,而Firefox则对整个选项卡进行屏幕截图。* - bummi
7
跟进@yellowcap的建议:注意在Python 3+中,应该使用BytesIO而不是StringIO - Alex P. Miller
4
我面临调整大小的问题,当图像被缩放时。截图可以正常工作,但裁剪图像似乎无效。是否有人遇到过相同的问题? - Sumit Murari
显示剩余11条评论

52

在Python 3.5中对我起作用。

from selenium import webdriver


fox = webdriver.Firefox()
fox.get('http://stackoverflow.com/')
image = fox.find_element_by_id('hlogo').screenshot_as_png

附言:

保存至文件

image=driver.find_element_by_id('hlogo').screenshot(output_file_path)

1
@Julius 不,它不应该是这样的 - 只有当部分截图与已分配ID的元素完全匹配时才足够。 - reducing activity
@Julius 目前还不确定它的工作原理和位置。在 Python 2.7 和 Chrome 中,它无法正常工作。 - User
我们该如何继续保存图像? - Lakshmi Narayanan
4
@tanvi @Lakshmi 你们可以使用 image = driver.find_element_by_id('el_id').screenshot(output_file_path)。请参考这里的API文档。 - Ken Park
如Gavriel Cohen建议的那样,在保存之前,我必须执行'image.open'。im = Image.open(BytesIO(image)) # 使用PIL库在内存中打开图像 im.save('example.png') - ePandit
显示剩余4条评论

9

我编写了这个有用的Python3函数。

from base64 import b64decode
from wand.image import Image
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.common.action_chains import ActionChains
import math

def get_element_screenshot(element: WebElement) -> bytes:
    driver = element._parent
    ActionChains(driver).move_to_element(element).perform()  # focus
    src_base64 = driver.get_screenshot_as_base64()
    scr_png = b64decode(src_base64)
    scr_img = Image(blob=scr_png)

    x = element.location["x"]
    y = element.location["y"]
    w = element.size["width"]
    h = element.size["height"]
    scr_img.crop(
        left=math.floor(x),
        top=math.floor(y),
        width=math.ceil(w),
        height=math.ceil(h),
    )
    return scr_img.make_blob()

它将显示元素作为png图像以字节形式返回。 限制:元素必须适合视口。
您必须安装wand模块才能使用它。


1
不错的代码!当我在Chrome中尝试长页面时,我认为x = element.location_once_scrolled_into_view["x"] y = element.location_once_scrolled_into_view["y"],因为location可能会返回一个大于窗口的y值。 - Vimos

7

下面是一个可以实现上述功能的函数,调用 crop 函数前必须先将尺寸转换为整数:

from PIL import Image
from StringIO import StringIO
def capture_element(element,driver):
  location = element.location
  size = element.size
  img = driver.get_screenshot_as_png()
  img = Image.open(StringIO(img))
  left = location['x']
  top = location['y']
  right = location['x'] + size['width']
  bottom = location['y'] + size['height']
  img = img.crop((int(left), int(top), int(right), int(bottom)))
  img.save('screenshot.png')

这基本上与被接受的答案相同,但它有一个缺点,即它不使用Selenium,正如OP所需。 - iled
它确实使用了Selenium,但在这里不需要导入语句。它还包括将位置从浮点数转换为整数的过程,如果位置不是整数,则函数img.crop会抛出异常。 - SEDaradji
TypeError: initial_value must be str or None, not bytes - Pyd
你的print(img)是一个方法还是Byte对象? - Pyd
我不确定我理解你的问题。 - SEDaradji

5

在回应RandomPhobia的非常好的答案评论的基础上,这里提供两个解决方案,具有正确的导入语句,可以打开一个全屏截图,而无需先保存到文件:

from selenium import webdriver
from PIL import Image
from StringIO import StringIO
import base64

DRIVER = 'chromedriver'
browser = webdriver.Chrome(DRIVER)

browser.get( "http:\\\\www.bbc.co.uk" )

img 1 = Image.open(StringIO(base64.decodestring(browser.get_screenshot_as_base64())))

img 2 = Image.open(StringIO(browser.get_screenshot_as_png()))

我相信你接下来的问题一定是:"那哪个最快呢?",以下是如何确定最快的方法(我发现第一种方法要快得多):

import timeit

setup = '''
from selenium import webdriver
from PIL import Image
from StringIO import StringIO
import base64

DRIVER = 'chromedriver'
browser = webdriver.Chrome(DRIVER)
browser.get( "http:\\\\www.bbc.co.uk" )

file_name = 'tmp.png'
'''

print timeit.Timer('Image.open(StringIO(browser.get_screenshot_as_png()))', setup=setup).repeat(2, 10)
print timeit.Timer('Image.open(StringIO(base64.decodestring(browser.get_screenshot_as_base64())))', setup=setup).repeat(2, 10)
print timeit.Timer('browser.get_screenshot_as_file(file_name); pil_img = Image.open(file_name)', setup=setup).repeat(2, 10)

5

元素截图:

from PIL import Image
from io import BytesIO


image = self.browser.driver.find_element_by_class_name('example.bla.bla').screenshot_as_png
im = Image.open(BytesIO(image))  # uses PIL library to open image in memory
im.save('example.png')

我做了同样的事情,但是我遇到了以下错误:WebDriverException: Message: 未知错误:无法解析getElementRegion的值 (Session info: chrome=78.0.3904.108) - Mostafa Ghadimi
@MostafaGhadimi请查看此链接:https://sqa.stackexchange.com/questions/40321/chromedriver-driver-failed-to-parse-value-of-getelementregion-understanding - Gavriel Cohen

4

就这么简单:

element = driver.find_element_by_class_name('myclass')
element.screenshot('screenshot.png')

至少在Selenium 4中,文件名需要是完整路径。 - UltraBob

2

我将@randomphobia的答案转换成了一个函数。我还使用了@bummis的建议,使用location_once_scrolled_into_view代替location,以便无论页面大小如何都可以通用。

from selenium import webdriver
from PIL import Image
from io import BytesIO

def take_screenshot(element, driver, filename='screenshot.png'):
  location = element.location_once_scrolled_into_view
  size = element.size
  png = driver.get_screenshot_as_png() # saves screenshot of entire page

  im = Image.open(BytesIO(png)) # uses PIL library to open image in memory

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


  im = im.crop((left, top, right, bottom)) # defines crop points
  im.save(filename) # saves new cropped image


这里是要点概述:https://gist.github.com/WittmannF/b714d3ceb7b6a5cd50002f11fb5a4929

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接