Skip to main content

Find all the Links on a Webpage with Selenium WebDriver in Java

Find all the Links on a Webpage with Selenium WebDriver in Java

Key takeaways

  • driver.findElements(By.tagName("a")) returns every anchor on the page, including hidden ones and ones with no destination.
  • getAttribute("href") gives the resolved absolute URL; filter out null, empty, javascript:, mailto: and tel: values before using the list.
  • Do not click through the list in a loop: every click() plus back() makes the stored elements stale. Collect the URLs first, then visit or request them.

To find all the links on a web page with Selenium WebDriver in Java, call driver.findElements(By.tagName("a")). It returns a List<WebElement> with one entry per <a> tag, and you read each link’s URL with getAttribute("href") and its text with getText(). The examples below are written for Selenium 4 and compile against 4.51.0.

Selenium 4.6 and later ships with Selenium Manager, which downloads a matching browser driver on its own. You no longer download chromedriver and set webdriver.chrome.driver; new ChromeDriver() is enough. Add org.seleniumhq.selenium:selenium-java to your Maven or Gradle project and run this:

import java.util.List;

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;

WebDriver driver = new ChromeDriver();
try {
    driver.get("https://example.com/");

    List<WebElement> allLinks = driver.findElements(By.tagName("a"));
    System.out.println("Links found: " + allLinks.size());

    for (WebElement link : allLinks) {
        System.out.println(link.getText() + " -> " + link.getAttribute("href"));
    }
} finally {
    driver.quit();
}

findElements never throws when nothing matches; it returns an empty list. findElement (singular) returns only the first match and throws NoSuchElementException when there is none. The old findElementsByTagName helpers from Selenium 3 were removed in Selenium 4, so use the By form shown above.

The list includes anchors that are not visible, because findElements does not filter on visibility. If you only want links a user can see, add link.isDisplayed() to the loop condition.

Use getAttribute("href"). Selenium’s Javadoc says it returns the property with that name if one exists and falls back to the attribute otherwise. For href the property is the absolute URL resolved against the page’s base URL, which MDN documents for HTMLAnchorElement.href. So a link written as <a href="/pricing"> comes back as https://example.com/pricing.

If you need the raw text from the HTML source, use getDomAttribute("href"), which returns the attribute and ignores the property. An anchor without an href (a placeholder, or one wired up by JavaScript) gives null from getAttribute("href") or "" depending on the browser driver, so check for both.

Many pages repeat the same link in the header, body and footer, and some anchors are not web links at all. Collect the useful ones into a LinkedHashSet, which drops duplicates and keeps page order:

Set<String> urls = new LinkedHashSet<>();
for (WebElement anchor : driver.findElements(By.tagName("a"))) {
    String href = anchor.getAttribute("href");
    if (href != null && !href.isBlank()
            && !href.startsWith("javascript:")
            && !href.startsWith("mailto:")
            && !href.startsWith("tel:")) {
        urls.add(href);
    }
}

A CSS selector can do part of the filtering for you: driver.findElements(By.cssSelector("a[href]")) only returns anchors that have an href attribute. To limit the search to one area, locate the container first and search inside it, for example driver.findElement(By.id("main")).findElements(By.tagName("a")). For other ways to locate elements, see how to locate web elements with Selenium WebDriver, or the comparison of the fastest ways to find elements.

Request each URL with an HTTP client instead of the browser. It is much faster, and a status code tells you more than a page that happened to load. This version uses the java.net.http.HttpClient that ships with Java 11 and later, sends a HEAD request, and falls back to GET for servers that answer 405 Method Not Allowed or 501:

HttpClient client = HttpClient.newBuilder()
        .followRedirects(HttpClient.Redirect.NORMAL)
        .connectTimeout(Duration.ofSeconds(10))
        .build();

List<String> broken = new ArrayList<>();
for (String url : urls) {
    if (!url.startsWith("http")) {
        continue;
    }
    HttpRequest head = HttpRequest.newBuilder(URI.create(url))
            .method("HEAD", HttpRequest.BodyPublishers.noBody())
            .timeout(Duration.ofSeconds(15))
            .build();
    int status = client.send(head, HttpResponse.BodyHandlers.discarding()).statusCode();
    if (status == 405 || status == 501) {
        HttpRequest get = HttpRequest.newBuilder(URI.create(url))
                .GET().timeout(Duration.ofSeconds(15)).build();
        status = client.send(get, HttpResponse.BodyHandlers.discarding()).statusCode();
    }
    if (status >= 400) {
        broken.add(status + " " + url);
    }
}
System.out.println("Broken links: " + broken);

Treat the result as a first pass. Some sites answer automated requests with 403 or 429 even though the link works in a browser, and a link behind a login needs the session cookies the browser holds. Anything flagged is worth opening by hand before you report it as broken.

The loop from the original version of this post looks fine and breaks on the second pass:

for (WebElement link : allLinks) {
    link.click();
    driver.navigate().back();   // the next link.click() throws StaleElementReferenceException
}

After back() the browser reloads the page, so the elements in allLinks refer to a DOM that no longer exists. Selenium’s troubleshooting guide lists navigating away, reloading and DOM changes as the causes of a StaleElementReferenceException, and the fix is to locate the element again. Two patterns work. The first is to visit the URLs you collected earlier:

String startPage = driver.getCurrentUrl();
for (String url : urls) {
    driver.get(url);
    System.out.println(driver.getTitle());
}
driver.get(startPage);

The second is to find the elements again on every pass and click by index:

int count = driver.findElements(By.tagName("a")).size();
for (int i = 0; i < count; i++) {
    driver.findElements(By.tagName("a")).get(i).click();
    driver.navigate().back();
}

Use the second only when the click itself is what you are testing, for example a link that runs JavaScript. If a click opens a new tab, switch to it first; the new tab guide covers window handles. If the links load late, wait for them with an explicit wait, as in how to use explicit and implicit waits in Selenium WebDriver, instead of sleeping.

findElements only searches the current browsing context, so links inside an <iframe> do not show up until you switch into it. Switch by index, then return to the main page:

List<WebElement> frames = driver.findElements(By.tagName("iframe"));
for (int i = 0; i < frames.size(); i++) {
    driver.switchTo().frame(i);
    System.out.println(driver.findElements(By.tagName("a")).size());
    driver.switchTo().defaultContent();
}

Links added by JavaScript after the page loads are another common gap. If your count is lower than what you see on screen, wait for the content to render before calling findElements.

When you need one link and not all of them, search by its visible text: driver.findElement(By.linkText("Pricing")).click(). See how to click a link by text in Selenium WebDriver for linkText, partialLinkText and their limits.

Where does performance testing fit in?

A link check tells you whether pages exist, not whether they hold up under traffic or load quickly. Once the links work, LoadFocus has a free website speed test to see how those pages perform, and cloud load testing to run them under many concurrent users.

Frequently Asked Questions

Call driver.findElements(By.tagName("a")). It returns every anchor on the page as a list of WebElement objects, which you can loop over with getAttribute("href") and getText().

An anchor does not have to have an href. Placeholders and links driven by JavaScript have none, so getAttribute("href") returns null or an empty string. Filter those out before using the list.

findElements returns hidden anchors too. Filter the list with isDisplayed(), or search inside the container that holds the links you care about.

Collect the URLs first, then request them with an HTTP client outside the browser. Clicking each link in the browser is slower, and it forces you to relocate the elements after every navigation.

Navigating away and coming back reloads the DOM, so the elements you stored are no longer attached to it. Collect the URLs and visit them one by one, or locate the elements again on each pass.

Do I still need to download chromedriver?

No. Since Selenium 4.6, Selenium Manager downloads a matching driver when none is found, so new ChromeDriver() works without setup.

Sources

  • Selenium Manager, selenium.dev/documentation/selenium_manager (October 2026)
  • WebElement Javadoc, selenium.dev/selenium/docs/api/java/org/openqa/selenium/WebElement.html (October 2026)
  • Selenium errors troubleshooting, selenium.dev/documentation/webdriver/troubleshooting/errors (October 2026)
  • HTMLAnchorElement.href, developer.mozilla.org/en-US/docs/Web/API/HTMLAnchorElement/href (October 2026)

Bogdan

Founder at LoadFocus

Bogdan builds and runs the tools this blog is about. He writes from what the products actually do in production, including the parts that break.

Weekly tips right in your inbox.

Don’t miss out on the latest testing tips and tricks, tools and news related to software testing.

Already +20,000 members in the list.

The Load Testing Platform with AI That Explains Your Results

Load test websites and APIs from 25 cloud regions, monitor page speed and uptime, and get AI analysis that explains your results in plain English.

Cloud load testing, website speed and API monitoring in one place.

Hi there! Can I share a cool thing I'm working on with you?

A software testing newsletter that sparks joy.

Our goal is to create helpful content for software developers and testers.

If you're not into it, you can unsubscribe instantly.