需求背景
本站部分代码在没有登录的时候,为了防止爬虫抄袭,代码文本是以图片展示的。这个需求就导致我们需要一个将HTML转为图片的工具。
最早的时候,我们采用了网上开源的html2image
,使用了一段时间后发现了如下几个问题:
- 这个开源项目实在是太老了,已经很多很多年不更新了。
- 兼容性很差。由于长时间不更新,一些新的css样式等,是完全无法正常显示的。
- 没人维护,导致有很多安全性很严重的BUG。
基于上面的这些痛点,我们一直在积极的寻找替代方案。在看了很多方案后,发现都没办法完美的解决上面的问题。其中最难的就是,如何完美的显示各种复杂的HTML网页?你会发现,真正能做到持续兼容各种HTML和CSS、JS的,只有我们常用的浏览器。因为这个工作量巨大,只能交给大厂来做。目前开源的HTML转图片的方案,随着时间推移,大概率都会出问题。
如何一劳永逸的解决HTML转图片Image的问题呢?
解决办法
既然只有浏览器能完美的展示HTML,那我们就用浏览器来显示HTML,然后调用浏览器的API截图。这样就实现了HTML转图片的需求了。
我们采用的技术方案是java+selenium+chrome+chromedriver,这套技术组合其实更多的是用在自动化测试里,主要用于编写程序,对网页进行点击等自动化的操作。经过测试,这套方案,完美的解决了前面提出的需求和问题。
小缺陷:和传统方案不同,因为该方案需要浏览器支持,所以服务器上安装了chrome,算是一点小缺陷吧。
代码展示
由于不确定本文介绍的方案读者是否需要,这里的代码只展示核心部分,如果你想要完整的代码,可以联系作者。
package com.coderbbb.blogv2.utils;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.FileUtils;
import org.openqa.selenium.*;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverLogLevel;
import org.openqa.selenium.chrome.ChromeOptions;
import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
public class SeleniumUtil {
public static WebDriver webDriver;
public static ConcurrentHashMap<String, String> HTML_IMAGE_CACHE = new ConcurrentHashMap<>();
public synchronized static void init() {
if (webDriver == null) {
System.setProperty("webdriver.chrome.driver", "D:\\software\\chrome-driver\\chromedriver.exe");
ChromeOptions chromeOptions = new ChromeOptions();
chromeOptions.setLogLevel(ChromeDriverLogLevel.WARNING);
// 设置后台静默模式启动浏览器
chromeOptions.setHeadless(true);
chromeOptions.setExperimentalOption("excludeSwitches", Collections.singletonList("enable-automation"));
Map<String, Object> prefs = new HashMap<>();
prefs.put("credentials_enable_service", false);
prefs.put("profile.password_manager_enabled", false);
// chromeOptions.addArguments("--headless");
chromeOptions.addArguments("--disable-blink-features");
chromeOptions.addArguments("--disable-blink-features=AutomationControlled");
chromeOptions.setExperimentalOption("prefs", prefs);
chromeOptions.addArguments("--no-sandbox");
chromeOptions.addArguments("--disable-dev-shm-usage");
chromeOptions.addArguments("--incognito");
chromeOptions.addArguments("--disable-gpu");
chromeOptions.addArguments("--ignore-certificate-errors");
chromeOptions.addArguments("--ignore-ssl-errors");
String userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36";
userAgent = "user-agent=" + userAgent;
chromeOptions.addArguments(userAgent);
chromeOptions.addArguments("--start-maximized");
webDriver = new ChromeDriver(chromeOptions);
//设置窗口大小
Dimension dimension = new Dimension(1000, 30);
webDriver.manage().window().setSize(dimension);
}
}
public static WebDriver getWebDriver() {
if (webDriver == null) {
init();
}
return webDriver;
}
public static void main(String[] args) throws Exception {
getWebDriver().get("https://www.coderbbb.com");
File file = ((TakesScreenshot) getWebDriver()).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(file, new File("./code2.png"));
}
}
上面的代码中,main
方法就是直接调用selenium,打开一个网页,然后截图保存。
问题回复:
-
如何将一段HTML转化为图片?答:如果你需要将具体的一段HTML转化为图片,你可以自己写一个可以访问的网页,把这段HTML传入,再使用前面的办法截图。
-
如果网页很长,怎么全屏截图?答:selenium使用
HeadLess
模式(就是不显示界面,后台静默执行)时,可以任意设置浏览器窗口大小,那么你设置浏览器高度和你的HTML网页高度一致,再截图,就能得到完整的网页截图了。这个方案最简单,不用考虑滚动条等等的问题。 -
怎么获取HTML网页的高度?答:该问题是问题2的后续。主要是通过selenium来执行JavaScript脚本
return document.documentElement.scrollHeight
来获取。代码展示如下://根据内容,重新设定窗口大小 JavascriptExecutor javascriptExecutor = (JavascriptExecutor) getWebDriver(); Long height = (Long) javascriptExecutor.executeScript("return document.documentElement.scrollHeight"); Dimension dimension = new Dimension(1000, height.intValue()); getWebDriver().manage().window().setSize(dimension);
这段代码的作用,就是通过执行JavaScript脚本,来获取HTML网页的高度,然后根据该高度调整浏览器窗口的高度,从而实现任意高度的网页都可以完整显示,避免出现滚动条影响截图。其中,
getWebDriver()
是我们封装的方法,可以参考前面提到的代码。
效果展示
本站所有代码片段,都是通过该技术实现从HTML文本到图片的转化的。就拿本文来说,上面的代码图片就是该技术的效果展示。