Estoy usando Selenium con Python (en el cuaderno Jupyter). Tengo varias pestañas abiertas, digamos 5 pestañas (con todos los elementos ya terminados de cargarse) y me gustaría recorrerlas y hacer 2 cosas:
(En el código a continuación, me enfoco en el requisito de captura de pantalla, pero también me gustaría saber cómo guardarlo como PDF)
Este código permite recorrer las pestañas:
numTabs = len(driver.window_handles) for x in range(numTabs): driver.switch_to.window(driver.window_handles[x]) time.sleep(0.5) Sin embargo, si trato de agregar una llamada a driver.save_screenshot() como se muestra a continuación, el código parece detenerse después de tomar la primera captura de pantalla. Específicamente, se crea '0.png' para la primera pestaña (con índice 0) y cambia a la siguiente pestaña (con índice 1) pero deja de procesarse más. Ni siquiera pasa a la siguiente pestaña.
numTabs = len(driver.window_handles) for x in range(numTabs): driver.switch_to.window(driver.window_handles[x]) driver.save_screenshot(str(x) + '.png') #screenshot call time.sleep(0.5) Edit1: Modifiqué el código como se muestra a continuación para comenzar a tomar capturas de pantalla de window_handles[1] en lugar de [0] ya que realmente no necesito la captura de pantalla de [0] , pero ahora ni siquiera se genera una sola captura de pantalla. Entonces parece que la llamada save_screenshot() no funciona incluso después de la llamada inicial switch_to.window() .
tabs = driver.window_handles for t in range(1, len(tabs)): print("Processing tab " + tabs[t]) driver.switch_to.window(tabs[t]) driver.save_screenshot(str(t) + '.png') #screenshot call, but the code hangs. No screenshot taking, no further cycling through tabs. Edit2: descubrí por qué mi código está "colgado", sin importar qué método de impresión en PDF o capturas de pantalla esté usando. Mencioné anteriormente que las nuevas pestañas se abrieron al hacer clic en los botones de la página principal, pero luego de una inspección más cercana, ahora veo que el contenido de las nuevas pestañas se genera usando document. write() . Hay un código ajax que recupera el contenido de waybillHTML que luego se escribe en una nueva ventana usando document.write(waybillHTML)
Para más contexto, este es un sistema de pedidos, con la página principal con una lista de pedidos y un botón al lado de cada pedido que abre una nueva pestaña con una hoja de ruta. Y la parte importante es que las hojas de ruta se generan utilizando document. write() activado por los clics del botón. Me doy cuenta de que la opción "Ver código fuente de la página" está atenuada al hacer clic con el botón derecho en las nuevas pestañas. Cuando uso switch_to.window() para cambiar a una de estas pestañas, Page.printToPDF agota después de 300 (supongo que segundos).
--------------------------------------------------------------------------- TimeoutException Traceback (most recent call last) <ipython-input-5-d2f601d387b4> in <module> 14 driver.switch_to.window(handles[x]) 15 time.sleep(2) ---> 16 data = driver.execute_cdp_cmd("Page.printToPDF", printParams) 17 with open(str(x) + '.pdf', 'wb') as file: 18 file.write(base64.b64decode(data['data'])) ... TimeoutException: Message: timeout: Timed out receiving a message from renderer: 300.000 (Session info: headless chrome=96.0.4664.110) Entonces, mi pregunta refinada debería ser cómo usar Page.printToPDF para imprimir una página en una nueva ventana (que se genera dinámicamente con document. write() ) sin tiempo de espera .
Un enfoque que probé fue hacer esto:
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities caps = DesiredCapabilities().CHROME caps["pageLoadStrategy"] = "none" driver = webdriver.Chrome(options=chrome_options, desired_capabilities=caps)refiriéndose a: esta pregunta
pero el problema es que esto es demasiado 'agresivo' e impide que el código inicie sesión en el sistema de pedidos y realice la navegación y el filtrado necesarios para que el navegador llegue al punto en el que aparece la página de listado de pedidos con los botones de generación de hojas de ruta (es decir, el original configuración en esta pregunta).
Edit3: en este punto, he intentado algo tan simple como simplemente obtener la fuente de la página
try: pageSrc = driver.find_element(By.XPATH, "//*").get_attribute("outerHTML") print(pageSrc) de las pestañas generadas dinámicamente (mucho después de que terminaron de renderizar y puedo ver el contenido en la pantalla (sin usar headless para esta etapa de la depuración)) e incluso esto en sí mismo arroja una TimeoutException, por lo que no creo que sea un Problema con esperar a que se cargue el contenido. De alguna manera, el controlador no puede ver el contenido. Puede ser algo peculiar con la forma en que se generan estas páginas, no lo sé. Todos los métodos sugeridos en las respuestas para tomar capturas de pantalla y guardar archivos PDF son buenos, estoy seguro para las ventanas normales. Con Chrome, la View page source permanece atenuada, pero puedo ver el contenido HTML regular usando Inspect .
Edit4: al usar la función de inspección de Chrome, la fuente de la página generada dinámicamente tiene esta estructura HTML:
Curiosamente, "Ver código fuente de la página" permanece atenuado incluso después de que puedo inspeccionar el contenido: 
Edit5: Finalmente lo descubrí. Cuando hice clic en el botón para generar la nueva pestaña, el sitio haría una llamada ajax para obtener el contenido HTML de la nueva página y document.write . Escribirlo en una nueva pestaña. Supongo que esto está fuera del alcance de Selenium para manejar. En su lugar, importé selenium-wire , usé driver.wait_for_request para interceptar la llamada ajax, analicé la respuesta que contenía el código HTML de la nueva pestaña y volqué el HTML limpio en un nuevo archivo. A partir de ese momento, la generación del PDF se puede manejar fácilmente de varias maneras, como ya han sugerido otros.
Estoy un poco confundido en esta parte de sus preguntas:
Tomar una captura de pantalla,
(Como beneficio adicional) Imprima cada uno en PDF utilizando la función Guardar como PDF integrada de Chrome utilizando A4 horizontal, escalado normal y un directorio predeterminado específico, sin necesidad de interacción del usuario.
La función save_screenshot guarda un archivo de imagen en su sistema de archivos. Para convertir este archivo de imagen a PDF, debe abrirlo y escribirlo en un archivo PDF.
Esa tarea es fácil, utilizando varios módulos PDF de Python. Tengo un código para esto, así que avíseme si lo necesita y lo agregaré el código a continuación.
Con respecto a la impresión de las páginas web en las pestañas como archivos PDF, usaría execute_cdp_cmd con Page.printToPDF . El siguiente código se puede modificar para admitir su escenario de direcciones URL desconocidas mediante los clics del botón. Si necesita ayuda con esto, hágamelo saber.
import base64 import traceback from time import sleep from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.common.exceptions import TimeoutException chrome_options = Options() chrome_options.add_argument("--start-maximized") chrome_options.add_argument("--disable-infobars") chrome_options.add_argument("--disable-extensions") chrome_options.add_argument("--disable-popup-blocking") # headless mode is required for this method of printing chrome_options.add_argument("--headless") # disable the banner "Chrome is being controlled by automated test software" chrome_options.add_experimental_option("useAutomationExtension", False) chrome_options.add_experimental_option("excludeSwitches", ['enable-automation']) driver = webdriver.Chrome('/usr/local/bin/chromedriver', options=chrome_options) # replace this code with your button code ### driver.get('https://abcnews.go.com') urls = ['https://www.bbc.com/news', 'https://www.cbsnews.com/', 'https://www.cnn.com', 'https://www.newsweek.com'] for url in urls: driver.execute_script(f"window.open('{url}','_blank')") # I'm using a sleep statement, which can be replaced with # driver.implicitly_wait(x_seconds) or even a # driver.set_page_load_timeout(x_seconds) statement sleep(5) ### # A4 print parameters params = {'landscape': False, 'paperWidth': 8.27, 'paperHeight': 11.69} # get the open window handles, which in this care is 5 handles = driver.window_handles size = len(handles) # loop through the handles for x in range(size): try: driver.switch_to.window(handles[x]) # adjust the sleep statement as needed # you can also replace the sleep with # driver.implicitly_wait(x_seconds) sleep(2) data = driver.execute_cdp_cmd("Page.printToPDF", params) with open(f'file_name_{x}.pdf', 'wb') as file: file.write(base64.b64decode(data['data'])) # adjust the sleep statement as needed sleep(3) except TimeoutException as error: print('something went wrong') print(''.join(traceback.format_tb(error.__traceback__))) driver.close() driver.quit()Aquí está una de mis respuestas anteriores que podría ser útil:
Como su caso de uso es imprimir cada uno en PDF utilizando la función Guardar como PDF integrada de Chrome o tomar una captura de pantalla , en lugar de abrir todos los enlaces adicionales al mismo tiempo, puede abrir los enlaces en la pestaña adyacente uno por uno y tomar la captura de pantalla utilizando las siguientes estrategias de localización :
Bloque de código:
num_tabs_to_open = len(elements_href) windows_before = driver.current_window_handle # open the links in the adjascent tab one by one to take screenshot for href in elements_href: i = 0 driver.execute_script("window.open('" + href +"');") windows_after = driver.window_handles new_window = [x for x in windows_after if x != windows_before][0] driver.switch_to.window(new_window) driver.save_screenshot(f"image_{str(i)}.png") driver.close() driver.switch_to.window(windows_before) i = i+1Puede encontrar una discusión detallada relevante en: