了解网页抓取
什么是网页抓取?许多网站并未通过公开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);
接下来,让我们从网站中提取所有链接。为此,我们可以使用内置的 getAnchors 和 getHrefAttribute 方法,它们会从 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都能轻松克服,并提供可定制的体验。此外,我们还提供免费试用选项,如果您还不太确定,何不试一试呢?




