Java批量下载书籍图片并保存为PDF的方法

背景

因为经常出差火车上没网、不方便电子书阅读器批注,需要从某网站上批量下载多本书籍的图片并自动打包成PDF文件。

分析

1、尝试获得图片地址,发现F12被禁
解决方法:使用Chrome浏览器,点击右上角三个点呼出菜单,选择“更多工具”->“开发者工具”
或者使用Ctrl+Shift+C、Ctrl+Shift+I
2、审查元素,发现图片地址非常有规律:
在class为side-image的div里有一个img,src是../files/mobile/1.jpg?220927153454,去掉后面的问号部分即可得到/files/mobile/1.jpg,通过观察,这本书一共有多少页就会有多少个.jpg文件
3、回到栏目页,可得到基目录,所以批量抓取的大致思路是从栏目页获得基目录,然后不断累加一个数,直到获得jpg时对方服务器报404错误,即可得到刚刚处理的那一页即最后一页。
4、如何从栏目页获得基目录呢?
经观察,每个page_pc_btm_book_body里都有两个a标签,第一个是图片,第二个是“在线阅读”按钮,但是需要翻页怎么办呢?所以需要建立一个变量收集它们,每翻一页,做一次收集。于是可以写如下收集函数:

let books=[]
function catchBook() {let links = document.getElementsByClassName("page_pc_btm_book_body");for (let i in links) {if(!links[i].children||links[i].children.length<2)continue;let title = links[i].children[0].title;let link = links[i].children[0].href;books.push({title,link})}
}

然后在浏览器里每翻一页,在控制台里执行一次catchBook,这样书名和基目录就都获得了。
5、如何把JSON导出来呢
在控制台里JSON.stringify(books),把结果复制出来,然后到网上随便找一个JSON转Excel的工具,转出来即可,然后注意把第一行当表头,数据复制到第二行开始。
6、最后一步就写个程序从Excel里读出数据,把图片都批量抓下来即可,下面就说说如何写程序来处理。

需要引的包

<dependency><groupId>org.apache.poi</groupId><artifactId>poi</artifactId><version>4.1.0</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.0</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId><version>4.1.0</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-scratchpad</artifactId><version>4.1.0</version>
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>ooxml-schemas</artifactId><version>1.4</version>
</dependency>
<dependency><groupId>com.itextpdf</groupId><artifactId>itextpdf</artifactId><version>5.5.13.3</version>
</dependency>

从Excel到实体

先定义一个实体,这里我多加了一列type,表示类型,name就是从上面那个里面获得的title,link就是上面获得的link属性。

import lombok.Data;@Data
public class Book {private String type;private String name;private String link;
}

然后写个ExcelReader

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;public class ExcelReader {public static List<Book> readXlsxToList(String filePath) {List<Book> bookList = new ArrayList<>();try (FileInputStream fileInputStream = new FileInputStream(filePath);Workbook workbook = new XSSFWorkbook(fileInputStream)) {Sheet sheet = workbook.getSheetAt(0);Iterator<Row> rowIterator = sheet.iterator();// 获取表头(第一行)并转换为属性数组Row headerRow = rowIterator.next();String[] headers = getRowDataAsStringArray(headerRow);// 遍历每一行(从第二行开始)while (rowIterator.hasNext()) {Row row = rowIterator.next();Book book = new Book();// 遍历每个单元格,并根据属性名称设置对应的实体类属性值for (Cell cell : row) {int columnIndex = cell.getColumnIndex();if (columnIndex < headers.length) {String headerValue = headers[columnIndex];String cellValue = getCellValueAsString(cell);setBookProperty(book, headerValue, cellValue);}}bookList.add(book);}} catch (IOException e) {e.printStackTrace();}return bookList;}private static String[] getRowDataAsStringArray(Row row) {String[] rowData = new String[row.getLastCellNum()];for (Cell cell : row) {int columnIndex = cell.getColumnIndex();rowData[columnIndex] = getCellValueAsString(cell);}return rowData;}private static String getCellValueAsString(Cell cell) {String cellValue = "";if (cell != null) {switch (cell.getCellType()) {case STRING:cellValue = cell.getStringCellValue();break;case NUMERIC:cellValue = String.valueOf(cell.getNumericCellValue());break;case BOOLEAN:cellValue = String.valueOf(cell.getBooleanCellValue());break;case FORMULA:cellValue = cell.getCellFormula();break;default:cellValue = "";}}return cellValue;}private static void setBookProperty(Book book, String propertyName, String propertyValue) {switch (propertyName) {case "type":book.setType(propertyValue);break;case "name":book.setName(propertyValue);break;case "link":book.setLink(propertyValue);break;// 添加其他属性default:// 未知属性,可以根据需要进行处理break;}}
}

从实体集合到批量下载成jpg

还需要想办法实现批量下载的功能,需要注意的是Windows的默认文件排序是按ASC码排序的,会把10.jpg排在2.jpg前面,所以需要对页码格式化一下,把它变成三位数。

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;public class ImageDownloader {public static void downloadImages(List<Book> bookList, String targetDir) {for (Book book : bookList) {String type = book.getType();String name = book.getName();String link = book.getLink();String basePath = targetDir + "/" + type + "/" + name;int count = 1;boolean continueDownload = true;if(!new File(basePath).exists()){new File(basePath).mkdirs();}while (continueDownload) {String imgUrl = link + "files/mobile/" + count + ".jpg";String outputPath = String.format("%s/%03d.jpg", basePath, count);if (!imageExists(outputPath)) {try {downloadImage(imgUrl, outputPath);System.out.println("Downloaded: " + outputPath);} catch (IOException e) {System.out.println("Error downloading image: " + imgUrl);e.printStackTrace();continueDownload = false;}} else {System.out.println("Image already exists: " + outputPath);}count++;}}}private static boolean imageExists(String path) {Path imagePath = Paths.get(path);return Files.exists(imagePath);}private static void downloadImage(String imageUrl, String outputPath) throws IOException {URL url = new URL(imageUrl);HttpURLConnection httpConn = (HttpURLConnection) url.openConnection();int responseCode = httpConn.getResponseCode();if (responseCode == HttpURLConnection.HTTP_OK) {try (InputStream inputStream = httpConn.getInputStream();FileOutputStream outputStream = new FileOutputStream(outputPath)) {byte[] buffer = new byte[4096];int bytesRead;while ((bytesRead = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, bytesRead);}}} else {throw new IOException("Server returned response code " + responseCode);}}
}

开始批量下载

import java.util.List;public class Test {public static void main(String[] args) {List<Book> books = ExcelReader.readXlsxToList("C:\\Users\\Administrator\\Desktop\\某某书库.xlsx");String targetDir = "D:\\书库\\";ImageDownloader.downloadImages(books, targetDir);}
}

写完执行,回去睡一觉

jpg图片批量转成pdf

都下载完之后,就可以想办法批量转成PDF格式了。

import com.itextpdf.text.*;
import com.itextpdf.text.pdf.PdfWriter;import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.MalformedURLException;public class ImageToPdfConverter {public static void convertToPdf(String folderPath, String outputFilePath) {try {// 获取文件夹中的所有jpg文件File folder = new File(folderPath);File[] files = folder.listFiles((dir, name) -> name.toLowerCase().endsWith(".jpg"));// 预读第一章图片获得大小Rectangle rect = null;if (files.length == 0) {return;} else {Image image = Image.getInstance(files[0].getAbsolutePath());rect = new Rectangle(image.getWidth(), image.getHeight());}// 创建PDF文档对象Document document = new Document(rect);document.setMargins(0, 0, 0, 0);// 创建PDF写入器PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream(outputFilePath));writer.setStrictImageSequence(true);// 打开PDF文档document.open();// 遍历图片文件并将其加入到PDF文档中for (File file : files) {Image image = Image.getInstance(file.getAbsolutePath());document.add(image);}// 关闭PDF文档document.close();System.out.println("PDF文件生成成功!");} catch (FileNotFoundException | DocumentException e) {e.printStackTrace();} catch (MalformedURLException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);}}public static void main(String[] args) {String startDir="D:\\书库\\开发技术\\";File[] subdirs = new File(startDir).listFiles();for (File subdir : subdirs) {if(subdir.isDirectory()){convertToPdf(subdir.getAbsolutePath(), subdir.getAbsolutePath()+".pdf");}}}
}

结束

最后把PDF文件传到网盘上,手机、平板、电脑随时可以下载离线看,非常舒服。

注意:自己抓取书籍自己看无所谓,但通过网络分享出去是侵犯他人著作权的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/34413.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++学习系列】1.小谷记账踩坑记

文章目录 前言1.基础支持2. 几个小坑2.1 为什么要用引用传值2.2 头文件的作用2.3 while true的使用和跳出 3. 未解决的问题 前言 是尚硅谷的C第一季的项目&#xff0c;我跟着敲下来了&#xff0c;发现几个坑点&#xff0c;记录下来&#xff1b; 1.基础支持 有这个则只require…

大华智慧园区综合管理平台文件上传漏洞复现(HW0day)

0x01 产品简介 “大华智慧园区综合管理平台”是一款综合管理平台&#xff0c;具备园区运营、资源调配和智能服务等功能。平台意在协助优化园区资源分配&#xff0c;满足多元化的管理需求&#xff0c;同时通过提供智能服务&#xff0c;增强使用体验。 0x02 漏洞概述 大华智慧园…

Vue+SpringBoot项目开发:登录页面美化,登录功能实现(三)

写在开始:一个搬砖程序员的随缘记录上一章写了从零开始VueSpringBoot后台管理系统&#xff1a;Vue3TypeScript项目搭建 VueTypeScript的前端项目已经搭建完成了 这一章的内容是引入element-plus和axios实现页面的布局和前后端数据的串联&#xff0c;实现一个登陆的功能&#x…

DocX 生成Word

当然&#xff0c;这里是一个使用DocX库在.NET Core中操作Word文档的简单示例&#xff1a; 首先&#xff0c;确保你在项目中安装了DocX库。你可以在NuGet包管理器中搜索并安装DocX。 然后&#xff0c;使用以下代码来创建一个简单的Word文档并添加一些内容&#xff1a; using …

「何」到底该读「なん」还是「なに」?柯桥学日语

「何」到底该读「なん」还是「なに」&#xff1f; 首先&#xff0c;讲一个规律&#xff0c;大家记住就行。当「何」后面所接单词的第一个发音在“た”、“だ”、“な”行时&#xff0c;读作“なん”。一般这种情况下&#xff0c;后面跟的是の、でも、です和だ。 用例&#xff…

php后端实现调用高德地图进行POI搜索

对于当前位置或者选定省市位置进行查询 接口实现 /*** 查询地址* ApiTitle (查询地址)* ApiSummary (查询地址)* ApiMethod (POST)* ApiRoute (/api/demo/address)* ApiParams (name"dart", type"integer", requiredtrue, description"省…

【博客694】k8s kubelet 状态更新机制

k8s kubelet 状态更新机制 场景&#xff1a; 当 Kubernetes 中 Node 节点出现状态异常的情况下&#xff0c;节点上的 Pod 会被重新调度到其他节点上去&#xff0c;但是有的时候我们会发现节点 Down 掉以后&#xff0c;Pod 并不会立即触发重新调度&#xff0c;这实际上就是和 K…

软件设计基础

巩固基础&#xff0c;砥砺前行 。 只有不断重复&#xff0c;才能做到超越自己。 能坚持把简单的事情做到极致&#xff0c;也是不容易的。 软件项目管理。 在经历了软件危机和大连的软件项目失败以后&#xff0c;人们对软件工程专业的现状进行了多次分析。得出了普遍性的结论&…

【软件工程】数据流图/DFD概念符号/流程图分层/数据字典

【软件工程】数据流图/DFD概念符号/流程图分层/数据字典 目录 【软件工程】数据流图/DFD概念符号/流程图分层/数据字典 一、数据流图 ( DFD ) 简介 二、数据流图 ( DFD ) 概念符号 1、数据流 2、加工 ( 核心 ) 3、数据存储 4、外部实体 三、数据流图 ( DFD ) 分层 1、…

python制作小程序制作流程,用python编写一个小程序

这篇文章主要介绍了python制作小程序代码宠物运输&#xff0c;具有一定借鉴价值&#xff0c;需要的朋友可以参考下。希望大家阅读完这篇文章后大有收获&#xff0c;下面让小编带着大家一起了解一下。 1 importtkinter2 importtkinter.messagebox3 importmath4 classJSQ:5 6 7 d…

019-从零搭建微服务-认证中心(八)

写在最前 如果这个项目让你有所收获&#xff0c;记得 Star 关注哦&#xff0c;这对我是非常不错的鼓励与支持。 源码地址&#xff08;后端&#xff09;&#xff1a;https://gitee.com/csps/mingyue 源码地址&#xff08;前端&#xff09;&#xff1a;https://gitee.com/csps…

并发编程--------JUC集合

并发集合 一、ConcurrentHashMap 1.1 存储结构 ConcurrentHashMap是线程安全的HashMap ConcurrentHashMap在JDK1.8中是以CASsynchronized实现的线程安全 CAS&#xff1a;在没有hash冲突时&#xff08;Node要放在数组上时&#xff09; synchronized&#xff1a;在出现hash…

运算符重载

这里写目录标题 运算符重载在全局范围内重载运算符运算符重载时要遵循的规则运算符重载到底以成员函数的形式更好还是全局函数&#xff08;友元函数&#xff09;的形式更好重载例题&#xff08;属于友元函数的 运算符重载函数&#xff09; 运算符重载 运算符重载其实就是定义一…

Python 向Excel写数据

1.项目终端导入 xlwt 库 pip install xlwt2.导入依赖包 import xlwt3.创建Excel表格类型文件 调用xlwt模块中的Workbook方法来创建一个excel表格类型文件&#xff0c;其中的第一个参数是设置数据的编码格式&#xff0c;这里是’utf-8’的形式&#xff0c;style_compression设…

022 - STM32学习笔记 - 扩展外部SDRAM(一) - 初识SDRAM和FMC

022 - STM32学习笔记 - 扩展外部SDRAM&#xff08;一&#xff09; - 初识SDRAM和FMC 之前学习了I2C读写EEPROM和SPI读写FLASH&#xff0c;学完之后在学习一种新的存储介质–SDRAM。 一、初识SDRAM 我们知道在stm32内部是有一定大小的SRAM&#xff08;256Kb&#xff09;和FLA…

无人驾驶实战-第十二课(强化学习自动驾驶系统)(完)

在七月算法上报了《无人驾驶实战》课程&#xff0c;老师讲的真好。好记性不如烂笔头&#xff0c;记录一下学习内容。 课程入口&#xff0c;感兴趣的也可以跟着学一下。 ————————————————————————————————————————— 强化学习&#xff…

c++日志工具之——log4cpp

1、log4cpp概述 Log4cpp是一个开源的C类库&#xff0c;它提供了C程序中使用日志和跟踪调试的功能&#xff0c;它的优点如下&#xff1a; 提供应用程序运行上下文&#xff0c;方便跟踪调试&#xff1b; 可扩展的、多种方式记录日志&#xff0c;包括命令行、文件、回卷文件、内…

SA8000 社会责任要求之健康安全准则

【SA8000 社会责任要求之健康安全准则】 健康和安全 准则 3.1 组织应提供一个安全和健康的工作环境&#xff0c;并应采取有效的措施防止潜在的健康和安全事故和职业伤害&#xff0c;或在工作的过程中发生的或引起的疾病。基于产业相关的安全与健康的知识以及任何特定的危害&…

阿里云服务器搭建WordPress建站教程基于Windows系统

本教程是使用阿里云服务器镜像系统选择的是Windows操作系统&#xff0c;手动安装WordPress博客网站全过程。本教程介绍如何在Windows操作系统的ECS实例上搭建WordPress网站。 目录 准备工作 搭建WordPress网站 解析WordPress网站域名 准备工作 创建Windows操作系统的ECS实…

Docker mysql+nacos单机部署

docker 网络创建 由于nacos需要访问mysql的数据&#xff0c;因此mysql容器和nacos容器之间需要进行通信。容器间通信有很多方式&#xff0c;在这里采用同一网络下的方式进行实现。因此需要创建网络。创建网络的命令如下&#xff1a; docker network create --driver bridge n…