返回博客
指南
Raluca PenciucLast updated on Mar 31, 20262 min read

《Java 网页抓取完全指南》

《Java 网页抓取完全指南》

与20世纪“时间就是金钱”的观念不同,如今一切都围绕数据展开。特别是在过去的十年里,网络爬虫变得极为流行。原因不难理解——互联网充斥着大量宝贵的信息,这些信息足以决定企业的成败。

随着企业逐渐意识到数据提取的益处,越来越多的人开始学习如何构建自己的爬虫工具。除了能为业务增长提供助力外,这对开发者而言还可能是一个提升编程技能的绝佳项目。

如果你是Java开发者,但日常工作与网页抓取无关,本文将带你探索一个能充分发挥技能的新领域。本文将提供一个分步教程,教你如何使用Java创建一个简单的网页抓取工具,从网站中提取数据并以CSV格式保存到本地。

了解网页抓取

什么是网页抓取?许多网站并未通过公开API提供数据,因此网页抓取工具会直接从浏览器中提取数据。这就像一个人手动复制文本,但速度快如闪电。

考虑到更优质的商业智能意味着更明智的决策,这一过程的价值远超表面印象。随着网站产出的内容日益庞大,完全依靠人工完成此类操作已不再可行。

您或许会疑惑:“这些数据能用来做什么?”让我们来看看网页抓取在哪些场景下能真正派上用场:

  • 潜在客户开发:持续运营的企业需要通过开发潜在客户来寻找客户。
  • 价格情报:企业制定产品定价和营销策略时,需参考竞争对手的价格。
  • 机器学习:为了让基于人工智能的解决方案正常运行,开发人员需要提供训练数据。

关于网络爬虫价值的这篇精彩文章中,提供了详细说明及更多应用场景。

尽管您已了解网页抓取的工作原理及其如何提升业务效率,但创建抓取工具并非易事。网站拥有多种手段来识别并阻止机器人访问其数据。

以下是一些示例:

  • 全自动公共图灵测试(CAPTCHA):这些逻辑题对人类来说相对容易解决,但对爬虫而言却是巨大的障碍。
  • IP封禁:若网站检测到同一IP地址发出多次请求,便会封锁该IP访问权限,或大幅降低访问速度。
  • 蜜罐:对人类不可见但对机器人可见的隐形链接;一旦机器人落入陷阱,网站便会封锁其IP地址。
  • 地理封锁:网站可能对特定内容实施地理限制。例如,当你查询其他地区的机票价格时,系统可能会返回本地区特有的信息。

应对所有这些障碍绝非易事。事实上,虽然构建一个尚可的机器人并不太难,但要打造一个出色的网页抓取工具却极其困难。因此,网页抓取API已成为过去十年中最热门的话题之一。

WebScrapingAPI 能从任何网站抓取 HTML 内容,并自动解决我之前提到的问题。此外,我们采用亚马逊云服务(AWS),确保速度与可扩展性。听起来很符合您的需求吗?立即开始免费试用 WebScrapingAPI,前 14 天内您将可进行 5000 次 API 调用。

理解网络

要理解网络,您需要了解超文本传输协议(HTTP),该协议阐明了服务器与客户端的通信方式。一条消息包含多项信息,用于描述客户端及其数据处理方式:方法、HTTP 版本和头部。

Web 爬虫使用 GET 方法进行 HTTP 请求,这意味着它们从服务器检索数据。一些高级选项还包括 POST 和 PUT 方法。有关详细信息,您可以在此处查看 HTTP 方法的详细列表

关于请求和响应的更多细节可查阅 HTTP 头部。您可以参考完整的头部列表,但与网页抓取相关的包括:

  • User-Agent:指示应用程序、操作系统、软件及其版本;网络爬虫依赖此标头使请求看起来更真实。
  • Host:您访问的服务器域名。
  • Referrer:包含用户访问的来源网站;因此,显示的内容可能会有所不同,这一点也必须予以考虑。
  • Cookie:保存关于请求和服务器的机密信息(例如身份验证令牌)。
  • Accept:确保服务器返回的响应采用特定格式(例如:text/plain、application/json 等)。

了解 Java

Java 是一种开源的面向对象语言,因此成为最受欢迎的编程语言之一。自我们首次接触 Java 以来,已过去近二十年,这种编程语言也变得越来越易于上手。

Java 的许多变革都旨在减少代码实现的依赖性。正因如此,许多开发者青睐该语言,但它还具备其他优势:

  • 它是开源的;
  • 提供丰富的API;
  • 它支持跨平台,具备更强的通用性;
  • 拥有详尽的文档和可靠的社区支持。

制作自己的网页爬虫

现在我们可以开始讨论数据提取了。首先,我们需要一个提供有价值信息的网站。在本教程中,我们选择抓取这个分享意大利食谱的网页

步骤 1:搭建运行环境

要构建我们的 Java 网页爬虫,首先需要确保具备所有必要条件:

  • Java 8:尽管 Java 11 是当前最新的长期支持(LTS)版本,但 Java 8 仍是开发者首选的生产环境标准。
  • Gradle:是一款功能丰富的灵活开源构建自动化工具,具备依赖管理等功能(需 Java 8 或更高版本);
  • Java 集成开发环境(IDE):本指南将使用 IntelliJ IDEA,因其与 Gradle 的集成非常简便。
  • HtmlUnit:在抓取数据时可模拟点击和提交表单等浏览器事件,并支持 JavaScript。

安装完成后,应验证是否正确遵循了官方指南。打开终端并运行以下命令:

> java -version
> gradle -v

这些命令应显示您机器上已安装的 Java 和 Gradle 版本:

如果未弹出任何错误提示,则说明一切就绪。

现在让我们创建一个项目,以便开始编写代码。幸运的是,JetBrains 提供了一份关于如何开始使用 IntelliJ 和 Gradle 的详尽教程,因此我们在配置过程中不会迷失方向。

请确保在创建项目后,让 IDE 完成首次构建,这样您将获得一个自动生成的文件树。

完成后,打开“build.gradle”文件,并在“dependencies”块中添加以下内容:

implementation('net.sourceforge.htmlunit:htmlunit:2.51.0')

这将把 HtmlUnit 安装到我们的项目中。别忘了点击右侧 Gradle 工具箱中的“重新加载”按钮,这样就能消除所有“未找到”的警告。

步骤 2:检查要抓取的页面

很好,继续吧!导航至您想要抓取的页面,在页面任意位置右键点击,然后选择“检查元素”。开发者控制台将弹出,您可以在其中查看网站的 HTML 代码。

步骤 3:发送 HTTP 请求并抓取 HTML

现在,为了将该 HTML 获取到本地机器上,我们需要使用 HtmlUnit 发送一个 HTTP 请求,该请求将返回文档。让我们回到 IDE,将这个想法转化为代码。

首先,编写使用 HtmlUnit 所需的导入语句:

import com.gargoylesoftware.htmlunit.*;
import com.gargoylesoftware.htmlunit.html.*;
import java.io.IOException;
import java.util.List;

接着初始化一个 WebClient 并向网站发送 HTTP 请求,该请求将返回一个 HtmlPage 对象。请务必记住,在收到响应后要关闭连接,否则进程会持续运行。

WebClient webClient = new WebClient(BrowserVersion.CHROME);

try {
   HtmlPage page = webClient.getPage("https://foodnetwork.co.uk/italian-family-dinners/");

   webClient.getCurrentWindow().getJobManager().removeAllJobs();
   webClient.close();
   recipesFile.close();

} catch (IOException e) {
   System.out.println("An error occurred: " + e);
}

值得一提的是,HtmlUnit会在控制台抛出一大堆错误信息,可能会让你以为电脑要炸了。不过别担心,其中98%都可以安全忽略。

这些错误主要源于 HtmlUnit 试图执行网站服务器端的 JavaScript 代码。不过,其中部分可能是真实错误,反映了你代码中的问题,因此在运行程序时最好留意这些提示。

你可以通过配置 WebClient 的某些选项,跳过查看其中一部分无用的错误:

webClient.getOptions().setCssEnabled(false);
webClient.getOptions().setThrowExceptionOnFailingStatusCode(false);
webClient.getOptions().setThrowExceptionOnScriptError(false);
webClient.getOptions().setPrintContentOnFailingStatusCode(false);

步骤 4:提取特定部分

现在我们已经获取了 HTML 文档,但我们需要的是数据,这意味着我们需要将之前的响应解析为人类可读的信息。

让我们从小处着手,先提取网站的标题。我们可以借助内置方法 getTitleText 来实现:

String title = page.getTitleText();
System.out.println("Page Title: " + title);

接下来,让我们从网站中提取所有链接。为此,我们可以使用内置的 getAnchorsgetHrefAttribute 方法,它们会从 HTML 中提取所有 <a> 标签,然后获取 href 属性的值:

List<HtmlAnchor> links = page.getAnchors();
for (HtmlAnchor link : links) {
   String href = link.getHrefAttribute();
   System.out.println("Link: " + href);
}

如您所见,HtmlUnit 提供了许多内置且直观易懂的方法,为您节省了大量查阅文档的时间。

让我们来看一个更贴近实际的例子。我们需要从网站中提取所有食谱,更准确地说,是提取它们的标题和地址。

如果你检查其中一张食谱卡片,会发现我们所需的所有信息都通过链接的属性提供,这意味着我们只需查找具有“card-link”类的链接并获取其属性即可。

List<?> anchors = page.getByXPath("//a[@class='card-link']");
for (int i = 0; i < anchors.size(); i++) {
   HtmlAnchor link = (HtmlAnchor) anchors.get(i);
   String recipeTitle = link.getAttribute("title").replace(',', ';');
   String recipeLink = link.getHrefAttribute();
}

这次我们使用 XPath 表达式,在 HTML 文档中任意层级深度搜索链接。随后,遍历结果列表,并提取每个链接的标题和 href 属性。

步骤 5:将数据导出为 CSV

当数据需要传递给另一个应用程序(在本例中是食谱聚合器)时,这种提取方式会非常有用。因此,我们需要将解析后的数据导出到外部文件中。

我们将创建一个 CSV 文件,因为它既能被其他应用程序轻松读取,也能通过 Excel 打开以进行进一步处理。首先,再进行一次导入:

import java.io.FileWriter;

然后初始化我们的 FileWriter,它将以“追加”模式创建 CSV 文件:

FileWriter recipesFile = new FileWriter("recipes.csv", true);
recipesFile.write("id,name,link\n");

创建完成后,我们还需写入 CSV 的第一行,即表格的表头。现在回到之前的循环(即解析所有食谱卡片的地方),并用以下代码完成:

recipesFile.write(i + "," + recipeTitle + "," + recipeLink + "\n");

文件写入已完成,现在是时候关闭它了:

recipesFile.close();

太棒了,就这样!现在我们可以以一种清晰、直观且便于分享的方式查看所有解析后的数据。

结论与替代方案

本教程到此结束。希望本文内容丰富,能帮助您更好地理解网页抓取技术。

正如你所想象的,这项技术远不止于为食谱聚合网站提供数据。如何找到正确的数据并加以分析以创造新机遇,全凭你自己去探索。

不过,正如我在文章开头所说,网络爬虫需要面对许多挑战。开发者可能会觉得用自己的爬虫解决这些问题很有趣,因为这既是一次绝佳的学习体验,又充满乐趣。但如果你有项目要完成,可能希望避免由此产生的相关成本(时间、金钱、人力)。

使用专用的API来解决这些问题总是更轻松。尽管存在JavaScript渲染、代理服务器、验证码等各种可能的阻碍,WebScrapingAPI都能轻松克服,并提供可定制的体验。此外,我们还提供免费试用选项,如果您还不太确定,何不试一试呢?

关于作者
Raluca Penciuc, 全栈开发工程师 @ WebScrapingAPI
Raluca Penciuc全栈开发工程师

Raluca Penciuc 是 WebScrapingAPI 的全栈开发工程师,主要负责开发爬虫、优化规避机制,并探索可靠的方法以降低在目标网站上的被检测概率。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网络数据。