如何用Java递归获取网站所有URL?现有递归实现异常
解决全站URL递归遍历的问题
我来帮你搞定这个全站遍历的问题!你现在的代码之所以只会重复刷首页,核心问题是没记下来哪些页面已经爬过,而且所谓的“递归”其实只是做了两层循环,根本没真正递归深入。咱们一步步改好它:
问题根源分析
你当前的逻辑是:访问首页→收集链接→循环访问每个链接→再收集该页面链接,但完全没记录已访问的URL——如果首页的链接里包含首页本身,就会无限重复访问;同时也没过滤外部链接(一不小心就爬到别的网站去了),更没有把递归逻辑封装成可复用的流程。
解决方案步骤
- 添加已访问URL跟踪:用
Set<String>存储爬过的URL,自动去重,避免循环爬取 - 封装递归核心方法:把“访问URL→收集有效链接→递归处理未访问链接”的逻辑打包成一个方法
- 修复链接过滤逻辑:过滤空链接、外部链接,还要注意
img标签的资源地址是src而非href(你之前的代码这里有小bug) - 完善工具方法:补全链接有效性验证的实现,修正泛型问题
修改后的完整代码
import org.openqa.selenium.By; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.firefox.FirefoxDriver; import java.net.URL; import java.net.HttpURLConnection; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; public class SiteCrawler { private static FirefoxDriver driver; // 记录已访问的URL,自动去重 private static Set<String> visitedUrls = new HashSet<>(); // 目标站点根域名,用来过滤外部链接 private static final String BASE_DOMAIN = "https://example.com/"; public static void main(String[] args) throws Exception { driver = new FirefoxDriver(); // 从首页开始启动全站爬取 crawlSite(BASE_DOMAIN); // 爬完记得关闭浏览器释放资源 driver.quit(); } /** * 递归爬取网站的核心方法 */ private static void crawlSite(String url) throws Exception { // 已经爬过的页面直接跳过 if (visitedUrls.contains(url)) { return; } // 标记当前URL为已访问 visitedUrls.add(url); System.out.println("正在爬取页面: " + url); // 访问当前页面 driver.get(url); // 收集当前页面的所有有效站内链接 List<String> pageLinks = findAllValidLinks(driver); // 输出当前页面的链接有效性报告 reportLinks(pageLinks); // 递归处理每个未访问的子链接 for (String link : pageLinks) { crawlSite(link); } } /** * 收集当前页面的所有有效站内链接(包含a标签和img标签) */ public static List<String> findAllValidLinks(WebDriver driver) { List<String> validLinks = new ArrayList<>(); String currentPageUrl = driver.getCurrentUrl(); // 处理a标签的href链接 List<WebElement> aElements = driver.findElements(By.tagName("a")); for (WebElement a : aElements) { String href = a.getAttribute("href"); if (href != null && !href.isEmpty()) { String absoluteUrl = convertToAbsoluteUrl(currentPageUrl, href); // 只保留本站域名下的链接 if (absoluteUrl.startsWith(BASE_DOMAIN)) { validLinks.add(absoluteUrl); } } } // 处理img标签的src资源地址(之前的代码误用了href,这里修正) List<WebElement> imgElements = driver.findElements(By.tagName("img")); for (WebElement img : imgElements) { String src = img.getAttribute("src"); if (src != null && !src.isEmpty()) { String absoluteUrl = convertToAbsoluteUrl(currentPageUrl, src); if (absoluteUrl.startsWith(BASE_DOMAIN)) { validLinks.add(absoluteUrl); } } } return validLinks; } /** * 把相对路径转换为绝对URL */ private static String convertToAbsoluteUrl(String baseUrl, String relativePath) { if (relativePath.startsWith("http")) { return relativePath; } // 简单处理相对路径,复杂场景可以用URI类来处理 return baseUrl.endsWith("/") ? baseUrl + relativePath : baseUrl + "/" + relativePath; } /** * 输出链接有效性报告 */ public static void reportLinks(List<String> links) { for (String link : links) { System.out.println("URL: " + link + " 状态: " + isLinkBroken(link)); } } /** * 验证链接是否有效 */ private static String isLinkBroken(String url) { try { URL linkUrl = new URL(url); HttpURLConnection connection = (HttpURLConnection) linkUrl.openConnection(); connection.setRequestMethod("HEAD"); connection.connect(); int responseCode = connection.getResponseCode(); return responseCode == 200 ? "正常" : "异常(状态码: " + responseCode + ")"; } catch (Exception e) { return "无效(错误信息: " + e.getMessage() + ")"; } } }
关键改进说明
visitedUrls集合:用HashSet确保每个URL只被爬一次,彻底解决重复访问的问题crawlSite递归方法:真正实现了“爬取当前页→处理子链接→递归爬取子页面”的全站遍历逻辑- 链接过滤与转换:只处理本站域名的链接,同时把相对路径转为绝对URL,避免路径错误
- 修复img标签逻辑:把原来错误的
href改为src,正确收集图片资源地址 - 资源清理:爬取完成后自动关闭浏览器,避免资源浪费
这样修改后,就能从首页开始递归遍历整个网站的所有站内链接,再也不会重复刷首页啦!
内容的提问来源于stack exchange,提问作者Irina
相关产品推荐
相关产品推荐

