Tome la captura de pantalla de la página completa con Selenium Python con chromedriver

Después de probar varios enfoques … Me he topado con esta página para tomar una captura de pantalla de página completa con chromedriver, selenium y python.

El código original está aquí . (y copio el código en esta publicación abajo)

Utiliza PIL y funciona muy bien! Sin embargo, hay un problema … es que captura encabezados fijos y se repite para toda la página y también pierde algunas partes de la página durante el cambio de página. url de muestra para tomar una captura de pantalla:

http://www.w3schools.com/js/default.asp

Cómo evitar los encabezados repetidos con este código … O hay alguna opción mejor que use solo python … (no sé java y no quiero usar java).

Por favor, vea la captura de pantalla del resultado actual y el código de muestra a continuación.

captura de pantalla de página completa con encabezados repetidos

test.py

""" This script uses a simplified version of the one here: https://snipt.net/restrada/python-selenium-workaround-for-full-page-screenshot-using-chromedriver-2x/ It contains the *crucial* correction added in the comments by Jason Coutu. """ import sys from selenium import webdriver import unittest import util class Test(unittest.TestCase): """ Demonstration: Get Chrome to generate fullscreen screenshot """ def setUp(self): self.driver = webdriver.Chrome() def tearDown(self): self.driver.quit() def test_fullpage_screenshot(self): ''' Generate document-height screenshot ''' #url = "http://effbot.org/imagingbook/introduction.htm" url = "http://www.w3schools.com/js/default.asp" self.driver.get(url) util.fullpage_screenshot(self.driver, "test.png") if __name__ == "__main__": unittest.main(argv=[sys.argv[0]]) 

util.py

 import os import time from PIL import Image def fullpage_screenshot(driver, file): print("Starting chrome full page screenshot workaround ...") total_width = driver.execute_script("return document.body.offsetWidth") total_height = driver.execute_script("return document.body.parentNode.scrollHeight") viewport_width = driver.execute_script("return document.body.clientWidth") viewport_height = driver.execute_script("return window.innerHeight") print("Total: ({0}, {1}), Viewport: ({2},{3})".format(total_width, total_height,viewport_width,viewport_height)) rectangles = [] i = 0 while i  total_height: top_height = total_height while ii  total_width: top_width = total_width print("Appending rectangle ({0},{1},{2},{3})".format(ii, i, top_width, top_height)) rectangles.append((ii, i, top_width,top_height)) ii = ii + viewport_width i = i + viewport_height stitched_image = Image.new('RGB', (total_width, total_height)) previous = None part = 0 for rectangle in rectangles: if not previous is None: driver.execute_script("window.scrollTo({0}, {1})".format(rectangle[0], rectangle[1])) print("Scrolled To ({0},{1})".format(rectangle[0], rectangle[1])) time.sleep(0.2) file_name = "part_{0}.png".format(part) print("Capturing {0} ...".format(file_name)) driver.get_screenshot_as_file(file_name) screenshot = Image.open(file_name) if rectangle[1] + viewport_height > total_height: offset = (rectangle[0], total_height - viewport_height) else: offset = (rectangle[0], rectangle[1]) print("Adding to stitched image with offset ({0}, {1})".format(offset[0],offset[1])) stitched_image.paste(screenshot, offset) del screenshot os.remove(file_name) part = part + 1 previous = rectangle stitched_image.save(file) print("Finishing chrome full page screenshot workaround...") return True 

Puedes lograr esto cambiando el CSS del encabezado antes de la captura de pantalla:

 topnav = driver.find_element_by_id("topnav") driver.execute_script("arguments[0].setAttribute('style', 'position: absolute; top: 0px;')", topnav) 

EDITAR : ponga esta línea después de su ventana de desplazamiento:

 driver.execute_script("document.getElementById('topnav').setAttribute('style', 'position: absolute; top: 0px;');") 

Así que en tu util.py estará:

 driver.execute_script("window.scrollTo({0}, {1})".format(rectangle[0], rectangle[1])) driver.execute_script("document.getElementById('topnav').setAttribute('style', 'position: absolute; top: 0px;');") 

Si el sitio utiliza la etiqueta del header , puede hacerlo con find_element_by_tag_name("header")

 element = driver.find_element_by_tag_name('body') element_png = element.screenshot_as_png with open("test2.png", "wb") as file: file.write(element_png) 

Esto funciona para mi Guarda toda la página como captura de pantalla. Para obtener más información, puede leer la api docs: http://selenium-python.readthedocs.io/api.html

Después de conocer el enfoque de @Moshisho.

Mi script de trabajo independiente completo es … (sueño agregado 0.2 después de cada desplazamiento y posición)

 import sys from selenium import webdriver import util import os import time from PIL import Image def fullpage_screenshot(driver, file): print("Starting chrome full page screenshot workaround ...") total_width = driver.execute_script("return document.body.offsetWidth") total_height = driver.execute_script("return document.body.parentNode.scrollHeight") viewport_width = driver.execute_script("return document.body.clientWidth") viewport_height = driver.execute_script("return window.innerHeight") print("Total: ({0}, {1}), Viewport: ({2},{3})".format(total_width, total_height,viewport_width,viewport_height)) rectangles = [] i = 0 while i < total_height: ii = 0 top_height = i + viewport_height if top_height > total_height: top_height = total_height while ii < total_width: top_width = ii + viewport_width if top_width > total_width: top_width = total_width print("Appending rectangle ({0},{1},{2},{3})".format(ii, i, top_width, top_height)) rectangles.append((ii, i, top_width,top_height)) ii = ii + viewport_width i = i + viewport_height stitched_image = Image.new('RGB', (total_width, total_height)) previous = None part = 0 for rectangle in rectangles: if not previous is None: driver.execute_script("window.scrollTo({0}, {1})".format(rectangle[0], rectangle[1])) time.sleep(0.2) driver.execute_script("document.getElementById('topnav').setAttribute('style', 'position: absolute; top: 0px;');") time.sleep(0.2) print("Scrolled To ({0},{1})".format(rectangle[0], rectangle[1])) time.sleep(0.2) file_name = "part_{0}.png".format(part) print("Capturing {0} ...".format(file_name)) driver.get_screenshot_as_file(file_name) screenshot = Image.open(file_name) if rectangle[1] + viewport_height > total_height: offset = (rectangle[0], total_height - viewport_height) else: offset = (rectangle[0], rectangle[1]) print("Adding to stitched image with offset ({0}, {1})".format(offset[0],offset[1])) stitched_image.paste(screenshot, offset) del screenshot os.remove(file_name) part = part + 1 previous = rectangle stitched_image.save(file) print("Finishing chrome full page screenshot workaround...") return True driver = webdriver.Chrome() ''' Generate document-height screenshot ''' url = "http://effbot.org/imagingbook/introduction.htm" url = "http://www.w3schools.com/js/default.asp" driver.get(url) fullpage_screenshot(driver, "test1236.png") 

Esta respuesta mejora las respuestas anteriores de am05mhz y Javed Karim .

Asume el modo sin cabeza y que inicialmente no se configuró una opción de tamaño de ventana. Antes de llamar a esta función, asegúrese de que la página se haya cargado completamente o lo suficiente.

Intenta establecer el ancho y el alto tanto como sea necesario. La captura de pantalla de toda la página a veces puede incluir una barra de desplazamiento vertical innecesaria. Una forma de evitar generalmente la barra de desplazamiento es tomando una captura de pantalla del elemento del cuerpo en su lugar. Después de guardar una captura de pantalla, revierte el tamaño a lo que era originalmente, y el tamaño de la siguiente captura de pantalla no se configura correctamente.

En última instancia, esta técnica puede que todavía no funcione perfectamente para algunos ejemplos.

 def save_screenshot(driver: webdriver.Chrome, path: str = '/tmp/screenshot.png'): # Ref: https://stackoverflow.com/a/52572919/ original_size = driver.get_window_size() required_width = driver.execute_script('return document.body.parentNode.scrollWidth') required_height = driver.execute_script('return document.body.parentNode.scrollHeight') driver.set_window_size(required_width, required_height) # driver.save_screenshot(path) # has scrollbar driver.find_element_by_tag_name('body').screenshot(path) # avoids scrollbar driver.set_window_size(original_size['width'], original_size['height']) 

Si usa Python anterior a 3.6, elimine las anotaciones de tipo de la definición de función.

No estoy seguro si las personas todavía tienen este problema. He hecho un pequeño truco que funciona bastante bien y que funciona bien con zonas dinámicas. Espero eso ayude

 # 1. get dimensions browser = webdriver.Chrome(chrome_options=options) browser.set_window_size(default_width, default_height) browser.get(url) time.sleep(sometime) total_height = browser.execute_script("return document.body.parentNode.scrollHeight") browser.quit() # 2. get screenshot browser = webdriver.Chrome(chrome_options=options) browser.set_window_size(default_width, total_height) browser.get(url) browser.save_screenshot(screenshot_path) 

Cambié el código de Python 3.6, tal vez sea útil para alguien:

 from selenium import webdriver from sys import stdout from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.desired_capabilities import DesiredCapabilities import unittest #from Login_Page import Login_Page from selenium.webdriver.firefox.firefox_binary import FirefoxBinary from io import BytesIO from PIL import Image def testdenovoUIavailable(self): binary = FirefoxBinary("C:\\Mozilla Firefox\\firefox.exe") self.driver = webdriver.Firefox(firefox_binary=binary) verbose = 0 #open page self.driver.get("http://yandex.ru") #hide fixed header #js_hide_header=' var x = document.getElementsByClassName("topnavbar-wrapper ng-scope")[0];x[\'style\'] = \'display:none\';' #self.driver.execute_script(js_hide_header) #get total height of page js = 'return Math.max( document.body.scrollHeight, document.body.offsetHeight, document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);' scrollheight = self.driver.execute_script(js) if verbose > 0: print(scrollheight) slices = [] offset = 0 offset_arr=[] #separate full screen in parts and make printscreens while offset < scrollheight: if verbose > 0: print(offset) #scroll to size of page if (scrollheight-offset) 0: self.driver.get_screenshot_as_file('screen_%s.jpg' % (offset)) print(scrollheight) #create image with screenshot = Image.new('RGB', (slices[0].size[0], scrollheight)) offset = 0 offset2= 0 #now glue all images together for img in slices: screenshot.paste(img, (0, offset_arr[offset2])) offset += img.size[1] offset2+= 1 screenshot.save('test.png') 

¿Por qué no solo obtener el ancho y alto de la página y luego cambiar el tamaño del controlador? Así será algo como esto

 total_width = driver.execute_script("return document.body.offsetWidth") total_height = driver.execute_script("return document.body.scrollHeight") driver.set_window_size(total_width, total_height) driver.save_screenshot("SomeName.png") 

Esto va a hacer una captura de pantalla de toda la página sin la necesidad de combinar diferentes piezas.

Las capturas de pantalla se limitan a la ventana gráfica, pero puede solucionar esto capturando el elemento del body , ya que el controlador web capturará todo el elemento incluso si es más grande que la ventana gráfica. Esto le ahorrará tener que lidiar con el desplazamiento y la costura de las imágenes, sin embargo, puede ver problemas con la posición del pie de página (como en la captura de pantalla a continuación).

Probado en Windows 8 con el controlador Chrome.

 url = "https://stackoverflow.com/" driver = webdriver.Chrome() driver.get(url) el = driver.find_element_by_tag_name('body') el.screenshot('/path/to/save/in/scrape.png') driver.quit() 

Devoluciones: (tamaño completo: https://i.stack.imgur.com/ppDiI.png )

SO_scrape

 element=driver.find_element_by_tag_name('body') element_png = element.screenshot_as_png with open("test2.png", "wb") as file: file.write(element_png) 

Hubo un error en el código sugerido anteriormente en la línea 2. Aquí está el corregido. Al ser un novato aquí, todavía no puedo editar mi propia publicación.

A veces el baove no consigue mejores resultados. Por lo tanto, puede usar otro método para obtener la altura de todos los elementos y sumrlos para establecer la altura de captura de la siguiente manera:

 element=driver.find_elements_by_xpath("/html/child::*/child::*") eheight=set() for e in element: eheight.add(round(e.size["height"])) print (eheight) total_height = sum(eheight) driver.execute_script("document.getElementsByTagName('html')[0].setAttribute('style', 'height:"+str(total_height)+"px')") element=driver.find_element_by_tag_name('body') element_png = element.screenshot_as_png with open(fname, "wb") as file: file.write(element_png) 

Por cierto, funciona en FF.

Modifique ligeramente el código de @ihightower y @ A.Minachev, y haga que funcione en la retina de mac:

 import time from PIL import Image from io import BytesIO def fullpage_screenshot(driver, file, scroll_delay=0.3): device_pixel_ratio = driver.execute_script('return window.devicePixelRatio') total_height = driver.execute_script('return document.body.parentNode.scrollHeight') viewport_height = driver.execute_script('return window.innerHeight') total_width = driver.execute_script('return document.body.offsetWidth') viewport_width = driver.execute_script("return document.body.clientWidth") # this implementation assume (viewport_width == total_width) assert(viewport_width == total_width) # scroll the page, take screenshots and save screenshots to slices offset = 0 # height slices = {} while offset < total_height: if offset + viewport_height > total_height: offset = total_height - viewport_height driver.execute_script('window.scrollTo({0}, {1})'.format(0, offset)) time.sleep(scroll_delay) img = Image.open(BytesIO(driver.get_screenshot_as_png())) slices[offset] = img offset = offset + viewport_height # combine image slices stitched_image = Image.new('RGB', (total_width * device_pixel_ratio, total_height * device_pixel_ratio)) for offset, image in slices.items(): stitched_image.paste(image, (0, offset * device_pixel_ratio)) stitched_image.save(file) fullpage_screenshot(driver, 'test.png') 

He modificado la respuesta de jeremie-s para que solo obtenga la url una vez.

 browser = webdriver.Chrome(chrome_options=options) browser.set_window_size(default_width, default_height) browser.get(url) height = browser.execute_script("return document.body.parentNode.scrollHeight") # 2. get screenshot browser.set_window_size(default_width, height) browser.save_screenshot(screenshot_path) browser.quit() 

Puedes usar Splinter
Splinter es una capa de abstracción sobre las herramientas de automatización del navegador existentes, como Selenium
Hay una nueva característica browser.screenshot(..., full=True) en la nueva versión 0.10.0 .
full=True opción full=True hará una captura de pantalla completa para usted.