将 HTML 转换为 PNG,宽度取决于本地内容
Convert HTML to PNG with width dependent on content locally
我需要根据文档的内容将 HTML 文档转换为 PNG。 HTML 文档包含一张图像,其样式取决于 image/document 的大小。我一直在研究和尝试解决方案,但 none 非常适合我的需求。例如,如果一个 HTML 文档包含一个 500x500 的图像和它下面的一些文本,我希望输出为 500 像素宽以及图像和文本的高度。
wkhtmltoimage 是我找到的最接近这样的程序。它具有我需要的智能调整大小功能(只需将宽度设置为 1 并让它扩展以填充),但它基于非常旧版本的 webkit。它不支持 CSS3 calc()
或 vw
。此外,它在 ECMAScript 5 上。尽管 ECMAScript 5 有很多方法来获取文档的宽度,但 wkhtmltoimage 不支持其中任何一种,它们都是 return 0。我的文本大小取决于宽度文件,所以我需要支持。
我发现的所有其他解决方案似乎都不支持智能调整大小,因为它们基于无头浏览器。然而,我可能误解了这些,他们可能支持我正在寻找的东西。
对于那些好奇的人,我的实际实现是在一个 python 脚本中,该脚本将字符串 HTML 文档通过管道传输到程序中,并将 png 发送到程序的其他部分。不过,我不介意在那里工作。
TL;DR 是否有一些 local 程序可以实现我想要的:将 HTML 文档转换为支持 vw
的 PNG 文件,calc
,以及智能宽度?
这是我能够创建的最佳解决方案。它使用硒和铬。需要注意的非常重要的一点:因为启动 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, outerHeight(document.body)];")
driver.set_window_size(size[0], 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")
我需要根据文档的内容将 HTML 文档转换为 PNG。 HTML 文档包含一张图像,其样式取决于 image/document 的大小。我一直在研究和尝试解决方案,但 none 非常适合我的需求。例如,如果一个 HTML 文档包含一个 500x500 的图像和它下面的一些文本,我希望输出为 500 像素宽以及图像和文本的高度。
wkhtmltoimage 是我找到的最接近这样的程序。它具有我需要的智能调整大小功能(只需将宽度设置为 1 并让它扩展以填充),但它基于非常旧版本的 webkit。它不支持 CSS3 calc()
或 vw
。此外,它在 ECMAScript 5 上。尽管 ECMAScript 5 有很多方法来获取文档的宽度,但 wkhtmltoimage 不支持其中任何一种,它们都是 return 0。我的文本大小取决于宽度文件,所以我需要支持。
我发现的所有其他解决方案似乎都不支持智能调整大小,因为它们基于无头浏览器。然而,我可能误解了这些,他们可能支持我正在寻找的东西。
对于那些好奇的人,我的实际实现是在一个 python 脚本中,该脚本将字符串 HTML 文档通过管道传输到程序中,并将 png 发送到程序的其他部分。不过,我不介意在那里工作。
TL;DR 是否有一些 local 程序可以实现我想要的:将 HTML 文档转换为支持 vw
的 PNG 文件,calc
,以及智能宽度?
这是我能够创建的最佳解决方案。它使用硒和铬。需要注意的非常重要的一点:因为启动 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, outerHeight(document.body)];")
driver.set_window_size(size[0], 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")