使用Java Jsoup爬取网站时如何下载静态资源文件
实现网页静态资源同步下载方案
你现有的Jsoup爬取逻辑只保存了原始HTML,补充静态资源下载和路径替换逻辑即可实现浏览器「另存为」的效果,具体修改如下:
核心实现逻辑
- 为每个HTML文件创建同名的资源存储文件夹,命名规则参考浏览器默认规则:
[HTML文件名]_files - 解析HTML提取所有静态资源链接:图片、CSS、JS三类核心资源,可按需扩展字体、音视频等资源类型
- 将资源的相对路径统一转换为原网站的绝对路径,避免跨域或路径错误导致下载失败
- 下载资源到本地对应文件夹后,替换HTML中原资源链接为本地相对路径,保证本地打开HTML时可正常加载资源
完整修改后代码
import org.jsoup.Connection; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.*; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.UUID; private List<FileRelation> scrapUrls(List<String> urlsToScrap, String jobPath) throws IOException { List<FileRelation> files = new ArrayList<>(); Document doc; for (String url: urlsToScrap) { try { // 原请求HTML逻辑保留 Connection connection = Jsoup.connect(url) .userAgent("Mozilla/5.0 (X11; U; Linux i586; en-US; rv:1.7.3) Gecko/20040924 Epiphany/1.4.4 (Ubuntu)") .timeout(5000); doc = connection.get(); String baseUrl = new URL(url).getProtocol() + "://" + new URL(url).getHost(); // 生成HTML文件名和对应资源文件夹 String fileName = UUID.randomUUID() + ".html"; String assetFolderName = fileName.replace(".html", "_files"); String assetFolderPath = jobPath + File.separator + assetFolderName; new File(assetFolderPath).mkdirs(); // 提取并下载所有img资源 Elements imgs = doc.select("img[src]"); for (Element img : imgs) { String src = img.attr("src"); String absoluteSrc = getAbsoluteUrl(baseUrl, src); String localFileName = downloadAsset(absoluteSrc, assetFolderPath); if (localFileName != null) { img.attr("src", assetFolderName + "/" + localFileName); } } // 提取并下载所有CSS资源 Elements links = doc.select("link[rel=stylesheet][href]"); for (Element link : links) { String href = link.attr("href"); String absoluteHref = getAbsoluteUrl(baseUrl, href); String localFileName = downloadAsset(absoluteHref, assetFolderPath); if (localFileName != null) { link.attr("href", assetFolderName + "/" + localFileName); } } // 提取并下载所有JS资源 Elements scripts = doc.select("script[src]"); for (Element script : scripts) { String src = script.attr("src"); String absoluteSrc = getAbsoluteUrl(baseUrl, src); String localFileName = downloadAsset(absoluteSrc, assetFolderPath); if (localFileName != null) { script.attr("src", assetFolderName + "/" + localFileName); } } // 保存修改后的HTML BufferedWriter writer = new BufferedWriter( new FileWriter(jobPath + File.separator + fileName) ); writer.write(doc.html()); writer.close(); FileRelation file = new FileRelation(fileName, jobPath); files.add(file); } catch (Exception e) { System.out.println("ScarpingService - " + e); } } return files; } // 辅助方法:相对路径转绝对路径 private String getAbsoluteUrl(String baseUrl, String path) { if (path.startsWith("http")) { return path; } if (path.startsWith("//")) { return "https:" + path; } return baseUrl + (path.startsWith("/") ? path : "/" + path); } // 辅助方法:下载静态资源到本地文件夹 private String downloadAsset(String assetUrl, String saveFolderPath) { try { URL url = new URL(assetUrl); String fileName = new File(url.getPath()).getName(); // 处理文件名为空的情况 if (fileName.isBlank()) { fileName = UUID.randomUUID().toString(); } // 避免文件名冲突可加UUID前缀,不需要可以删掉 fileName = UUID.randomUUID() + "_" + fileName; File saveFile = new File(saveFolderPath + File.separator + fileName); try (InputStream in = url.openStream(); FileOutputStream out = new FileOutputStream(saveFile)) { byte[] buffer = new byte[4096]; int len; while ((len = in.read(buffer)) != -1) { out.write(buffer, 0, len); } } return fileName; } catch (Exception e) { // 资源下载失败直接跳过,不影响主流程 System.out.println("资源下载失败:" + assetUrl + ",错误信息:" + e.getMessage()); return null; } }
补充说明
- 代码里用
File.separator替代了硬编码的\\,适配Windows、Linux等不同操作系统的路径规则 - 资源下载失败时会直接跳过,保留原链接不替换,不会影响HTML的正常打开
- 如果需要下载CSS里引用的字体、背景图等资源,需要额外解析CSS文件内容提取对应链接下载,再替换CSS里的路径
- 可以根据需求调整静态资源的选择器,比如添加
video[src]、audio[src]等选择器下载音视频资源
内容的提问来源于stack exchange,提问作者Horatiuw
相关产品推荐
相关产品推荐

