Java根据URL截图的4种方式

河曲智叟  金牌会员 | 2023-12-7 01:51:22 | 来自手机 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 908|帖子 908|积分 2724

方案选择


  • XHTMLRenderer(不要用)
  • PhantomJs(三方库,已停更)
  • Puppeteer(Chrome团队开发和维护)
  • Selenium(支持多浏览器、多语言,服务器需要安谷歌浏览器)
一、XHTMLRenderer(不要用)


  • XHTMLRenderer它是一个Java库,用于将XHTML文档渲染为图像或PDF格式。
  • 也不要用它来转PDF
1、XHTMLRenderer介绍

XHTMLRenderer(也被称为Flying Saucer)是一个用于将XHTML和CSS内容渲染为PDF或图像的库。然而,它的一个主要限制是它只支持一部分的CSS 2.1规范,并且不支持HTML5和CSS3的许多特性。这意味着如果您的HTML内容使用了这些库不支持的特性,那么它可能无法正确地渲染这些内容。
此外,XHTMLRenderer需要输入的HTML内容是良好的XHTML。这意味着所有的标签都必须正确地关闭,属性值必须用引号括起来,等等。如果输入的HTML内容不符合这些规则,那么XHTMLRenderer可能无法正确地解析它。
如果您发现XHTMLRenderer无法满足您的需求,您可能需要考虑使用其他的库。
例如,PhantomJS(第二种)、Selenium(终极办法)、Puppeteer可以在无头浏览器环境中渲染HTML,并且支持最新的HTML和CSS规范。这些库可以更好地处理复杂的HTML内容,并且提供了更多的选项来控制渲染过程。
2、适用场景


  • 简单的可以解析的html
  • 不适用:复杂的网页生成的html
3、实现

3.0导包

  • gradle
  1.     implementation 'org.jsoup:jsoup:1.15.3'
  2.     // XHTMLRenderer 的核心包
  3.     // - 用于将XML、XHTML和CSS文档渲染为PDF、图像或Swing组件。
  4.     // - 它基于W3C标准和开放源代码的浏览器引擎,可以将HTML文档转换为可打印或可展示的格式。
  5.     implementation 'org.xhtmlrenderer:flying-saucer-core:9.1.18'
复制代码
3.1、通过JSOUP获取url的html
  1. Jsoup是一个Java库,用于处理HTML。它提供了一个非常方便的API,用于提取和操作数据,使用DOM,CSS和jquery-like方法。
  2. Jsoup的主要功能包括:
  3. 1. 从URL、文件或字符串中解析HTML。
  4. 2. 使用DOM和CSS选择器来查找、提取和操作数据。
  5. 3. 清理用户提交的内容,以防止跨站脚本攻击。
  6. 4. 输出整洁的HTML。
  7. Jsoup设计用于处理所有类型的HTML:从整洁的HTML5到混乱的实际生产HTML。
  8. Jsoup会将输入HTML解析为与浏览器相同的DOM结构,这使得它能够处理各种复杂的HTML结构。
复制代码

  • jsoup获取url的html
  1. package com.cc.urlgethtml.utils;
  2. import lombok.Data;
  3. import org.jsoup.Jsoup;
  4. import org.jsoup.nodes.Document;
  5. import java.io.IOException;
  6. /**
  7. * <p></p>
  8. *
  9. * @author CC
  10. * @since 2023/11/6
  11. */
  12. @Data
  13. public class JsoupUtils {
  14.     public static String getHtmlByJsoup(String url) {
  15.         Document doc;
  16.         try {
  17.             doc = Jsoup.connect(url).get();
  18.         } catch (IOException e) {
  19.             throw new RuntimeException(e);
  20.         }
  21.         return doc.html();
  22.     }
  23. }
复制代码
3.2、使用XHTMLRenderer
  1. /** <p>通过Java2DRenderer将html转为图片</p>
  2.      * <ol>
  3.      *     <li>通过jsoup根据url获取静态html</li>
  4.      *     <li>使用Java2DRenderer将静态html转为图片,浏览器下载</li>
  5.      * </ol>
  6.      *
  7.      * <li>优点:不用第三方插件。如果是标准的html页面,可以使用该方式</li>
  8.      * <li>缺点:复杂的html转不了,直接会报错</li>
  9.      */
  10.     @GetMapping("/getJava2DRenderer")
  11.     public String get1(HttpServletResponse response){
  12.         //url
  13.         String url = "http://www.baidu.com";
  14.         //一、通过jsoup获取url的html(获取的html不完整,而且是渲染前的)
  15. //        String html = JsoupUtils.getHtmlByJsoup(url);
  16.         //二、模拟从url获取的:非常简单的html。测了从富文本中获取的html有可能都识别不了。恶心。
  17.         String html = "<html><body><h1>Hello, World!</h1></body></html>";
  18.         String fileName = "文件名.png";
  19.         //转图片,有问题——复杂的html转不了
  20.         Document document;
  21.         try {
  22.             DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
  23.             document = builder.parse(new ByteArrayInputStream(html.getBytes()));
  24.         } catch (Exception e) {
  25.             throw new RuntimeException(e);
  26.         }
  27.         Java2DRenderer renderer = new Java2DRenderer(document, 480, 640);
  28.         BufferedImage img = renderer.getImage();
  29.         //方式一:浏览器下载
  30.         try {
  31.             response.setCharacterEncoding(StandardCharsets.UTF_8.name());
  32.             response.setContentType("image/png;charset=".concat(StandardCharsets.UTF_8.name()));
  33.             response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS,HttpHeaders.CONTENT_DISPOSITION);
  34.             response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
  35.                     "attachment; filename=".concat(
  36.                             URLEncoder.encode(fileName, StandardCharsets.UTF_8.name())
  37.                     ));
  38.             ServletOutputStream out = response.getOutputStream();
  39.             //把 BufferedImage 缓冲图像流 写到 输出流 out 中
  40.             ImageIO.write(img, "png", out);
  41.             out.close();
  42.         }catch(Exception e){
  43.             throw new RuntimeException(e.getMessage());
  44.         }
  45.         //方式二:写入本地
  46.         /*FSImageWriter imageWriter = new FSImageWriter();
  47.         try {
  48.             imageWriter.write(img, "D:\\ABC.jpg");
  49.         } catch (IOException e) {
  50.             throw new RuntimeException(e);
  51.         }*/
  52.         return html;
  53.     }
复制代码

  • 截图

二、PhantomJs(我用的)


  • 我使用的,已经停更,建议使用Puppeteer
  • 是三方插件,需要使用Java调用三方插件
  • 需要下载这个插件,下载地址:https://phantomjs.org/download.html
  • 我用的版本:2.2.1
  • Windows、Linux都实现了
1、PhantomJs介绍

PhantomJS是一个无头浏览器,它使用WebKit布局引擎(与旧版的Safari和Google Chrome相同)进行页面渲染。"无头"意味着它可以在没有用户界面的情况下运行浏览器,这对于自动化脚本和服务器环境非常有用。
PhantomJS的主要特性包括:

  • 原生支持多种Web标准:如DOM处理,CSS选择器,JSON,Canvas和SVG。
  • 页面自动化:可以通过JavaScript API控制网页,包括加载和操作网页。
  • 屏幕捕获:可以将网页渲染为PDF或各种图片格式。
  • 网络监控:可以监控网络活动,用于性能分析,调试和测试。
虽然PhantomJS非常强大,但请注意,它的开发已于2018年停止,因此可能不支持最新的Web标准和技术。如果你需要一个持续更新并支持最新Web技术的无头浏览器,你可能需要考虑使用Puppeteer,它是一个由Google维护的,使用Chromium(Google Chrome的开源版本)作为后端的无头浏览器库。
2、PhantomJs安装(下载)后,直接去执行命令即可截图


  • Windows/Linux。都进入安装目录的bin目录
  • 语法:win/Linux程序 调用的js 截图的网站 截图后保存的地址
  1. phantomjs.exe ……\win\examples\rasterize.js http://www.baidu.com ……Test\bddddd11.png
  2. ./phantomjs ……\win\examples\rasterize.js http://www.baidu.com ……Test\bddddd11.png
复制代码

  • 例如:Windows(Linux一样的)

  • 能保存截图,说明可以使用:

3、Java代码实现phantomjs截图

3.1、思路

  • 单独在win/linux安装phantomjs
  • 根据不同系统生成不同的执行命令
  • 使用Java库Runtime.getRuntime().exec("cmd"):调用phantomjs即可
  • 最终完成截图。
3.1、将下载的包放入项目地址中,方便调用

3.2、修改rasterize.js文件
位置:.../examples/rasterize.js
  1. var page = require('webpage').create(),
  2.     system = require('system'),
  3.     address, output, size;
  4. //可以带cookie
  5. //var flag = phantom.addCookie({
  6. //        "domain": ".baidu.com" ,
  7. //        "expires": "Fri, 01 Jan 2038 00:00:00 GMT",
  8. //        "expiry": 2145916800,
  9. //        "httponly": false,
  10. //        "name": "token",
  11. //        "path": "/",
  12. //        "secure": false,
  13. //        "value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJVc2VySWQiOiJud19kemdkIiwiRXhwaXJlIjoiMjAyMy0wOC0xMCAxNDoyMzo0NyJ9.InsFJkcXI6C57r-1Oqb7PMn-OcP9k0W5lf1K896EasY"
  14. //});
  15. if (system.args.length < 3 || system.args.length > 5) {
  16.     phantom.exit(1);
  17. } else {
  18.     address = system.args[1];//传入url地址
  19.     output = system.args[2];//输出图片的地址
  20.     page.viewportSize = { width: 800, height: 1800 };//自定义定义宽高
  21.     if (system.args.length > 3 && system.args[2].substr(-4) === ".pdf") {
  22.         size = system.args[3].split('*');
  23.         page.paperSize = size.length === 2 ? { width: size[0], height: size[1], margin: '0px' }
  24.                                            : { format: system.args[3], orientation: 'portrait', margin: '1cm' };
  25.     } else if (system.args.length > 3 && system.args[3].substr(-2) === "px") {
  26.         size = system.args[3].split('*');
  27.         if (size.length === 2) {
  28.             pageWidth = parseInt(size[0], 10);
  29.             pageHeight = parseInt(size[1], 10);
  30.             page.viewportSize = { width: pageWidth, height: pageHeight };
  31.             page.clipRect = { top: 0, left: 0, width: pageWidth, height: pageHeight };
  32.         } else {
  33.             console.log("size:", system.args[3]);
  34.             pageWidth = parseInt(system.args[3], 10);
  35.             pageHeight = parseInt(pageWidth * 3/4, 10); // it's as good an assumption as any
  36.             console.log ("pageHeight:",pageHeight);
  37.             page.viewportSize = { width: pageWidth, height: pageHeight };
  38.         }
  39.     }
  40.     if (system.args.length > 4) {
  41.         page.zoomFactor = system.args[4];
  42.     }
  43.     page.open(address, function (status) {
  44.         if (status !== 'success') {
  45.             console.log('Unable to load the address!');
  46.             phantom.exit(1);
  47.         } else {
  48.             window.setTimeout(function () {
  49.                 page.render(output);
  50.                 phantom.exit();
  51.             }, 3000);
  52.         }
  53.     });
  54. }
  55. address = system.args[1];//传入url地址
  56. output = system.args[2];//输出图片的地址
  57. page.viewportSize = { width: 1200, height: 780 };//自定义定义宽高 (2000 * 1300 比较合适)
复制代码
3.3、application.yml配置文件配置phantomjs的位置地址
  1. # phantomjs的位置地址
  2. phantomjs:
  3.   binPath:
  4.     windows: plugins/phantomjs211/win/bin/phantomjs.exe
  5.     linux: /plugins/phantomjs211/linux/bin
  6.   jsPath:
  7.     windows: plugins/phantomjs211/win/examples/rasterize.js
  8.     linux: /plugins/phantomjs211/linux/examples/rasterize.js
  9.   imagePath:
  10.     windows: D:/Test
  11.     linux: /downImage
复制代码
3.4、获取配置文件的插件地址
  1. package com.cc.urlgethtml.utils;
  2. import lombok.Data;
  3. import org.springframework.beans.factory.annotation.Value;
  4. import org.springframework.stereotype.Component;
  5. /**
  6. * <p>根据不同系统获取不同路径</p>
  7. *
  8. * @author CC
  9. * @since 2023/11/3
  10. */
  11. @Data
  12. @Component
  13. public class PhantomPath {
  14.     @Value("${phantomjs.binPath.windows}")
  15.     private String binPathWin;
  16.     @Value("${phantomjs.jsPath.windows}")
  17.     private String jsPathWin;
  18.     @Value("${phantomjs.binPath.linux}")
  19.     private String binPathLinux;
  20.     @Value("${phantomjs.jsPath.linux}")
  21.     private String jsPathLinux;
  22.     @Value("${phantomjs.imagePath.windows}")
  23.     private String imagePathWin;
  24.     @Value("${phantomjs.imagePath.linux}")
  25.     private String imagePathLinux;
  26.     //获取当前系统是否是Windows系统(不是就是服务器,服务器默认Linux系统(centos))
  27.     private static final Boolean IS_WINDOWS = System.getProperty("os.name").toLowerCase().contains("windows");
  28.     public String getImagePath() {
  29.         return IS_WINDOWS ? imagePathWin : imagePathLinux;
  30.     }
  31.     public String getBinPath() {
  32.         return IS_WINDOWS ? binPathWin : binPathLinux;
  33.     }
  34.     public String getJsPath() {
  35.         return IS_WINDOWS ? jsPathWin : jsPathLinux;
  36.     }
  37.     public boolean getIsWindows() {
  38.         return IS_WINDOWS;
  39.     }
  40. }
复制代码
3.5、PhantomTools工具
  1. package com.cc.urlgethtml.utils;
  2. import cn.hutool.core.util.StrUtil;
  3. import lombok.Data;
  4. import lombok.extern.slf4j.Slf4j;
  5. import org.springframework.stereotype.Component;
  6. import org.springframework.util.ResourceUtils;
  7. import javax.annotation.Resource;
  8. import java.io.*;
  9. import java.util.ArrayList;
  10. import java.util.List;
  11. /** <p>根据网页地址转换成图片</p>
  12. *  <p>条件:需要插件及js脚本</p>
  13. *  <p>缺陷:部分网址,截图样式部分还是有问题</p>
  14. * @since 2023/11/6
  15. * @author CC
  16. **/
  17. @Data
  18. @Slf4j
  19. @Component
  20. public class PhantomTools {
  21.     @Resource
  22.     private PhantomPath phantomPath;
  23.     // token
  24.     private static String cookie = "token=";
  25.     /**
  26.      * @param url 地址
  27.      * @param imageName 图片全名
  28.      * @since 2023/11/3
  29.      **/
  30.     public void printUrlScreen2jpg(String url, String imageName) throws IOException{
  31.         //执行命令,生成图片
  32.         String[] cmd = this.getCmd(url, imageName);
  33.         Process process;
  34.         if (phantomPath.getIsWindows()) {
  35.             //一个参数:直接执行命令()
  36.             process = Runtime.getRuntime().exec(cmd);
  37.         }else {
  38.             String linuxPath = getBasePath().concat(phantomPath.getBinPathLinux());
  39.             log.info("Linux执行目录:{}", linuxPath);
  40.             process = Runtime.getRuntime().exec(cmd, new String[]{}, new File(linuxPath));
  41.         }
  42.         //读取执行日志
  43.         InputStream inputStream = process.getInputStream();
  44.         BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
  45.         String msg;
  46.         while ((msg = reader.readLine()) != null) {
  47.             log.info("PhantomJs导出图片日志:{}", msg);
  48.         }
  49.         log.info("PhantomJs导出图片:{}", imageName);
  50.         //关闭流
  51.         close(process, reader);
  52.     }
  53.     // 生成cmd命令:可以是直接的字符串,也可以是string数组
  54.     public String[] getCmd(String url, String imageName) throws FileNotFoundException {
  55.         String basePath = getBasePath();
  56.         List<String> list = new ArrayList<>();
  57.         //Windows命令
  58.         if (phantomPath.getIsWindows()) {
  59.             list.add(basePath.concat(phantomPath.getBinPath()));
  60.             list.add(basePath.concat(phantomPath.getJsPath()));
  61.             list.add(url);
  62.             list.add(String.format("%s/%s", phantomPath.getImagePath(), imageName));
  63.         }
  64.         //Linux命令
  65.         else {
  66.             list.add("./phantomjs");
  67.             list.add(phantomPath.getJsPath());
  68.             list.add(url);
  69.             list.add(String.format("%s/%s", phantomPath.getImagePath(), imageName));
  70.         }
  71.         log.info("PhantomJs导出图片的cmd:{}", list);
  72.         return list.toArray(new String[0]);
  73.     }
  74.     //获取项目跟目录的:plugins目录
  75.     private static String getBasePath() throws FileNotFoundException {
  76.         String basePath = ResourceUtils.getURL("plugins").getPath();
  77.         return StrUtil.removePrefix(basePath, "/");
  78.     }
  79.     //关闭命令
  80.     public static void close(Process process, BufferedReader bufferedReader) throws IOException {
  81.         if (bufferedReader != null) {
  82.             bufferedReader.close();
  83.         }
  84.         if (process != null) {
  85.             process.destroy();
  86.         }
  87.     }
  88. }
复制代码
3.6、测试

  • controller
  1.    /** <p>PhantomJs截图<p>
  2.      * <p>截图到本地,未下载到浏览器<p>
  3.      * @return {@link String}
  4.      * @since 2023/11/6
  5.      * @author CC
  6.      **/
  7.     @GetMapping("/getPhantomJs")
  8.     public void getPhantomJs(){
  9.         String url = "https://www.baidu.com/";
  10.         try {
  11.             phantomTools.printUrlScreen2jpg(url, "第一个图片.png");
  12.         } catch (Exception e) {
  13.             throw new RuntimeException(e);
  14.         }
  15.     }
复制代码

  • 成功

  • 导出京东等网站的缺陷(很多js无法、图片无法加载)

三、Puppeteer

3.1、Puppeteer介绍
Puppeteer是一个由Google Chrome团队官方提供的Node.js库,它提供了一组API来通过DevTools协议控制Chromium或Chrome。在大多数情况下,它可以用来替代PhantomJS。
Puppeteer的主要功能包括:

  • 生成页面的屏幕截图和PDF。
  • 爬取SPA(单页应用)并生成预渲染内容(即“SSR”(服务器端渲染))。
  • 自动表单提交,UI测试,键盘输入等。
  • 创建一个最新的自动化测试环境,使用最新的JavaScript和浏览器功能,如Async/Await,ES6等。
  • 捕获网站的时间线,以便帮助诊断性能问题。
Puppeteer默认下载并使用特定版本的Chromium,所以它对于最新的web API和特性的支持非常好。
3.2、实现(未实现)
四、Selenium


  • 相当于用户在操作浏览器
4.1、Selenium介绍
Selenium是一个流行的开源Web测试框架,它允许你编写脚本以自动化浏览器操作。这些脚本可以用于测试Web应用程序在各种浏览器中的行为,或者用于自动化重复的Web浏览任务。
Selenium支持多种编程语言,包括Java、C#、Python、Ruby和JavaScript。它还支持所有主流的Web浏览器,包括Chrome、Firefox、Safari和Internet Explorer。
Selenium有两个主要组件:

  • Selenium WebDriver:这是一个库,提供了一组API来编程式地控制浏览器。你可以使用这些API来模拟用户操作,如点击按钮、填写表单和导航页面。
  • Selenium Grid:这是一个工具,用于在多台机器和多种浏览器上并行运行测试。这对于测试Web应用程序在不同环境中的行为非常有用。
虽然Selenium主要用于测试,但它也可以用于Web爬虫,以自动化数据收集任务。
4.2、实现(未实现)
4.3、Puppeteer和Selenium区别
  1. Puppeteer和Selenium都可以用于网页截图,但它们各有优势。
  2. Puppeteer:
  3. - Puppeteer是由Chrome团队开发和维护的,因此它与Chrome浏览器的集成非常紧密,可以使用最新的Chrome特性。
  4. - Puppeteer的API设计得更为现代和简洁,使用Promise,可以很好地与现代JavaScript(如async/await)一起使用。
  5. - Puppeteer在截图和PDF生成方面的功能更为强大,例如,它可以很容易地截取全页面的屏幕截图,或者生成页面的PDF。
  6. Selenium:
  7. - Selenium支持多种编程语言和浏览器,如果你需要在多种浏览器或使用不同的编程语言进行截图,Selenium可能是更好的选择。
  8. - Selenium有一个大型的社区和大量的第三方库,这可能会使得解决问题和寻找已有的解决方案更为容易。
  9. 总的来说,如果你只需要在Chrome浏览器中进行截图,并且喜欢使用现代的JavaScript API,那么Puppeteer可能是更好的选择。如果你需要在多种浏览器中进行截图,或者使用Java、Python等其他编程语言,那么Selenium可能是更好的选择。
复制代码
五、总结

1、生成的命令

  • 生成cmd命令:可以是直接的字符串,也可以是string数组(推荐)
2、Process

  • 提供了与操作系统中的进程进行交互
  • 方法(执行系统命令):Runtime.getRuntime().exec()
  1. Runtime.getRuntime().exec(参数1)
  2. 参数1是:执行的命令
  3. Runtime.getRuntime().exec(参数1,参数2,参数3)
  4. 参数1是:执行的命令
  5. 参数3是:程序执行的文件夹,如:/phantomjs211/linux/bin
复制代码
3、代码地址
https://gitee.com/KakarottoChen/blog-code.git
……\blog-code\SpringBoot\UrlGetHtml
4、参考:

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

河曲智叟

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表