A Java Developer's Guide: scrape website java with JSoup, Selenium, and APIs

Web data extraction guides, proxy tutorials, automation best practices, and developer documentation for Scrappey — a reliable API for collecting publicly available web data at scale.

A Java Developer's Guide: scrape website java with JSoup, Selenium, and APIs

A Java Developer's Guide: scrape website java with JSoup, Selenium, and APIs

Created time
Feb 12, 2026 07:58 AM
Date
Status
Before you can scrape a website with Java, you’ve got to pick the right tool for the job. This decision boils down to one simple question: is the website static or dynamic? Get this wrong, and you'll either over-engineer your solution or hit a brick wall. For straightforward, static HTML pages, Jsoup is your fastest, most lightweight option. But the moment you encounter a modern, JavaScript-heavy site, you'll need a full-blown browser automation tool like Selenium or Playwright.

Choosing the Right Java Scraping Toolkit

notion image
When you decide to build a scraper in Java, you're tapping into a seriously powerful and mature ecosystem. It's an excellent choice for creating scalable data extraction pipelines thanks to its performance, strong typing, and fantastic support for concurrency. You can build anything from a quick script to an enterprise-grade data operation.
But your first move is crucial. It all hinges on whether the website loads its important data with the initial HTML document or uses JavaScript to render content after the page loads. Your answer to that question will shape your entire approach.

The Static vs. Dynamic Divide

Most developers dip their toes into scraping with static websites. These are the simple pages where all the content you need is right there in the initial HTML source code—think of a basic blog post or a no-frills product page. For these jobs, a simple HTML parsing library is all you need.
Dynamic websites, however, are the modern standard. These sites use JavaScript frameworks like React or Angular to fetch and display data after the initial page has already loaded. To scrape them, you need a tool that can act like a real browser and execute all that JavaScript.

Your Core Library Options

Making the right choice from the start will save you countless hours of refactoring down the road. Let's break down the main libraries you'll be working with so you can understand their strengths and weaknesses.

Your Java Web Scraping Library Options

Here’s a quick comparison of the most popular Java libraries for scraping static vs. dynamic websites to help you choose the right tool for your project.
Library
Primary Use Case
Handles JavaScript?
Best For
Jsoup
Parsing static HTML
No
Fast and simple data extraction from server-rendered pages, API responses, or local HTML files.
Selenium
Browser Automation
Yes
Scraping complex, dynamic websites that require user interactions like clicking, scrolling, and form submission.
Playwright
Modern Browser Automation
Yes
A newer alternative to Selenium, offering a more modern API and improved performance for JavaScript-heavy sites.
HttpClient
Making HTTP Requests
No
Used as a foundation to fetch the raw HTML from a server, which is then passed to a parser like Jsoup.
This table should give you a solid starting point. For simple tasks, stick with Jsoup and HttpClient. For the complex, interactive sites that dominate the web today, Selenium or Playwright will be your best friends.
In the grand scheme of web scraping, Java holds its own. The global market, valued at around 2 billion by 2030. In North America, where over 35% of the market is concentrated, companies are constantly using Java scrapers for price monitoring—a sector that has seen a 25% spike in scraping activity thanks to dynamic pricing. You can dig into the full web scraping market report to get a better handle on these trends.

Extracting Data from Static Sites with Jsoup

notion image
When you're scraping websites with Java, your bread and butter will be static HTML pages. These are the classic web pages where all the content is baked into the initial server response—no fancy client-side JavaScript needed to render the good stuff. For these jobs, Jsoup is your best friend. It’s lightweight, incredibly fast, and has a wonderfully intuitive API for parsing HTML.
What makes Jsoup so effective is that it does one thing exceptionally well: it turns raw, messy HTML into a clean, traversable Document Object Model (DOM). This lets you navigate the page's structure and pluck out specific data using CSS selectors, the same way you would in your browser's developer tools.

Getting Jsoup into Your Project

Getting started is a breeze with a build tool like Maven or Gradle. You just need to add a single dependency to your pom.xml, and you'll have the entire Jsoup library ready to go.
Here's the snippet you'll need:
Once Maven pulls this in, you're all set to write some scraping code. The core of Jsoup revolves around the connect() method to point it at a URL, and the get() method to fetch the page and return a parsed Document object. It handles the entire HTTP request and parsing process in a single, elegant line of code.

Fetching and Parsing HTML

Let's say you want to scrape product titles from a simple e-commerce category page. Your first move is to tell Jsoup where to look.
import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import java.io.IOException;
public class JsoupScraper { public static void main(String[] args) { String url = "https://example-ecommerce-site.com/products"; try { Document doc = Jsoup.connect(url).get(); System.out.println("Page Title: " + doc.title()); } catch (IOException e) { System.err.println("Error fetching the URL: " + e.getMessage()); } } }
This tiny bit of code connects to the URL, grabs its complete HTML, and prints the page title. That try-catch block is absolutely essential for handling network hiccups like timeouts or 404 errors. You'd be surprised how many developers forget this, ending up with brittle scripts that fall over at the first sign of trouble.

Pinpointing Data with CSS Selectors

With the HTML parsed into a Document object, the real fun begins. Now you can use CSS selectors to zero in on the exact elements you want. This is where opening your browser's DevTools becomes second nature. Inspect the page, find the classes or IDs on the elements holding your data, and use them to build your Jsoup query.
For instance, if all our product titles are wrapped in <h3> tags with a class of product-title, we can extract them with a two-step process:
  1. Select the Elements: Use the doc.select() method with your CSS query. It returns an Elements collection—basically, a list of all matching Element objects.
  1. Iterate and Extract: Loop through the collection and use methods like .text() to get the content from each element.
Let's expand on the last example to grab all the product titles:
import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.IOException;
// ... inside the try block Document doc = Jsoup.connect(url).get(); Elements productTitles = doc.select("h3.product-title");
System.out.println("Found " + productTitles.size() + " products:"); for (Element title : productTitles) { System.out.println("- " + title.text()); }
This approach is the foundation of most static scraping tasks. You can get fancy with more complex selectors to navigate nested elements, pull attributes like href from links, or scrape entire tables. You'll quickly find that a surprising number of websites don't actually need JavaScript to display their core data, a topic we cover in more detail here: https://wiki.scrappey.com/why-you-probably-dont-need-javascript-with-a-scraper.

Scraping Dynamic Content with Selenium and Playwright

Jsoup is great, but it hits a wall with modern websites. So many sites today don't just send you a neat package of HTML. Instead, they serve a bare-bones skeleton and then use JavaScript to pull in the real content from APIs, rendering everything right in your browser. If you send a simple HTTP request to one of these pages, you'll get that skeleton back, completely missing the data you're after.
To scrape these sites, you have to act less like a script and more like a user. That means you need a tool that can actually run a browser, execute the JavaScript, and see the page exactly as a person would.
This is where browser automation frameworks come in. For Java developers, the two heavyweights in this space are Selenium and Playwright. These aren't just fetching HTML; they're firing up a real browser instance (often in "headless" mode so you don't see the UI), letting all the scripts run, and then giving your code access to the fully-rendered page.

Selenium: The Established Standard

Selenium has been the undisputed king of browser automation for well over a decade. Its age is its strength—it boasts a massive community, tons of documentation, and an API that's stable and well-understood by millions. Getting it into a Java project is a piece of cake; just add the selenium-java dependency to your Maven or Gradle file.
The central piece of the Selenium puzzle is the WebDriver. It’s the bridge that lets your Java code talk to the browser. You'll typically create an instance of ChromeDriver or FirefoxDriver, tweak its settings (like running it headless), and you're ready to go.
Now, one of the biggest rookie mistakes when scraping dynamic content is a timing issue. You navigate to a page and immediately try to grab an element, but it hasn't been created yet by the JavaScript. Boom: NoSuchElementException.
To get around this, Selenium gives us explicit waits. Instead of just pausing your script with a blind Thread.sleep(), you tell WebDriver to wait until a certain condition is met—like an element finally becoming visible on the page.
Let's say you're trying to scrape product reviews that only load after a short delay:
WebDriver driver = new ChromeDriver(); driver.get("https://example.com/product-reviews");
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10)); List reviews = wait.until( ExpectedConditions.visibilityOfAllElementsLocatedBy(By.cssSelector("div.review-text")) );
for (WebElement review : reviews) { System.out.println(review.getText()); } driver.quit();
This snippet tells Selenium, "Wait up to 10 seconds for those review elements to show up before you do anything else." It makes your scraper dramatically more reliable than just guessing with a fixed delay.

Playwright: The Modern Contender

Playwright is the newer kid on the block from Microsoft, and it's been turning a lot of heads with its slick, modern API and impressive performance. While Selenium was originally built for testing, Playwright was designed for robust automation from day one, which gives it some nice perks. If you're weighing your options, check out this comprehensive comparison of Playwright and Selenium to see which is a better fit.
One of Playwright's most-loved features is its auto-waiting capability. Most Playwright actions, like clicking an element or getting its text, will automatically wait for the element to be ready. This often gets rid of the need for explicit waits entirely, making your code cleaner and easier to read.
Here’s that same review-scraping task, but this time with Playwright for Java:
try (Playwright playwright = Playwright.create()) { Browser browser = playwright.chromium().launch(); Page page = browser.newPage(); page.navigate("https://example.com/product-reviews");
// Playwright automatically waits for the element to be visible Locator reviewLocator = page.locator("div.review-text"); for (String reviewText : reviewLocator.allTextContents()) { System.out.println(reviewText); } browser.close();
}
See how much simpler that is? Playwright handles the waiting behind the scenes. It's a modern touch that's winning over developers for new projects.

Handling User Interactions

Often, just waiting for elements to load isn't enough. The data you need might be hidden behind a "Load More" button, buried deep down an infinite-scroll feed, or only revealed after you fill out a form.
Both Selenium and Playwright are built for this kind of interaction:
  • Scrolling: You can easily execute a snippet of JavaScript to scroll the window down, triggering those infinite-scroll loaders.
  • Clicking: Both frameworks have a .click() method to simulate a user clicking anything you can target, from pagination buttons to dropdown menus.
  • Form Filling: Locating input fields and typing into them is straightforward with .sendKeys() (Selenium) or .fill() (Playwright).
Ultimately, the choice between Selenium and Playwright often boils down to your team's familiarity and the project's specific needs. Selenium is the battle-hardened veteran you can always count on. Playwright offers a more modern, streamlined experience that can speed up development. Either way, getting comfortable with a browser automation tool is a non-negotiable skill for any developer serious about web scraping in Java.

Overcoming Common Scraping Obstacles

If you think writing the code to fetch and parse a webpage is the hard part, you're in for a surprise. Once you try to scrape a website in Java at any real scale, you'll find the true challenge isn't the code—it's getting around the anti-scraping defenses modern websites throw up. Success at scale is less about slick parsing algorithms and more about learning to mimic human behavior to fly under the radar.
The first wall most developers hit is the classic IP ban. When a server sees hundreds of requests firing off from the same IP address in minutes, it rightly flags that traffic as a bot. The fix? Distribute your requests across a massive pool of IP addresses using a rotating proxy service. This makes your scraper's traffic look like it's coming from thousands of different users all over the world, which dramatically lowers your chances of getting blocked.
The diagram below shows the typical flow for scraping dynamic pages, which almost always demand these kinds of advanced tactics.
notion image
This process really drives home why browser automation is a must for JavaScript-heavy sites—the exact places where anti-bot measures are the strongest.

Integrating Rotating Proxies in Java

Good news is, setting up a proxy with Java's HttpClient or even a browser automation tool like Selenium is pretty straightforward. You just configure your client to route its traffic through the proxy server's address and port.
When you're ready to scale, you need a plan for CAPTCHAs, IP bans, and understanding how Cloudflare blocks crawlers. A quality proxy service handles the IP rotation for you. Your code just hits a single endpoint, and the service juggles a huge pool of IPs on the backend.

Managing Sessions and Logins

A lot of the good stuff online is behind a login wall. This creates a session management puzzle for your scraper. When a real user logs in, the server gives them a session cookie. The browser sends this cookie back with every subsequent request to prove it's the same authenticated user.
To pull this off in Java, your scraper needs to follow the same dance:
  1. Hit the Login Endpoint: Send a POST request with the username and password to the website's login form.
  1. Grab the Session Cookies: Once you get a successful login response, dig through the headers and pull out the Set-Cookie values.
  1. Use Cookies on Future Requests: For every request you make after that, stick those cookies in the Cookie header.
This little trick lets your scraper maintain an authenticated state, giving it access to protected content just like a logged-in user.

Implementing Robust Error Handling and Retries

Let's be real: network requests fail. Servers crash, connections time out, and anti-bot systems will occasionally block you even with proxies. A scraper that falls apart after one failed request is just a toy. The professional approach is to build in a retry mechanism with exponential backoff.
Here's the basic idea:
  • If a request fails with a temporary error (like a 503 Service Unavailable), don't immediately try again.
  • Wait for a short, slightly randomized interval, maybe 1-2 seconds.
  • If that attempt also fails, double the waiting time before you go again.
  • Always cap the number of retries so you don't get stuck in an endless loop.
This strategy keeps you from hammering a server that's already struggling and makes it much more likely that one of your later attempts will get through once the temporary issue resolves.

Boosting Speed with Concurrency

Scraping one page at a time is painfully slow. Luckily, Java's multi-threading support is fantastic, making it easy to speed things up by fetching multiple pages at the same time. The ExecutorService in Java's java.util.concurrent package is your best friend here.
You can create a fixed-size thread pool and then submit each URL you need to scrape as its own task. The ExecutorService handles all the thread management, running several scraping jobs in parallel. This can easily turn a multi-hour job into a multi-minute one.
But with great power comes great responsibility. Don't be that person. Firing hundreds of concurrent requests can feel like a denial-of-service attack to a website's server. It's crucial to cap your thread pool at a reasonable size (think 5-10 threads) to avoid getting your entire IP range banned. The sweet spot is combining a small, concurrent thread pool with a good rotating proxy service—you get speed without being a jerk.

Scaling Your Scraper with the Scrappey API

So, you’ve decided to scrape a website in Java. You can write the code, sure, but what about all the other stuff? I’m talking about managing rotating proxies, solving endless CAPTCHAs, and wrestling with JavaScript rendering. This infrastructure isn't just complex; it’s a massive time sink that needs constant babysitting.
Honestly, why build all that yourself when you can just outsource the heavy lifting?
This is exactly where a service like the Scrappey API comes into play. It’s a total game-changer. Instead of getting tangled up in browser automation or messy proxy lists, you just make a single, clean API call from your Java application. Scrappey takes care of the headless browser, proxy rotation, and even CAPTCHA solving on its end, sending back the clean HTML you actually want.
This approach cleans up your codebase in a hurry. You can literally replace hundreds of lines of messy Selenium or Playwright logic with just a few lines of standard HTTP client code. That means you get to focus on what really matters—pulling value from the data, not just fighting to get access to it.

Why Outsource Your Scraping Infrastructure?

Trying to manage a big scraping operation in-house is a serious engineering headache. The costs and complexities stack up fast, and before you know it, you’re spending more time on maintenance than on your actual business goals.
Just think about the tasks you get to offload by using a scraping API:
  • Proxy Management: Forget about sourcing, testing, and rotating thousands of IPs. You get instant access to a massive, managed proxy pool that just works.
  • Headless Browser Maintenance: No more dealing with random browser updates, driver incompatibilities, or the insane memory overhead of running tons of browser instances.
  • Anti-Bot Evasion: The service keeps up with the latest anti-scraping tech, handling JavaScript challenges and browser fingerprinting so you don’t have to.
  • CAPTCHA Solving: CAPTCHAs are handled automatically by integrated solvers, a job that would otherwise demand expensive third-party services.
By handing off these responsibilities, you turn a complicated infrastructure problem into a simple API integration. Your team can move faster, slash maintenance overhead, and build a much more reliable data pipeline.
The screenshot below gives you a peek at Scrappey's developer-focused dashboard.
It’s clear the platform is designed to hide all the messy parts of web scraping behind a clean, simple API endpoint for developers like us.

Making an API Request in Java

Plugging Scrappey into your Java project is about as easy as it gets. You can use any standard HTTP client, like Java 11’s built-in HttpClient, to hit the API endpoint. All you have to do is pass your API key and the target URL as parameters.
The service does its magic and sends back a JSON object. Inside, you’ll find the page's HTML, status codes, and other useful bits of metadata. This predictable, structured response makes parsing the results incredibly simple.
Here’s a complete, ready-to-run Java example showing how to call the Scrappey API and parse the response. This snippet uses the popular OkHttp library because it's so clean and straightforward.
import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; import org.json.JSONObject; import java.io.IOException;
public class ScrappeyApiExample { public static void main(String[] args) { String apiKey = "YOUR_API_KEY"; // Replace with your actual key String targetUrl = "https://example.com/products"; String scrappeyUrl = "https://api.scrappey.com/v1?key=" + apiKey + "&url=" + targetUrl;
OkHttpClient client = new OkHttpClient(); Request request = new Request.Builder() .url(scrappeyUrl) .build(); try (Response response = client.newCall(request).execute()) { if (!response.isSuccessful()) { throw new IOException("Unexpected code " + response); } String jsonData = response.body().string(); JSONObject jsonObject = new JSONObject(jsonData); // Extract the HTML solution from the JSON response String htmlContent = jsonObject.getJSONObject("solution").getString("response"); System.out.println("Successfully fetched HTML of length: " + htmlContent.length()); // Now you can pass this 'htmlContent' string to Jsoup for parsing // Document doc = Jsoup.parse(htmlContent); } catch (IOException e) { e.printStackTrace(); } }
}
This code is clean, concise, and pretty much production-ready. It completely removes the need for any browser automation on your side. If you're looking for ideas on how to apply this to different scenarios, you can find more inspiration by checking out some example use cases for scraping APIs. This approach frees you up to build powerful data applications without getting stuck in the weeds of web scraping mechanics.

Common Questions About Java Web Scraping

As you get your hands dirty with projects to scrape a website in Java, you’ll inevitably run into the same questions that pop up for every developer. Whether you're navigating the murky legal waters, figuring out login forms, or battling sophisticated anti-bot systems, clear answers are what you need to build scrapers that are both effective and responsible. Let's tackle the queries I see most often.

Is It Legal to Scrape a Website with Java?

This is the big one, and the answer isn't a simple yes or no—it's nuanced. Generally speaking, scraping data that's publicly available is legal in many places. But a few key factors can change the game. First, you absolutely must respect the website's robots.txt file, which is the official rulebook for automated bots and crawlers.
On top of that, a site's terms of service will often flat-out prohibit scraping. While how enforceable those terms are can be a gray area, ignoring them might get you blocked or worse. The most critical lines not to cross are scraping personal data, copyrighted content, or anything behind a login wall unless you have explicit permission.

How Do I Scrape Data That Requires a Login?

Scraping anything behind a login means you have to manage an authenticated session. In practice, this just means your scraper needs to handle cookies the same way a real browser does.
If you’re already using a browser automation tool like Selenium or Playwright, the process is pretty straightforward.
  • You just automate finding the username and password fields on the page.
  • Then, you simulate typing in the credentials and clicking the submit button.
  • From there, the browser instance takes care of the session cookies automatically for all your future requests.
When you're using a more bare-bones tool like HttpClient, you have to do the work yourself. You'll need to send a POST request to the login endpoint, grab the session cookies from the response headers, and then manually stick those cookies into the headers of every single request you make afterward to keep the session alive.

What Is the Best Way to Handle Anti-Scraping Tools?

There’s no magic bullet for fighting anti-scraping measures; it takes a multi-layered approach. A solid strategy combines several different techniques to make your scraper look and act more like a human user.
Your first line of defense should be a large pool of high-quality rotating residential proxies. This is hands-down the most effective way to sidestep IP-based blocking, as it makes your traffic look like it's coming from tons of different, real users.
After that, you need to pay close attention to your request headers.
  1. Randomize User-Agents: Don't just send the same user-agent string over and over—that's a rookie mistake. Keep a list of current, common browser user-agents and cycle through them.
  1. Mimic Real Headers: Make sure your requests include other standard headers a real browser would send, like Accept-Language, Accept-Encoding, and Referer. It makes you look much more legit.
  1. Vary Request Timing: Don't fire off requests like a machine gun. Add randomized delays between your requests to break up that robotic, predictable pattern.
For sites with really tough JavaScript challenges or device fingerprinting, a simple HTTP client just won't cut it. That's when you have to bring out the big guns and use a headless browser with Selenium or Playwright. Or, you could let a dedicated scraping API handle all those advanced headaches for you, which dramatically simplifies your code.
Ready to stop wrestling with proxies and CAPTCHAs? Scrappey handles the entire scraping infrastructure for you, delivering clean data through a simple API call. Get started for free at scrappey.com and focus on what matters most—your data.