Java Web Scraping
Web scraping in Java takes three libraries for most jobs: jsoup to download a page and select data with CSS selectors, HtmlUnit for pages that need some JavaScript without a real browser, and Selenium (or Playwright for Java) for pages that only work in Chrome or Firefox. A jsoup scraper is about 20 lines and runs on any Java 8 or later; HtmlUnit 5 needs Java 17. ProxyField's residential proxies plug into jsoup's proxy() and auth() methods, with 412M+ IPs in 195+ countries from $1.95/GB; start with the Easy Trial: 1 GB for $2.95.
- residential IPs
- 412M+
- from, traffic never expires
- $1.95/GB
- Easy Trial
- 1 GB for $2.95
On this page 9 sections
Is Java good for web scraping?
Yes, especially for long-running and large jobs: Java is fast, strongly typed, has mature HTTP and HTML libraries, and runs thousands of concurrent requests on virtual threads (Java 21+). Teams that already run Java services can scrape in the same stack, with the same logging, scheduling and deployment. Python has more scraping tutorials and frameworks, and Node.js has the most browser automation; Java's advantage is robustness at scale. Kotlin, Scala and other JVM languages use the same libraries.
Java web scraping libraries
| Library | What it does | Runs JavaScript | License | Use it for |
|---|---|---|---|---|
| jsoup | Fetches HTML, parses it, selects with CSS selectors, cleans HTML | No | MIT | Static pages: the default |
java.net.http.HttpClient | The JDK's HTTP client (Java 11+), HTTP/2, async | No | Part of the JDK | JSON APIs, custom fetching |
| HtmlUnit | A headless browser written in Java, with its own JavaScript engine | Partly | Apache-2.0 | Simple JavaScript, forms, without installing a browser |
| Selenium WebDriver | Drives real Chrome, Firefox, Edge, Safari | Yes | Apache-2.0 | Pages that need a real browser; teams on Selenium |
| Playwright for Java | Drives Chromium, Firefox, WebKit with auto-waiting | Yes | Apache-2.0 | Modern JavaScript-heavy sites |
Which one to pick: start with jsoup. If the page source does not contain the data, look for the page's JSON request in DevTools and call it with HttpClient. Only if neither works, use Playwright for Java or Selenium; HtmlUnit fits in between for light scripting.
Set up the project
Add jsoup to Maven (or Gradle); the current version is on its site (jsoup.org): 1.23.2 in October 2026.
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.23.2</version>
</dependency>Scrape a website with jsoup, step by step
This program reads every book's title, price and stock status from a practice catalog, follows the "next" link through all pages, waits a second between requests and writes a CSV file.
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import java.io.PrintWriter;
import java.nio.file.Files;
import java.nio.file.Path;
public class BookScraper {
public static void main(String[] args) throws Exception {
String url = "https://books.toscrape.com/catalogue/page-1.html";
try (PrintWriter out = new PrintWriter(Files.newBufferedWriter(Path.of("books.csv")))) {
out.println("title,price,in_stock");
while (url != null) {
Document doc = Jsoup.connect(url)
.userAgent("Mozilla/5.0 (compatible; books-demo/1.0)")
.timeout(30_000)
.get();
for (Element book : doc.select("article.product_pod")) {
String title = book.selectFirst("h3 a").attr("title").replace("\"", "\"\"");
String price = book.selectFirst(".price_color").text();
boolean inStock = book.selectFirst(".instock") != null;
out.printf("\"%s\",%s,%b%n", title, price, inStock);
}
Element next = doc.selectFirst("li.next a");
url = next != null ? next.absUrl("href") : null; // absolute URL of the next page
Thread.sleep(1_000); // one request per second
}
}
}
}What it does:
Jsoup.connect(url).get()downloads and parses the page in one call;userAgent()andtimeout()set the header and the limit.doc.select("article.product_pod")returns every element matching the CSS selector;selectFirst()returns one ornull.attr("title")reads an attribute andtext()the visible text;absUrl("href")turns a relative link into an absolute one.- The loop ends when there is no "next" link.
jsoup also cleans untrusted HTML against an allowlist (Jsoup.clean(html, Safelist.basic())), which is useful before you store scraped HTML.
Pages that need JavaScript: HtmlUnit, Selenium and Playwright
jsoup sees only the HTML the server sends. If the data is filled in by scripts, you have three options in Java.
HtmlUnit runs the page's JavaScript in its own engine, without a real browser; it handles many simple pages and forms, but complex front-end frameworks may not behave as in Chrome (htmlunit.org). HtmlUnit 5 uses the org.htmlunit package and needs JDK 17 or later.
import org.htmlunit.BrowserVersion;
import org.htmlunit.WebClient;
import org.htmlunit.html.DomNode;
import org.htmlunit.html.HtmlPage;
try (WebClient client = new WebClient(BrowserVersion.CHROME)) {
client.getOptions().setCssEnabled(false);
client.getOptions().setThrowExceptionOnScriptError(false);
HtmlPage page = client.getPage("https://quotes.toscrape.com/js/");
client.waitForBackgroundJavaScript(5_000);
for (DomNode quote : page.querySelectorAll(".quote .text")) {
System.out.println(quote.asNormalizedText());
}
}Selenium drives a real browser; Selenium Manager downloads the matching driver. More in what Selenium is.
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
ChromeOptions options = new ChromeOptions();
options.addArguments("--headless=new");
WebDriver driver = new ChromeDriver(options);
try {
driver.get("https://quotes.toscrape.com/js/");
new WebDriverWait(driver, Duration.ofSeconds(15))
.until(ExpectedConditions.presenceOfElementLocated(By.cssSelector(".quote")));
driver.findElements(By.cssSelector(".quote .text")).forEach(e -> System.out.println(e.getText()));
} finally {
driver.quit();
}Playwright for Java (com.microsoft.playwright:playwright) has the same API as in Python and Node.js, with automatic waiting; see what Playwright is.
import com.microsoft.playwright.*;
try (Playwright pw = Playwright.create()) {
Browser browser = pw.chromium().launch();
Page page = browser.newPage();
page.navigate("https://quotes.toscrape.com/js/");
page.locator(".quote").first().waitFor();
System.out.println(page.locator(".quote .text").allInnerTexts());
}A browser costs far more time, memory and traffic per page than jsoup, so use it only for the pages that need it; headless browser scraping explains how to tell.
Use a proxy in Java
When a site limits requests per IP, send them through a rotating residential proxy. jsoup takes the proxy host and port with proxy() and the credentials with auth() (jsoup 1.17.1 and later). One JDK detail trips most people up: Java disables Basic authentication for proxies when tunneling HTTPS, so set jdk.http.auth.tunneling.disabledSchemes to an empty string before the first request, or HTTPS requests fail with 407 Proxy Authentication Required even with the right password.
static {
System.setProperty("jdk.http.auth.tunneling.disabledSchemes", ""); // allow Basic for HTTPS tunnels
}
Document doc = Jsoup.connect("https://api.ipify.org?format=json")
.proxy("gate.proxyfield.io", 7000)
.auth(ctx -> ctx.isProxy() ? ctx.credentials("<login>-country-us-city-new_york", "<password>") : null)
.ignoreContentType(true)
.get();
System.out.println(doc.text()); // the exit IPWith java.net.http.HttpClient, set ProxySelector.of(new InetSocketAddress("gate.proxyfield.io", 7000)) and an Authenticator that returns the credentials for RequestorType.PROXY; the same system property applies. With an IP allowlist, auth is not needed, and you connect to the port you set up in the dashboard instead of 7000. <login>-country-us-city-new_york asks for an IP in New York, United States. Region, session and session time go in the same username: -region-<region>, -session-<id> and -ttl-<seconds> (30 to 129,600 seconds, 20 minutes by default), as in <login>-country-us-region-new_york-city-new_york-session-he9f2qga-ttl-600; every part after the login is optional.
Scale: concurrency, retries and politeness
- Concurrency. On Java 21+,
Executors.newVirtualThreadPerTaskExecutor()runs one lightweight thread per URL; cap requests per site with aSemaphoreso you do not overload it. - Thread safety. Use one
Connectionper request (or aJsoup.newSession()per thread); parsedDocumentobjects should not be shared between threads while they are modified. - Retries. Retry timeouts, 429 and 5xx responses with exponential backoff; see web scraping best practices.
- Politeness. Read
robots.txtand the site's terms, keep the rate low, and collect only public data; see is web scraping legal.
Screen scraping in Java
"Screen scraping" is the older name for reading data from what an application displays. On the web it means browser-based scraping, which in Java is Selenium or Playwright reading the rendered page, or taking a screenshot (page.screenshot() in Playwright). Reading text from images needs an OCR library, and data from desktop applications needs UI automation tools; both are outside web scraping.
ProxyField residential proxies for Java scrapers cost $2.95/GB for 1 GB down to $1.95/GB from 1,000 GB, with no subscription; the traffic never expires, and all purchases are final (residential and mobile proxy pricing).
Java Web Scraping FAQ
Is Java good for web scraping?
ProxyField's residential proxies (412M+ residential IPs, from $1.95/GB) plug into jsoup's proxy() and auth(), and Java is good for web scraping, especially for large and long-running jobs: jsoup handles static pages, HtmlUnit light JavaScript, and Selenium or Playwright full browsers. Python has more tutorials, but Java is fast, typed and scales well on virtual threads.
How do you do web scraping in Java?
Add jsoup, download and parse a page with Jsoup.connect(url).get(), select the fields with CSS selectors such as doc.select(".price"), follow the "next" links with absUrl("href"), and write the results to CSV or a database. Use Selenium or Playwright only for pages whose data needs JavaScript.
Which library should I use: jsoup, HtmlUnit, Selenium or HttpClient?
jsoup for HTML pages, HttpClient for JSON endpoints, HtmlUnit for simple JavaScript without installing a browser, and Selenium or Playwright for pages that only work in a real browser. Start with the lightest one that gets the data.
What is jsoup?
An open-source Java library (MIT license) that fetches web pages, parses HTML the way browsers do, lets you select elements with CSS selectors and cleans untrusted HTML. It is the most common starting point for Java scrapers and runs on Java 8 or later.
Can jsoup parse HTML from a JavaScript-rendered page?
Only if you give it the rendered HTML. jsoup does not run JavaScript, so on its own it sees the page as the server sent it; render the page with Selenium or Playwright, then pass driver.getPageSource() or page.content() to Jsoup.parse().
Is jsoup thread-safe?
Use one Connection per request (or one Jsoup.newSession() per thread), and do not modify a parsed Document from several threads at once. Running many independent requests in parallel, each in its own thread or virtual thread, is safe.
What Java version do I need for web scraping projects?
jsoup runs on Java 8 or later, java.net.http.HttpClient needs Java 11, HtmlUnit 5 needs Java 17, and virtual threads for large concurrent crawls need Java 21. A current LTS release (21 or later) covers all of them.
When should I use Selenium instead of jsoup?
When the data you need is not in the page source because scripts build it, or when you must click, scroll or fill forms first. For everything else jsoup is far faster and lighter.
Why does Java return 407 for HTTPS through a proxy with the right password?
Since Java 8u111 the JDK disables Basic authentication for proxy tunnels used by HTTPS. Set the system property jdk.http.auth.tunneling.disabledSchemes to an empty string before the first request, and the same credentials work.
What is screen scraping in Java?
Reading data from what a page or program displays rather than from an API. On the web that means rendering the page with Selenium or Playwright for Java and reading the visible elements, or saving screenshots; text in images needs OCR.
Show 1 more questionShow fewer questions
Do Kotlin developers use Java scraping libraries?
Yes. Kotlin runs on the JVM and calls jsoup, HtmlUnit, Selenium and Playwright for Java directly, so the examples on this page translate line by line, and coroutines work well for concurrent requests.
Related pages
Easy Trial: 1 GB for $2.95.
Try for the price of 1 GB, no subscription, traffic never expires.