Web Scraping in Java

Discover how to perform web scraping in Java using libraries like Jsoup, Selenium, and HtmlUnit. Learn to extract and automate data collection from websites and export data efficiently.

Web Scraping in Java
Pavlo Zinkovski
Pavlo Zinkovski 23 min read
Article content

Java is one of the most popular programming languages – and it can be a great pick for data collection. In this article, you’ll learn the essentials of web scraping in Java, covering everything from setting up your development environment to handling HTTP requests and extracting dynamic content. In this Java web scraping tutorial, we’ll guide you through using key libraries like Jsoup, HtmlUnit, and Selenium, with practical examples on parsing HTML, handling pagination, managing cookies, and exporting data to formats like JSON – all essential facets of web scraping with Java.

Can You Web Scrape With Java?

Java can be a good option for Java web scraping, but its suitability depends on the complexity and scale of the project. Here are some points to consider:

Pros

  • Stability and performance: Java is a compiled, statically-typed language, which offers good performance and stability for large-scale scraping projects.
  • Multithreading support: Java's built-in multithreading can be useful for making parallel requests and speeding up the web scraping process.
  • Robust libraries: There are several mature libraries for web scraping in Java:
  • Jsoup: Popular for extracting data and manipulating HTML. It’s lightweight and easy to use.
  • HtmlUnit: Simulates a web browser and is great for scraping dynamic websites.
  • Selenium: Java bindings are available for Selenium, which is useful for scraping JavaScript-heavy sites.
  • Cross-platform: Since Java offers multi platform compatibility, you can run scraping scripts on various operating systems without much modification.

Cons

  • Verbosity: Java can be more verbose compared to other scripting languages like Python, making it slightly less convenient for quick web scraping projects.
  • Handling JavaScript-heavy sites: While tools like HtmlUnit and Selenium can handle JavaScript, they may not be as efficient or straightforward as some JavaScript-based solutions or Python libraries like Playwright or Puppeteer.
  • Memory usage: Java applications can consume more memory compared to lighter-weight languages like Python or Node.js.

Basic Web Scraping With Java

Step 1: Getting Started with Web Scraping with Java

Before diving into web scraping with Java, you’ll need to have Java installed, set up a suitable development environment, and familiarize yourself with a few required Java libraries. Let’s do exactly that!

Installing Java: If you don’t have Java installed on your machine, you can download the latest version from the official Oracle website.

  1. Choose the correct version for your operating system (Windows, macOS, or Linux).
  2. Run the installer and follow the instructions.
  3. Set Java_HOME: After installation, make sure your environment variables are set correctly.
  4. On Windows: Go to “System Properties” > “Environment Variables” and add Java_HOME pointing to your Java installation path.
  5. On macOS/Linux: Add export Java_HOME=/path/to/Java in your shell configuration file (.bash_profile, .zshrc, etc.).

You can verify the installation by running the following command in your terminal:

java -version

Key libraries for web scraping with Java: To efficiently scrape valuable data from websites in Java, you’ll need to use libraries designed for parsing HTML, handling dynamic content, and navigating web elements.

  • Jsoup: A lightweight Java library used for parsing HTML. It is perfect for extracting and performing data analysis from static websites.
  • HtmlUnit: A headless browser that can simulate user interaction and execute JavaScript. Ideal for scraping dynamic content.
  • Selenium: Automates browsers and is commonly used for scraping sites with complex JavaScript. Java has strong bindings for Selenium.

Step 2: Set Up Your Development Environment

Choose and set up a Java IDE: An IDE (Integrated Development Environment) will make coding in Java much easier by offering code suggestions, debugging tools, and project management features. Popular IDEs include IntelliJ IDEA, Eclipse, and NetBeans.

Install Maven (optional but recommended): Maven is a build automation tool used for managing dependencies in Java projects. It simplifies the process of adding external Java libraries like Jsoup, HtmlUnit, and Selenium to your project.

  1. Download Maven from the link above.
  2. Unzip the downloaded file to a directory of your choice.
  3. Add MAVEN_HOME to your environment variables and update your Path to include Maven’s bin directory.
  4. Check Maven’s installation by running:
mvn -v

Test the setup: Finally, test your setup with a simple "Hello, World!" program to make sure everything is working. Here’s an example:

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}

Step 3: Connect to Your Target Website

You can use this code snippet to connect to a target website using Jsoup, one of the key libraries for web scraping in Java:

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

public class WebScraper {
    public static void main(String[] args) {
        try {
            // Connect to the target website
            Document document = Jsoup.connect("https://example.com")
                                     .userAgent("Mozilla/5.0")
                                     .timeout(5000)   // Set a timeout (in milliseconds)
                                     .get();          // Execute the request and get the HTML
            
            // Print the webpage's title
            String title = document.title();
            System.out.println("Page Title: " + title);
            
        } catch (Exception e) {
            e.printStackTrace();  // Handle exceptions such as network issues
        }
    }
}

How it works:

  1. We connect to the target website (https://example.com) by sending an HTTP GET request.
  2. We use a real browser’s User-Agent string to avoid being blocked by the website.
  3. If the connection is successful within 5 seconds (timeout), the HTML content of the page is fetched and parsed.
  4. Finally, the title of the page is extracted and displayed in the console.

This code can be modified to extract other elements from the webpage, like headings, paragraphs, or links, using Jsoup's powerful HTML parsing capabilities.

import org.jsoup.Jsoup;: This will import the Java class Jsoup from the Jsoup library, which is responsible for connecting to the website and parsing its HTML content.

import org.jsoup.nodes.Document;: The Document class represents the HTML page as a whole. Once connected to the website, the HTML is stored in a Document Java object.

Jsoup.connect(): The connect() method initiates a connection to the specified URL. In this case, we are connecting to "https://example.com".

.userAgent(): This sets the "User-Agent" header for the request. The User-Agent string title helps mimic a real browser. Common values include `"Mozilla/5.0"` for simulating a typical web browser. Some websites block bots, so using a user agent helps you appear like a real user.

.timeout(): Sets the connection timeout (in milliseconds). For example, .timeout(5000) ensures the program waits up to 5 seconds for the server’s response before throwing a timeout exception.

.get(): This method sends the HTTP GET request to the server and retrieves the HTML content of the webpage. The HTML is parsed and stored in the Document object.

document.title(): After retrieving the webpage, the web scraper extracts the <title> tag content. The title() method returns the title of the webpage, which we then print.

catch (Exception e): This block catches and handles any exceptions that may occur (e.g., network errors, invalid URL, or the server not responding).

Step 4: Handle HTTP Status Codes and Timeouts

import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

public class WebScraper {
    public static void main(String[] args) {
        try {
            // Connect to the target website and check the status code
            Connection connection = Jsoup.connect("https://example.com")
                                         .userAgent("Mozilla/5.0")
                                         .timeout(5000)  // Set a timeout of 5 seconds
                                         .ignoreHttpErrors(true);  // Prevent Jsoup from throwing exceptions on non-200 status codes

            // Execute the request and get the response
            Connection.Response response = connection.execute();

            // Check the status code
            int statusCode = response.statusCode();
            if (statusCode == 200) {
                // If the status code is 200 (OK), parse the HTML document
                Document document = response.parse();
                System.out.println("Page Title: " + document.title());
            } else {
                // Handle non-200 status codes (e.g., 404, 500)
                System.out.println("Failed to retrieve the page. Status Code: " + statusCode);
            }

        } catch (java.net.SocketTimeoutException e) {
            // Handle timeouts
            System.out.println("Connection timed out.");
        } catch (Exception e) {
            // Handle other exceptions (e.g., network issues, malformed URLs)
            e.printStackTrace();
        }
    }
}

How it works:

  1. The code attempts to connect to a target website and handles different types of failures, such as timeouts or non-200 status codes.
  2. If the connection is successful and the status code is 200, it fetches and parses the HTML content.
  3. For any non-200 status code, it gracefully reports the issue without terminating the program.
  4. Timeout errors are specifically caught and reported, allowing you to take appropriate actions, like retrying the request.

Connection connection = Jsoup.connect(): Creates a Connection object to manage the connection to the website. The URL "https://example.com" is the target website – and sets the User-Agent to mimic a real browser and avoids being blocked by the server.

.timeout(5000) sets a 5-second timeout for the connection. If the connection takes longer than this, it will throw a SocketTimeoutException.

.ignoreHttpErrors(true) ensures that Jsoup does not throw exceptions for non-200 HTTP status codes (e.g., 404 or 500). Instead, we handle the status codes manually.

Connection.Response response = connection.execute(): Executes the HTTP request and returns a Response object, which contains the full HTTP response (including status code, HTTP headers, and body).

response.statusCode(): Retrieves the HTTP status code of the response. A status code of 200 indicates success, while other codes like 404 (Not Found) or 500 (Server Error) indicate problems.

document.title(): If the status code is 200, it parses the HTML document using response.parse() and prints the title of the page.

Step 5: Select HTML Elements and Extract Data From Them

Let’s assume you’re trying to scrape data from the following HTML table:

<table id="data-table">
  <tr>
    <th>Name</th>
    <th>Age</th>
    <th>City</th>
  </tr>
  <tr>
    <td>John</td>
    <td>25</td>
    <td>New York</td>
  </tr>
  <tr>
    <td>Jane</td>
    <td>30</td>
    <td>Los Angeles</td>
  </tr>
  <tr>
    <td>Mike</td>
    <td>35</td>
    <td>Chicago</td>
  </tr>
</table>

We can use this code snippet to extract its data:

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class WebScraper {
    public static void main(String[] args) {
        try {
            // Connect to the website and get the HTML document
            Document document = Jsoup.connect("https://example.com")
                                     .userAgent("Mozilla/5.0")
                                     .timeout(5000)
                                     .get();

            // Select the table by its ID (e.g., "data-table")
            Element table = document.getElementById("data-table");

            // Get all rows (tr elements) from the table
            Elements rows = table.select("tr");

            // Loop through the rows
            for (Element row : rows) {
                // Get all cells (th or td elements) in each row
                Elements cells = row.select("th, td");

                // Extract and print data from each cell
                for (Element cell : cells) {
                    System.out.print(cell.text() + "\t");
                }
                System.out.println();  // Move to the next line after each row
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

And here’s how it works:

1. Connecting to the website: The Java web scraper connects to the target website and retrieves the HTML content. .userAgent() sets the User-Agent to mimic a real browser, while .timeout(5000) specifies a 5-second timeout.

Document document = Jsoup.connect("https://example.com")
                         .userAgent("Mozilla/5.0")
                         .timeout(5000)
                         .get();

2. Selecting the table: The table is selected using its id attribute (data-table in this case). The method getElementById() fetches the table HTML element by its element link.

Element table = document.getElementById("data-table");

3. Extracting table rows (<tr>): This selects all the rows (<tr> elements) within the table. The select() method allows you to query elements based on CSS selectors. In this case, "tr" is used to select all rows.

Elements rows = table.select("tr");

4. Iterating through rows and cells: For each row, the select("th, td") query selects all the header (<th>) and data (<td>) cells. This ensures you get both the header row and the data rows.

for (Element row : rows) {
    Elements cells = row.select("th, td");

5. Extracting cell data: The cell.text() method extracts the text content from each cell (<th> or <td>). It prints the text followed by a tab (\t) for formatting, so the data aligns horizontally in the output.

for (Element cell : cells) {
    System.out.print(cell.text() + "\t");
}

6. New line for each row: After iterating through all cells in a row, a newline is printed to separate the rows.

System.out.println();

Step 6: Manage Cookies and Sessions

Handling cookies and sessions is essential when you're scraping websites that require you to maintain a session across multiple requests, such as login pages or websites with persistent user-specific content. In a Java file, you can handle cookies and sessions using other web scraping libraries (e.g. Jsoup), which allows you to manage cookies by accessing and sending them along with requests.

1. Accessing cookies on initial request: When you make an initial request to a website, the server may respond with cookies (e.g., session cookies) that you need to store and send in subsequent requests.

import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.util.Map;

public class WebScraper {
    public static void main(String[] args) {
        try {
            // Connect to the website and execute the request
            Connection.Response response = Jsoup.connect("https://example.com/login")
                                                .method(Connection.Method.GET)
                                                .execute();

            // Get cookies from the response
            Map<String, String> cookies = response.cookies();

            // Print the cookies to see what's being stored
            System.out.println("Cookies: " + cookies);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

.execute(): Sends the request and gets a Connection.Response object, which contains the cookies returned by the server. response.cookies(): Extracts the cookies from the response and stores them in a Map<String, String>. These cookies often include session IDs that allow you to maintain the session across subsequent requests.

2. Sending cookies in subsequent requests: Once you've retrieved the cookies from the initial request, you can send them along with subsequent requests to maintain the session.

import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.util.Map;

public class WebScraper {
    public static void main(String[] args) {
        try {
            // Step 1: Perform initial request and get cookies
            Connection.Response loginResponse = Jsoup.connect("https://example.com/login")
                                                     .method(Connection.Method.GET)
                                                     .execute();

            // Step 2: Get the cookies from the initial response
            Map<String, String> cookies = loginResponse.cookies();

            // Step 3: Use the cookies to make a subsequent request
            Document document = Jsoup.connect("https://example.com/after-login")
                                     .cookies(cookies)  // Pass the cookies along with the request
                                     .get();

            // Print the page content after login
            System.out.println(document.body().text());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

.cookies(cookies): Adds the cookies you received from the previous response to the new request. This simulates a logged-in session or a state where the website recognizes your browser. Subsequent Requests: By sending these cookies, you can now access protected or personalized content (e.g., user account pages or dashboard content).

3. Handling login sessions: For websites that require login, you often need to send a POST request with login credentials and then store the session cookies.

import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.util.Map;

public class WebScraper {
    public static void main(String[] args) {
        try {
            // Step 1: Send a POST request with login credentials
            Connection.Response loginResponse = Jsoup.connect("https://example.com/login")
                                                     .data("username", "yourUsername")
                                                     .data("password", "yourPassword")
                                                     .method(Connection.Method.POST)
                                                     .execute();

            // Step 2: Get cookies after successful login
            Map<String, String> cookies = loginResponse.cookies();

            // Step 3: Use the cookies to access a protected page
            Document dashboard = Jsoup.connect("https://example.com/dashboard")
                                      .cookies(cookies)  // Use login cookies
                                      .get();

            // Print the content of the protected page
            System.out.println("Dashboard: " + dashboard.body().text());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Here’s how this code works:

  1. Login with POST request: Connection.Method.POST sends a POST request with login data (username and password) to authenticate the user. .data("key", "value") adds form data (in this case, the login credentials) to the POST request.
  2. Retrieve data from cookies: After logging in, the server sends cookies that maintain your session. These are stored in the Map<String, String> cookies.
  3. Access protected content: You can now use these cookies to access protected or personalized pages, such as a dashboard or user profile page.

4. Managing expired sessions: Web sessions often expire after some time. To handle this, you can:

  • Check the response for expiration: If a subsequent request returns a login page or a similar response, it means the session has expired.
  • Re-login programmatically: Re-login and obtain a new session, updating the cookies.

5. Handling cookies with multiple requests: For complex web scraping tasks, where multiple sequential requests are needed, you can manage cookies manually by storing and updating them between requests.

import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;

import java.util.Map;

public class WebScraper {
    public static void main(String[] args) {
        try {
            // Perform initial request to get cookies
            Connection.Response initialResponse = Jsoup.connect("https://example.com")
                                                       .method(Connection.Method.GET)
                                                       .execute();

            // Store cookies in a Map
            Map<String, String> cookies = initialResponse.cookies();

            // Perform another request using the same cookies
            Document page = Jsoup.connect("https://example.com/another-page")
                                 .cookies(cookies)
                                 .get();

            // Print the content of the page
            System.out.println(page.body().text());

            // Optionally, update cookies if the server sends new ones
            cookies.putAll(initialResponse.cookies());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Step 7: Navigate Multi-Page Sites (Handling Pagination)

Navigating multi-page sites and handling pagination is a common requirement in web crawling, especially when scraping search results, product listings, or news articles that span multiple web pages. In Java, using Jsoup, you can automate the process of scraping desired data from multiple pages by extracting and following the pagination links.

1. Identify the pagination structure: First, inspect the element of the target website and locate the pagination structure. Pagination is typically represented by a series of links, like "Next", "Previous", or numbered page links.

<div class="pagination">
  <a href="/page=1">1</a>
  <a href="/page=2">2</a>
  <a href="/page=3">3</a>
  <a href="/page=4">Next</a>
</div>

2. Extract the pagination link: Extract the URL for the "Next" or numbered page from the pagination section.

3. Loop through the pages: Continue sending requests for subsequent pages by following the pagination links until you reach the last page.

Let’s assume you’re scraping a website with paginated content where each web page contains product listings or articles.

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class PaginationScraper {
    public static void main(String[] args) {
        try {
            // Start from the first page
            String baseUrl = "https://example.com/page=1";
            boolean hasNextPage = true;

            while (hasNextPage) {
                // Fetch the current page
                Document document = Jsoup.connect(baseUrl)
                                         .userAgent("Mozilla/5.0")
                                         .timeout(5000)
                                         .get();

                // Extract the data (e.g., product listings or articles)
                Elements items = document.select(".item-class");  // Replace with actual CSS selector

                for (Element item : items) {
                    String title = item.select(".title-class").text();  // Example selector
                    String price = item.select(".price-class").text();  // Example selector
                    System.out.println("Title: " + title + ", Price: " + price);
                }

                // Find the link to the next page
                Element nextPageLink = document.select("a:contains(Next)").first();

                if (nextPageLink != null) {
                    // Get the URL for the next page
                    String nextUrl = nextPageLink.absUrl("href");  // Absolute URL for the next page
                    baseUrl = nextUrl;  // Set the baseUrl for the next iteration
                } else {
                    // No more pages
                    hasNextPage = false;
                }

            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Let’s take a closer look at how this code works. First, you start scraping from the first page, defined by baseUrl.

String baseUrl = "https://example.com/page=1";

The loop will continue until there are no more web pages to scrape. The hasNextPage flag is used to control the loop.

while (hasNextPage) {

Jsoup retrieves the HTML of the current page. The user agent header is specified to mimic a real browser, and a timeout is set to avoid hanging.

Document document = Jsoup.connect(baseUrl)
                         .userAgent("Mozilla/5.0")
                         .timeout(5000)
                         .get();

This is where you extract the actual data (like product titles, prices, or articles) from the page. The .select() method is used to target elements using CSS selectors.

Elements items = document.select(".item-class");
for (Element item : items) {
    String title = item.select(".title-class").text();
    String price = item.select(".price-class").text();
}

The "a:contains(Next)" CSS selector is used to find the "Next" button in the pagination section. This link typically points to the next page.

Element nextPageLink = document.select("a:contains(Next)").first();

If the "Next" button exists, the Java web scraper follows the link by updating the baseUrl with the `href` value of the next page. If no "Next" button is found, hasNextPage is set to false, ending the loop.

if (nextPageLink != null) {
    String nextUrl = nextPageLink.absUrl("href");
    baseUrl = nextUrl;
} else {
    hasNextPage = false;  // No more pages to scrape
}

Step 7.1: Alternative Pagination Methods

Different websites may have different pagination patterns. Here are a few variations:

1. Numbered pages: Websites may use direct numbered links instead of "Next". For example:

<div class="pagination">
  <a href="/page=1">1</a>
  <a href="/page=2">2</a>
  <a href="/page=3">3</a>
  <a href="/page=4">4</a>
</div>

In this case, you can extract all the numbered links and iterate through them.

Elements pageLinks = document.select(".pagination a");
for (Element link : pageLinks) {
    String pageUrl = link.absUrl("href");
    // Fetch each page with the pageUrl
}

2. URL-based incrementing: Some websites may use URLs where the page number is simply appended as a query parameter. For example:

https://example.com/page=1
https://example.com/page=2
https://example.com/page=3

In this case, you can increment the page number in a loop:

int pageNumber = 1;
boolean hasNextPage = true;

while (hasNextPage) {
    String pageUrl = "https://example.com/page=" + pageNumber;

    // Fetch and process the page
    Document document = Jsoup.connect(pageUrl)
                             .userAgent("Mozilla/5.0")
                             .timeout(5000)
                             .get();

    // Check if the page contains content, otherwise end the loop
    if (document.select(".no-results").size() > 0) {
        hasNextPage = false;
    }

    pageNumber++;
}

3. AJAX-based pagination: Some modern websites use AJAX to load additional content when the user scrolls down the page (infinite scrolling). In such cases, you need to simulate AJAX requests by inspecting the network activity in the browser's developer tools to find the API endpoint that loads more data.

Here's a basic pattern for handling AJAX-based pagination:

for (int page = 1; page <= totalPages; page++) {
    String apiUrl = "https://example.com/api/items?page=" + page;

    // Simulate an AJAX request by calling the API
    Document jsonResponse = Jsoup.connect(apiUrl)
                                 .ignoreContentType(true)
                                 .get();

    // Parse the JSON response and extract the data
    // Example: JsonNode jsonNode = new ObjectMapper().readTree(jsonResponse.text());
}

Step 8: Export the data to JSON

Exporting scraped data to JSON is a common web scraping task, especially when you want to store structured data that can easily be processed or used later in other applications. There are several Java web scraping libraries to work with JSON, such as Jackson, Gson, and org.json. In this example, we'll use the Jackson library, which is popular and widely used for JSON handling.

1. Perform Jackson dependency management: If you’re using Maven, add the following dependency to your pom.xml:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.13.0</version>
</dependency>

If you’re using Gradle, add this to your build.gradle:

implementation 'com.fasterxml.jackson.core:jackson-databind:2.13.0'

2. Create a Java class for the scraped data: Suppose we are performing web scraping Java of product data, and we want to store the product's name and price. We will create a Product class to hold this information.

public class Product {
    private String name;
    private String price;

    // Constructor
    public Product(String name, String price) {
        this.name = name;
        this.price = price;
    }

    // Getters and Setters
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPrice() {
        return price;
    }

    public void setPrice(String price) {
        this.price = price;
    }
}

3. Scrape data and store it in a list: Use Jsoup to scrape the data from a website and store the scraped items in a List<Product>.

import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import java.util.ArrayList;
import java.util.List;

public class WebScraper {
    public static void main(String[] args) {
        List<Product> products = new ArrayList<>();

        try {
            // Connect to the website and get the document
            Document document = Jsoup.connect("https://example.com/products")
                                     .userAgent("Mozilla/5.0")
                                     .timeout(5000)
                                     .get();

            // Select the product elements
            Elements items = document.select(".product-item");  // Adjust the selector to match the website

            // Loop through each product and extract data
            for (Element item : items) {
                String name = item.select(".product-name").text();  // Adjust the selector to match the website
                String price = item.select(".product-price").text();  // Adjust the selector to match the website

                // Create a Product object and add it to the list
                products.add(new Product(name, price));
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        // Now that we have the data, let's export it to JSON
        exportToJson(products);
    }

    // Function to export data to JSON
    public static void exportToJson(List<Product> products) {
        // Implement this in the next step
    }
}

4. Convert the data to JSON and export: Now, let’s implement the exportToJson method using the Jackson library to convert the list of Product objects to a JSON file.

import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.IOException;
import java.util.List;

public class WebScraper {

    // Other code...

    public static void exportToJson(List<Product> products) {
        // Create an ObjectMapper instance from Jackson
        ObjectMapper objectMapper = new ObjectMapper();

        try {
            // Write the list of products to a JSON file
            objectMapper.writeValue(new File("products.json"), products);
            System.out.println("Data exported to products.json");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Example output of products.json:

[
  {
    "name": "Product 1",
    "price": "$10.99"
  },
  {
    "name": "Product 2",
    "price": "$15.50"
  },
  {
    "name": "Product 3",
    "price": "$8.25"
  }
]

6. Pretty printing (optional): If you want to format the JSON file to be more human-readable (pretty-print), you can configure the ObjectMapper as follows:

objectMapper.writerWithDefaultPrettyPrinter().writeValue(new File("products.json"), products);

This will output a nicely formatted JSON file with indentation and line breaks.

7. Exporting to JSON string instead of file (optional): If you don't want to write to a file but instead convert the data to a JSON string (perhaps to send it over a network), you can use:

String jsonString = objectMapper.writeValueAsString(products);
System.out.println(jsonString);

8. Handling nested JSON data (optional): If the data you scrape has more complexity (e.g., nested data like product details), you can extend the Product class to handle nested objects. For example:

public class Product {
    private String name;
    private String price;
    private List<String> reviews;  // Example of nested data

    // Constructor, getters, and setters
}

Jackson will automatically convert the nested lists or objects into corresponding JSON structures.

Handling Challenges in Web Scraping with Java

Adding a powerful proxy server to your Java programs can help you bypass rate limits, geo-restrictions, or to avoid being blocked by the target website. To integrate proxies into your web scraping code, you'll need to configure your HTTP requests to use the proxy server.

1. Using Proxy with HttpURLConnection and Jsoup

You can use HttpURLConnection to set proxy details and then use Jsoup to make requests with this setup.

import org.jsoup.Jsoup;
import org.jsoup.Connection;
import org.jsoup.nodes.Document;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;

public class ProxyScraper {
    public static void main(String[] args) {
        try {
            // Define the proxy details
            String proxyHost = "your.proxy.host";
            int proxyPort = 8080; // Replace with your proxy port
            
            // Create a Proxy object
            Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));
            
            // Configure Jsoup to use the proxy
            Connection connection = Jsoup.connect("https://example.com")
                                        .proxy(proxy)
                                        .userAgent("Mozilla/5.0")
                                        .timeout(5000);

            // Fetch the page
            Document document = connection.get();
            
            // Print the content of the page
            System.out.println(document.title());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Here’s how this code works:

1. Create proxy object: Proxy.Type.HTTP specifies that the proxy is an HTTP proxy. Use Proxy.Type.SOCKS for SOCKS proxies.

Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(proxyHost, proxyPort));

2. Configure Jsoup connection: .proxy(proxy) applies the proxy settings to the Jsoup connection.

Connection connection = Jsoup.connect("https://example.com")
                             .proxy(proxy)
                             .userAgent("Mozilla/5.0")
                             .timeout(5000);

3. Fetch and print the page: connection.get() sends the request and retrieves the page using the configured proxy.

Document document = connection.get();
System.out.println(document.title());

2. Using Proxies with Multiple Requests

If you need to use different proxies for different requests, you can configure the proxy settings dynamically.

import org.jsoup.Jsoup;
import org.jsoup.Connection;
import org.jsoup.nodes.Document;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

public class ProxyScraper {
    public static void main(String[] args) {
        // List of proxy details
        List<Proxy> proxies = Arrays.asList(
            new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy1.host", 8080)),
            new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy2.host", 8080)),
            new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy3.host", 8080))
        );

        try {
            // Select a random proxy from the list
            Random random = new Random();
            Proxy selectedProxy = proxies.get(random.nextInt(proxies.size()));

            // Configure Jsoup to use the selected proxy
            Connection connection = Jsoup.connect("https://example.com")
                                        .proxy(selectedProxy)
                                        .userAgent("Mozilla/5.0")
                                        .timeout(5000);

            // Fetch the page
            Document document = connection.get();
            
            // Print the content of the page
            System.out.println(document.title());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Here’s how this code works:

1. You create a list of proxy objects to rotate IP addresses.

List<Proxy> proxies = Arrays.asList(
    new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy1.host", 8080)),
    new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy2.host", 8080)),
    new Proxy(Proxy.Type.HTTP, new InetSocketAddress("proxy3.host", 8080))
);

2. Randomly select a proxy from the list to use for the request.

Random random = new Random();
Proxy selectedProxy = proxies.get(random.nextInt(proxies.size()));

3. Apply the selected proxy to the Jsoup connection.

Connection connection = Jsoup.connect("https://example.com")
                             .proxy(selectedProxy)
                             .userAgent("Mozilla/5.0")
                             .timeout(5000);

3. Handling Proxy Authentication

If your proxy requires authentication (username and password), you may need to configure HttpURLConnection for authentication.

import org.jsoup.Jsoup;
import org.jsoup.Connection;
import org.jsoup.nodes.Document;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Authenticator;
import java.net.PasswordAuthentication;

public class ProxyScraper {
    public static void main(String[] args) {
        // Proxy authentication setup
        Authenticator.setDefault(new Authenticator() {
            @Override
            protected PasswordAuthentication getPasswordAuthentication() {
                return new PasswordAuthentication("username", "password".toCharArray());
            }
        });

        // Define the proxy details
        Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress("your.proxy.host", 8080));

        try {
            // Configure Jsoup to use the proxy
            Connection connection = Jsoup.connect("https://example.com")
                                        .proxy(proxy)
                                        .userAgent("Mozilla/5.0")
                                        .timeout(5000);

            // Fetch the page
            Document document = connection.get();
            
            // Print the content of the page
            System.out.println(document.title());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Scraping Dynamic Content Websites in Java

Scraping dynamic content in Java can be challenging because many modern websites use JavaScript to load content dynamically after the initial page load. To help web scrapers access such content, you'll need to handle JavaScript execution, which isn't directly supported by Jsoup as it only handles static HTML.

1. Use Selenium WebDriver

Selenium WebDriver can handle dynamic content by controlling a real browser (like Chrome or Firefox). First, you need to add Selenium WebDriver to your web scraping project. If you’re using Maven, add the following dependencies to your pom.xml:

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>selenium-java</artifactId>
    <version>4.7.2</version>
</dependency>

Also, you’ll need to download the WebDriver executable for the browser you plan to use (e.g., ChromeDriver for Chrome).

import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeOptions;
import java.util.List;

public class SeleniumScraper {
    public static void main(String[] args) {
        // Set the path to the WebDriver executable
        System.setProperty("webdriver.chrome.driver", "/path/to/chromedriver");

        // Configure options for headless mode (optional)
        ChromeOptions options = new ChromeOptions();
        options.addArguments("--headless"); // Run in headless mode

        // Create a new instance of the Chrome driver
        WebDriver driver = new ChromeDriver(options);

        try {
            // Navigate to the web page
            driver.get("https://example.com/dynamic-content");

            // Wait for dynamic content to load (adjust as needed)
            Thread.sleep(5000); // Wait for 5 seconds

            // Locate elements and extract data
            List<WebElement> items = driver.findElements(By.cssSelector(".item-class"));
            for (WebElement item : items) {
                String title = item.findElement(By.cssSelector(".title-class")).getText();
                String price = item.findElement(By.cssSelector(".price-class")).getText();
                System.out.println("Title: " + title + ", Price: " + price);
            }

        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // Close the browser
            driver.quit();
        }
    }
}

Here’s how this code works:

1. Set the path to the WebDriver executable.

System.setProperty("webdriver.chrome.driver", "/path/to/chromedriver");

2. Configure the browser to run in headless mode if you don’t need a GUI.

ChromeOptions options = new ChromeOptions();
options.addArguments("--headless");

3. Navigate to the URL, wait for dynamic content to load, and extract the required data.

driver.get("https://example.com/dynamic-content");
Thread.sleep(5000); // Wait for content to load
List<WebElement> items = driver.findElements(By.cssSelector(".item-class"));

2. Using HtmlUnit

HtmlUnit is a headless browser that can also offer Javascript support. It’s less resource-intensive compared to Selenium but may not support all modern JavaScript features.

Add the HtmlUnit dependency to your `pom.xml`:

<dependency>
    <groupId>org.seleniumhq.selenium</groupId>
    <artifactId>htmlunit-driver</artifactId>
    <version>3.2.0</version>
</dependency>

Here’s an example code snippet:

import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlElement;

public class HtmlUnitScraper {
    public static void main(String[] args) {
        try (WebClient webClient = new WebClient()) {
            // Disable JavaScript and CSS if not needed (improves performance)
            webClient.getOptions().setJavaScriptEnabled(true);
            webClient.getOptions().setCssEnabled(false);

            // Load the page
            HtmlPage page = webClient.getPage("https://example.com/dynamic-content");

            // Wait for JavaScript to finish executing (optional)
            webClient.waitForBackgroundJavaScript(5000);

            // Extract data
            for (HtmlElement item : page.getByXPath("//div[@class='item-class']")) {
                String title = item.getFirstByXPath(".//span[@class='title-class']").asText();
                String price = item.getFirstByXPath(".//span[@class='price-class']").asText();
                System.out.println("Title: " + title + ", Price: " + price);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

And here’s how this code works:

1. Configure WebClient and enable JavaScript execution:

WebClient webClient = new WebClient();
webClient.getOptions().setJavaScriptEnabled(true);
webClient.getOptions().setCssEnabled(false);

2. Load the page and optionally wait for JavaScript to finish:

HtmlPage page = webClient.getPage("https://example.com/dynamic-content");
webClient.waitForBackgroundJavaScript(5000); // Wait for JavaScript to execute

3. Use XPath to locate and extract data:

for (HtmlElement item : page.getByXPath("//div[@class='item-class']")) {
    String title = item.getFirstByXPath(".//span[@class='title-class']").asText();
    String price = item.getFirstByXPath(".//span[@class='price-class']").asText();
}

3. Analyzing Network Requests

If dynamic content is loaded via API requests, you can inspect the network traffic in your browser's developer tools to understand how the data is being fetched. You can then replicate these API calls in your Java code.

If you find that the content is loaded from an API endpoint like https://example.com/api/items, you can make HTTP requests directly:

import org.jsoup.Jsoup;
import org.jsoup.Connection;
import org.jsoup.nodes.Document;

public class ApiScraper {
    public static void main(String[] args) {
        try {
            // Make a direct API request
            Connection connection = Jsoup.connect("https://example.com/api/items")
                                        .userAgent("Mozilla/5.0")
                                        .timeout(5000);
            Document document = connection.get();
            
            // Process the JSON response (if applicable)
            System.out.println(document.text());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Here’s how this code works:

1. Make a direct request to the API endpoint:

Connection connection = Jsoup.connect("https://example.com/api/items");

2. Process the response, which might be JSON or another format:

Document document = connection.get();
System.out.println(document.text());

As infatica`s CTO & CEO, Pavlo shares the knowledge on the technical fundamentals of proxies.

You can also learn more about:

11 Best Proxy Browsers
Proxies and business
11 Best Proxy Browsers

Discover the best proxy browser options to enhance your web browsing experience. Learn how to configure proxies with a top server for improved privacy and security.

Selenium Scraping With Node.js
Web scraping
Selenium Scraping With Node.js

Learn Selenium scraping with Node.js in this step-by-step guide. Discover how to set up, extract data, handle errors, and optimize performance with Infatica proxies for seamless scraping results.

Web Scraping in Java
How to
Web Scraping in Java

Discover how to perform web scraping in Java using libraries like Jsoup, Selenium, and HtmlUnit. Learn to extract and automate data collection from websites and export data efficiently.

Get In Touch
Have a question about Infatica? Get in touch with our experts to learn how we can help.