Python Selenium 为使用 document.write() 打开的窗口截取屏幕截图并另存为 PDF

for*_*ice 10 python selenium window-handles webpage-screenshot jupyter-notebook

我将 Selenium 与 Python 结合使用(在 Jupyter 笔记本中)。我打开了许多选项卡,比如说 5 个选项卡(所有元素都已完成加载),我想循环浏览它们并执行两件事:

  1. 截图一下,
  2. (作为奖励)使用 Chrome 内置的另存为 PDF 功能,使用 A4 横向、正常缩放和指定的默认目录将每个打印为 PDF,无需用户交互。

(在下面的代码中,我重点关注屏幕截图要求,但也非常想知道如何将其另存为 PDF)

此代码允许循环浏览选项卡:

numTabs = len(driver.window_handles)
for x in range(numTabs):
    driver.switch_to.window(driver.window_handles[x])    
    time.sleep(0.5)     
Run Code Online (Sandbox Code Playgroud)

但是,如果我尝试添加driver.save_screenshot()如下所示的调用,代码似乎在拍摄第一个屏幕截图后停止。具体来说,为第一个选项卡(索引为 0)创建“0.png”,并切换到下一个选项卡(索引为 1),但停止进一步处理。它甚至不会循环到下一个选项卡。

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)    
Run Code Online (Sandbox Code Playgroud)

Edit1: 我修改了代码,如下所示,开始从 中截取屏幕截图,window_handles[1]因为[0]我实际上并不需要 中的屏幕截图[0],但现在甚至没有生成一个屏幕截图。因此,save_screenshot()即使在初次通话后,该通话似乎也不起作用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.
Run Code Online (Sandbox Code Playgroud)

Edit2: 我发现了为什么我的代码“挂起”,无论我使用哪种打印到 PDF 或截取屏幕截图的方法。我之前提到过,新选项卡是通过单击主页上的按钮打开的,但经过仔细检查,我现在看到新选项卡的内容是使用document. write(). 有一些 ajax 代码可以检索waybillHTML内容,然后使用以下命令将其写入新窗口document.write(waybillHTML)

有关更多上下文,这是一个订单系统,其主页包含订单列表,每个订单旁边有一个按钮,用于打开一个带有运单的新选项卡。重要的是,运单实际上是document. write()通过单击按钮触发生成的。我注意到在新选项卡中右键单击时“查看页面源代码”选项呈灰色。当我用来switch_to.window()切换到这些选项卡之一时,会在 300 秒(我想是秒)后Page.printToPDF 超时。

---------------------------------------------------------------------------
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)
Run Code Online (Sandbox Code Playgroud)

所以我的精致问题应该是如何Page.printToPDF在新窗口(使用动态生成document. write())中打印页面而不超时

我尝试的一种方法是这样做:

from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
caps = DesiredCapabilities().CHROME
caps["pageLoadStrategy"] = "none"
driver = webdriver.Chrome(options=chrome_options, desired_capabilities=caps)
Run Code Online (Sandbox Code Playgroud)

参考:这个问题

但问题是这太“激进”,并且阻止代码登录到订购系统并进行必要的导航和过滤,以使浏览器到达我获得带有运单生成按钮的订单列表页面(即原始在这个问题中设置)。

Edit3: 此时,我尝试了一些简单的方法,例如获取页面源代码

try:
    pageSrc = driver.find_element(By.XPATH, "//*").get_attribute("outerHTML")
    print(pageSrc)
Run Code Online (Sandbox Code Playgroud)

动态生成的选项卡(在它们完成渲染很久之后,我可以在屏幕上看到内容(在调试的这个阶段不使用无头)),甚至这本身也会抛出 TimeoutException,所以我不认为这是一个等待内容加载的问题。不知何故,驱动程序无法看到内容。这些页面的生成方式可能有些特殊 - 我不知道。我确信答案中建议的用于截取屏幕截图和保存 PDF 的所有方法对于其他正常窗口来说都很好。在 Chrome 中,该View page source内容仍然呈灰色,但我可以使用Inspect.

Edit4: 使用Chrome的检查功能,动态生成页面的页面源具有以下HTML结构:

动态生成页面的HTML结构

奇怪的是,即使我能够检查内容,“查看页面源代码”仍然呈灰色: 在此输入图像描述

Edit5: 终于弄清楚了。当我单击生成新选项卡的按钮时,网站将进行 ajax 调用来获取新页面的 HTML 内容,并将document.write其发送到新选项卡。我想这超出了硒处理的范围。相反,我导入了selenium-wire,用于driver.wait_for_request拦截 ajax 调用,解析包含新选项卡的 HTML 代码的响应,并将清理后的 HTML 转储到新文件中。从那时起,可以使用其他人已经建议的多种方法轻松处理 PDF 的生成。

Deb*_*anB 3

由于您的用例是使用 Chrome 内置的另存为 PDF 功能将每个打印为 PDF拍摄屏幕截图,因此您可能希望一一打开相邻选项卡中的链接并拍摄屏幕截图,而不是同时打开所有附加链接使用以下定位器策略的屏幕截图:

  • 代码块:

    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+1
    
    Run Code Online (Sandbox Code Playgroud)

参考

您可以在以下位置找到相关的详细讨论: