Як зробити частковий знімок екрана за допомогою Selenium WebDriver у python?


Відповіді:


138

Крім селену, цей приклад також вимагає бібліотеки PIL Imaging. Іноді це ставиться як одна зі стандартних бібліотек, а іноді ні, але якщо у вас його немає, ви можете встановити його за допомогою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, щоб дістатися до цього, це може бути життєздатним рішенням.


18
Ви також можете отримати знімок екрана безпосередньо в пам'яті:img = Image.open(StringIO(base64.decodestring(driver.get_screenshot_as_base64())))
ejk314

7
Альтернативою для завантаження в пам’ять є, img = fox.get_screenshot_as_png()а потім img = Image.open(StringIO(img))завантажити його як зображення PIL.
yellowcap

6
коментар взято з NAA @ sukrit-gupta: RandomPhobia, ваша відповідь чудово працює у випадку статичних сторінок, які не потрібно прокручувати. У випадку, якщо комусь потрібно отримати зображення з великих сторінок, які потребують прокрутки, ви повинні використовувати функцію location_once_scroll_into_view. Тож замініть location = element.location на: location = img.location_once_scroll_into_view. Також переконайтеся, що ви використовуєте Chrome замість Firefox, оскільки Chrome робить знімок екрана лише видимої області, тоді як Firefox робить знімок всієї вкладки.
bummi

6
Продовження за пропозицією @yellowcap: Примітка в Python 3+, BytesIOскоріше, ніж StringIO.
Алекс П. Міллер,

4
Я стикаюся з проблемою зміни розміру, поки зображення зменшено. Знімок екрана працює, але обрізання зображення, здається, не працює. Хтось стикався з тим самим?
Суміт Мурарі

31

Працював у мене в python3.5

from selenium import webdriver


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

ps

Зберегти у файл

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

1
@Julius ні, цього не повинно бути - достатньо лише тоді, коли частковий скріншот точно відповідає елементу з елементом, якому присвоєно id.
зменшення активності

@Julius, досі невідомо, де і як це працює. У Python 2.7 з Chrome це не працює.
Користувач

працював над chrome / python 2.7 для мене із селеном2. image = driver.find_element_by_id('el_id').screenshot_as_pngце атрибут елемента, і як я можу зберегти це як зображення?
танві

як приступити до збереження зображення?
Лакшмі Нараянан

2
@tanvi @Lakshmi Ви, хлопці, можете image = driver.find_element_by_id('el_id').screenshot(output_file_path). Будь ласка , зверніться тут для API док.
Ken Park

7

Я написав цю корисну функцію 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 відображеного елемента у вигляді байтів. Обмеження: елемент повинен поміщатися у вікні перегляду.
Для роботи з ним потрібно встановити модуль палички.


1
Хороший код! Коли я намагаюся з довгою сторінкою в хромі, я думаю, x = element.location_once_scrolled_into_view["x"] y = element.location_once_scrolled_into_view["y"]оскільки locationможе повернутися більше, ніж вікно.
Vimos

6

Ось функція, яка робить саме це: Розміри повинні бути відлиті до цілих чисел перед передачею функції обрізання:

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')

Це майже те саме, що і прийняті відповіді, з доданою помилкою, що він не використовує селен, як того хотів ОП.
Ілед

Він використовує селен, але оператор імпорту тут не потрібен, він також включає перетворення розташувань з плаваючого в int, функція img.crop видає виняток, якщо розташування не є цілими числами
SEDaradji

TypeError: initial_value must be str or None, not bytes
pyd

що таке ваш принт (img), це метод або байт-об'єкт?
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: unknown error: failed to parse value of getElementRegion (Session info: chrome=78.0.3904.108)
Мостафа Гадімі

@MostafaGhadimi pls перевірити це: sqa.stackexchange.com/questions/40321/…
Гавріель Коен

0

Я перетворив відповідь @ 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

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.