将 HTML 转换为 PNG,宽度取决于本地内容

问题描述

我需要根据内容将 HTML 文档转换为 PNG。 HTML 文档包含具有某种样式的图像,具体取决于图像/文档的大小。我一直在研究和尝试解决方案,但没有一个足够满足我的需求。例如,如果一个 HTML 文档包含一个 500x500 的图像和它下面的一些文本,我希望输出为 500px 宽以及图像和文本的高度。

wkhtmltoimage 是我找到的最接近这样的程序。它具有我需要的智能调整大小功能(只需将宽度设置为 1 并让它扩展以填充),但基于非常旧版本的 webkit。它不支持 CSS3 calc()vw。此外,它是在 ECMAScript 5 上。尽管 ECMAScript 5 有多种获取文档宽度的方法,但 wkhtmltoimage 不支持其中任何一种,它们都返回 0。我的文本大小取决于文档的宽度,因此我需要支持

我发现的所有其他解决方案似乎都不支持智能调整大小,因为它们基于无头浏览器。但是,我可能会误解这些,它们可能支持我正在寻找的内容

对于那些好奇的人,我的实际实现是在一个 python 脚本中,该脚本将字符串 HTML 文档通过管道传输到程序中,并将 png 发送到它的其他部分。不过,我不介意在那里工作。

TL;DR 是否有一些本地程序可以实现我想要的:将 HTML 文档转换为支持 vwcalc 和智能宽度的 PNG 文件

解决方法

这是我能够创建的最佳解决方案。它使用硒和铬。需要注意的一点很重要:因为启动 selenium 需要很长时间,所以我只初始化它一次。确保如果您在 asyncio 循环内执行此代码或其他东西来获取锁,那么它一次只做一件事。

此外,您需要修改 temp_file() 函数或在工作文件夹中创建一个 temp/ 目录。

这意味着在它自己的文件中,一旦导入就使用其他文件中的 html2png()。如前所述,启动 selenium 需要一段时间,因此导入时它会停止几秒钟。

import json
import os
import random
import string
import sys

from selenium import webdriver


def send(driver,cmd,params=None):
    if params is None:
        params = {}
    resource = "/session/%s/chromium/send_command_and_get_result" % driver.session_id
    url = driver.command_executor._url + resource
    body = json.dumps({'cmd': cmd,'params': params})
    response = driver.command_executor._request('POST',url,body)
    # if response['status']: raise Exception(response.get('value'))
    return response.get('value')


def get_random_string(length):
    return ''.join(random.choice(string.ascii_letters) for i in range(length))


def temp_file(extension="png"):
    while True:
        name = f"temp/{get_random_string(8)}.{extension}"
        if not os.path.exists(name):
            return name


def loadhtml(driver,html):
    base = "file:///" + os.getcwd().replace("\\","/")
    # html = html.replace("<base href='./'>",f"<base href='{base}/'>")
    html = f"<base href='{base}/'>" + html
    file = temp_file("html")
    with open(file,"w+") as f:
        f.write(html)
    driver.get("file:///" + os.path.abspath(file).replace("\\","/"))
    return file
    # print(json.dumps(html))
    # print(html)
    # html_bs64 = base64.b64encode(html.encode('utf-8')).decode()
    # driver.get("data:text/html;base64," + html_bs64)


opts = webdriver.ChromeOptions()
opts.headless = True
opts.add_experimental_option('excludeSwitches',['enable-logging'])
opts.add_argument('--no-proxy-server')
opts.add_argument("--window-size=0,0")
opts.add_argument("--hide-scrollbars")
opts.add_argument("--headless")
opts.add_argument("--disable-web-security")
opts.add_argument("--allow-file-access-from-files")
opts.add_argument("--allow-file-access-from-file")
opts.add_argument("--allow-file-access")
opts.add_argument("--disable-extensions")
# https://chromedriver.storage.googleapis.com/index.html?path=87.0.4280.88/
if sys.platform == "win32":
    driver = webdriver.Chrome("chromedriver87.exe",options=opts)
else:
    driver = webdriver.Chrome("chromedriver87",options=opts)


def html2png(html,png):
    driver.set_window_size(1,1)
    tempfile = loadhtml(driver,html)
    func = """
            function outerHeight(element) {
        const height = element.offsetHeight,style = window.getComputedStyle(element)

        return ['top','bottom']
            .map(function (side) {
                return parseInt(style["margin-"+side]);
            })
            .reduce(function (total,side) {
                return total + side;
            },height)
    }"""
    size = driver.execute_script(f"{func};return [document.documentElement.scrollWidth,outerHeight(document.body)];")
    driver.set_window_size(size[0],size[1])
    size = driver.execute_script(f"{func};return [document.documentElement.scrollWidth,size[1])
    send(driver,"Emulation.setDefaultBackgroundColorOverride",{'color': {'r': 0,'g': 0,'b': 0,'a': 0}})
    driver.get_screenshot_as_file(png)
    os.remove(tempfile)

# html2png("<p>test</p>","test.png")

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...