You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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() + ")";
        }
    }
}

关键改进说明

  1. visitedUrls集合:用HashSet确保每个URL只被爬一次,彻底解决重复访问的问题
  2. crawlSite递归方法:真正实现了“爬取当前页→处理子链接→递归爬取子页面”的全站遍历逻辑
  3. 链接过滤与转换:只处理本站域名的链接,同时把相对路径转为绝对URL,避免路径错误
  4. 修复img标签逻辑:把原来错误的href改为src,正确收集图片资源地址
  5. 资源清理:爬取完成后自动关闭浏览器,避免资源浪费

这样修改后,就能从首页开始递归遍历整个网站的所有站内链接,再也不会重复刷首页啦!

内容的提问来源于stack exchange,提问作者Irina

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:03:29