Wie mache ich einen Teil-Screenshot mit Selenium WebDriver in Python?

75

Ich habe viel danach gesucht, aber keine Lösung gefunden. Hier ist eine ähnliche Frage mit einer möglichen Lösung in Java.

Gibt es eine ähnliche Lösung in Python?

Streamoverflowed
quelle

Antworten:

137

Neben Selen erfordert dieses Beispiel auch die PIL Imaging-Bibliothek. Manchmal wird dies als eine der Standardbibliotheken eingefügt und manchmal nicht, aber wenn Sie es nicht haben, können Sie es mit installierenpip 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

und schließlich ist die Ausgabe ... das Stackoverflow-Logo !!!

Geben Sie hier die Bildbeschreibung ein

Das wäre natürlich übertrieben, wenn Sie nur ein statisches Bild aufnehmen würden, aber wenn Sie etwas aufnehmen möchten, für das Javascript erforderlich ist, könnte dies eine praktikable Lösung sein.

RandomPhobia
quelle
18
Sie können den Screenshot auch direkt im Speicher img = Image.open(StringIO(base64.decodestring(driver.get_screenshot_as_base64())))
abrufen
7
Eine Alternative für die in der Speicherlade ist img = fox.get_screenshot_as_png()und dann img = Image.open(StringIO(img))als PIL Bild zu laden.
Yellowcap
6
Kommentar aus einer NAA von @ sukrit-gupta: RandomPhobia, Ihre Antwort funktioniert hervorragend bei statischen Seiten, die nicht gescrollt werden müssen. Falls jemand Bilder von großen Seiten abrufen muss, die gescrollt werden müssen, sollten Sie die Funktion location_once_scrolled_into_view verwenden. Ersetzen Sie also location = element.location durch: location = img.location_once_scrolled_into_view Stellen Sie außerdem sicher, dass Sie Chrome anstelle von Firefox verwenden, da Chrome nur einen Screenshot des sichtbaren Bereichs erstellt, während Firefox einen Screenshot der gesamten Registerkarte erstellt.
Bummi
6
Follow-up zum Vorschlag von @yellowcap: Hinweis in Python 3+, sollten Sie BytesIOeher als StringIO.
Alex P. Miller
4
Ich habe ein Problem mit der Größenänderung, während das Bild ausgeblendet wird. Der Screenshot funktioniert, aber das Zuschneiden des Bildes scheint nicht zu funktionieren. Hat jemand das gleiche gesehen?
Sumit Murari
30

Arbeitete für mich in 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

In Datei speichern

image=driver.find_element_by_id('hlogo').screenshot(output_file_path)
Iman Kermani
quelle
1
@ Julius nein, das sollte nicht sein - es reicht nur, wenn ein Teil des Screenshots genau mit dem Element übereinstimmt, dem die ID zugewiesen wurde.
Reduzierung der Aktivität
@ Julius es ist immer noch ungewiss, wo und wie es funktioniert. In Python 2.7 mit Chrome funktioniert es nicht.
Benutzer
arbeitete für mich mit Selen2 an Chrome / Python 2.7. image = driver.find_element_by_id('el_id').screenshot_as_pngIst dies ein Attribut des Elements und wie kann ich es als Bild speichern?
Tanvi
Wie gehen wir vor, um das Bild zu speichern?
Lakshmi Narayanan
2
@tanvi @Lakshmi Ihr könnt es tun image = driver.find_element_by_id('el_id').screenshot(output_file_path). Das API-Dokument finden Sie hier .
Ken Park
7

Ich habe diese nützliche Python3-Funktion geschrieben.

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

Es gibt ein PNG-Bild des angezeigten Elements als Bytes zurück. Einschränkung: Das Element muss in das Ansichtsfenster passen.
Sie müssen das Stab-Modul installieren, um damit arbeiten zu können.

eugenhell
quelle
1
Schöner Code! Als ich mit langer Seite in Chrom versuchen, ich denke , x = element.location_once_scrolled_into_view["x"] y = element.location_once_scrolled_into_view["y"]da ist locationals das Fenster größer zurückkehrt ay kann.
Vimos
6

Hier ist eine Funktion, die genau das tut: Die Größen müssen in Ganzzahlen umgewandelt werden, bevor sie an die Zuschneidefunktion übergeben werden:

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')
SEDaradji
quelle
Dies ist so ziemlich das Gleiche wie die akzeptierten Antworten, mit dem zusätzlichen Fehler, dass Selen nicht verwendet wird, wie es das OP wollte.
iled
Es wird Selen verwendet, aber die Importanweisung wird hier nicht benötigt. Sie enthält auch die Konvertierung von Positionen von float nach int. Die Funktion img.crop löst eine Ausnahme aus, wenn die Positionen keine ganzen Zahlen sind
SEDaradji
TypeError: initial_value must be str or None, not bytes
Pyd
Was ist Ihr Druck (img), ist eine Methode oder ein Byte-Objekt?
Pyd
Ich bin nicht sicher, ob ich Ihre Frage verstehe
SEDaradji
5

Um die Kommentare als Antwort auf die sehr nette Antwort von RandomPhobia zu erweitern, finden Sie hier zwei Lösungen mit korrekten Importanweisungen, die einen Vollbild-Screenshot öffnen, ohne ihn zuerst in einer Datei zu speichern:

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

Und weil ich sicher bin, dass Ihre nächste Frage lautet: "Nun, das ist großartig, aber welche ist die schnellste?", So können Sie dies bestimmen (ich finde, dass die erste Methode mit Abstand die schnellste ist):

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)
StackG
quelle
5

Screenshot nach Element:

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')
Gavriel Cohen
quelle
Ich mache das gleiche, aber ich habe folgenden Fehler: WebDriverException: Message: unknown error: failed to parse value of getElementRegion (Session info: chrome=78.0.3904.108)
Mostafa Ghadimi
@MostafaGhadimi Bitte überprüfen Sie dies: sqa.stackexchange.com/questions/40321/…
Gavriel Cohen
0

Ich habe die Antwort von @ randomphobia in eine Funktion umgewandelt. Ich habe auch den Vorschlag von @bummis verwendet, location_once_scrolled_into_viewanstatt zu verwenden, locationum unabhängig von der Größe der Seite zu verallgemeinern.

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

Hier ist ein Kern: https://gist.github.com/WittmannF/b714d3ceb7b6a5cd50002f11fb5a4929

Fernando Wittmann
quelle