用于网页抓取的流行语言有Python、JavaScript和Node.js、PHP、Java、C#等。因为有很多选择,想要确定哪种语言最合适并不容易。每种语言都有其优点和缺点。在本文中,我们将使用Java进行网页抓取并使用 Java创建一个网页抓取工具。
有两个最常用的Java网页抓取库——JSoup和HtmlUnit。
JSoup是一个强大的库,可以有效地处理格式错误的HTML。这个库的名字来自于短语“tag soup”,它指的是格式错误的HTML文档。
HtmlUnit是用于Java程序的无图形用户界面或无头的浏览器。它可以模拟浏览器的关键方面,例如从页面中获取特定元素、单击这些元素等。正如这个库的名称所暗示的那样,它通常用于单元测试。这是一种模拟浏览器以进行测试的方法。
HtmlUnit也可用于网页抓取。好消息是,只需一行,就可以关闭JavaScript和CSS。这个库对网页抓取很有帮助,因为大多数情况下不需要JavaScript和CSS。后面我们将检查这两个库并创建网页抓取工具。
使用Java构建网络爬虫的先决条件
本教程使用Java进行网页抓取,前提是您要熟悉Java编程语言。为了管理包,我们将使用Maven。
除了Java基础知识外,您需要对网站的工作原理有初步的了解。还需要对HTML和使用XPath或CSS Selectors选择其中的元素有很好的了解。请注意,并非所有库都支持XPath。
SS Selectors的快速概览
在我们继续本Java网页抓取教程之前,先回顾一下CSS Selectors:
●#firstname–选择任何id等于“firstname”的元素
●.blue–选择class包含“blue”的任何元素
●p–选择所有<p>标签
●div#firstname–选择等于“firstname”的div元素id
●p.link.new–请注意,此处没有空格。选择<p class=”link new”>
●p.link .new–请注意此处的空格。选择在<p class=”link”>里“new”类的任何元素
接下来,让我们回顾一下可用Java进行网页抓取的库。
使用JSoup配合Java抓取网页
JSoup可能是使用Java进行网页抓取最常用的库了。让我们使用这个库来创建一个Java网页抓取工具。
总体来说,使用Java进行网页抓取涉及三个步骤。
获取JSoup
使用Java进行网页抓取的第一步是获取Java库。Maven可以在这里提供帮助。使用任何Java IDE创建一个Maven项目。如果您不想使用Maven,请前往以下页面查找替代进行下载:
在pom.xml(Project Object Model)文件中,为依赖项添加一个新部分并为JSoup添加一个依赖项。该pom.xml文件如下:
<dependencies>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.14.1</version>
</dependency>
</dependencies>
有了以上条件,我们就可以创建一个Java抓取工具了。
获取和解析HTML
使用Java进行网页抓取的第二步是从目标URL中获取HTML并将其解析为Java对象。让我们从导入开始:
import org.jsoup.Connection;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
请注意,使用通配符导入所有内容-import org.jsoup.*.并不是一个好习惯。想要始终准确导入您需要的内容尽量少用通配符。上述导入是我们将在本Java网页抓取教程中使用的内容。
JSoup提供了这个connect功能。此函数连接URL并返回一个Document.以下是获取页面HTML的方法:
Document doc = Jsoup.connect(“https://en.wikipedia.
org/wiki/Jsoup”).get();
您会经常在一些地方看到这行代码,但它有一个缺点。这种快捷的方式没有做任何错误处理。更好的方法是创建一个函数。此函数以URL作为参数。首先,创建一个连接并将其存储在一个变量中。之后,get()调用连接对象的方法来检索HTML文档。该文档作为Document类的实例返回。该get()方法可以抛出一个IOException,此IOException需要进行处理,如下:
public static Document getDocument
(String url) {
Connection conn = Jsoup.connect(url);
Document document = null;
try {
document = conn.get();
} catch (IOException e) {
e.printStackTrace();
// handle error
}
return document;
}
在某些情况下,您需要传递自定义用户代理。这可以通过userAgent()在调用函数之前将用户代理字符串发送到函数来完成get()。
Connection conn = Jsoup.connect(url);
conn.userAgent(“custom user agent”);
document = conn.get();
此操作基本能解决遇到的常见问题。
查询HTML
任何Java网络爬虫构建过程中最关键的步骤是查询HTMLDocument对象以获取所需数据。这是您在用Java编写网络爬虫时花费大部分时间的地方。
JSoup支持多种提取所需元素的方法。比如getElementByID,getElementsByTag等,使得它更容易查询DOM。
这是导航到Wikipedia上的JSoup页面示例。右键单击标题并选择“检查”,从而打开选定标题的开发人员工具。
在这种情况下,可以使用getElementByID或getElementsByClass。这里要注意的一个重点是getElementById(注意单数Element)返回一个Element对象,而getElementsByClass(注意复数Elements)返回Element对象的数组列表。
这个Elements的非常方便,因为这个库有一个Elements的扩展ArrayList<Element>.这个扩展使代码更简洁并提供更多功能。
在下面的代码示例中,first()方法可用于从ArrayList.获取第一个元素,在获得元素的引用后,text()可以用来获取文本。
Element firstHeading = document.getElementsByClass
(“firstHeading”).
first();
System.out.println(firstHeading.text());
这些功能都不错;但是,它们仅限于JSoup。对于大多数情况,select函数可能是更好的选择。选择功能不起作用的唯一情况是您需要向上遍历文档的时候。在这些情况下,您可能需要使用parent(),children()和child()。有关所有可用方法的完整列表,请访问此页面:
https://jsoup.org/cookbook/extracting-data/dom-navigation
以下代码演示了如何使用selectFirst()方法,该方法会返回第一个匹配项。
Element firstHeading= document.selectFirst
(“.firstHeading”);
在这个例子中,使用了selectFirst()方法。如果需要选择多个元素,可以使用该select()方法。将采用CSS Selector作为参数并返回一个实例Elements,它是类型ArrayList<Element>的扩展。
使用HtmlUnit配合Java抓取网页
有很多方法可以读取和修改加载的页面。HtmlUnit可以像浏览器一样使网页交互变得容易,包括阅读文本、填写表单、单击按钮等。在这种情况下,我们将使用该库中的方法从URL读取信息。
如上一节所述,使用Java进行网页抓取涉及三个步骤。
获取和解析HTML
使用Java进行网页抓取的第一步是获取Java库。Maven可以在这里提供帮助。创建一个新的maven项目或使用在上一节中创建的项目。如果您不想使用Maven,请前往此页面查找替代进行下载:
https://sourceforge.net/projects/htmlunit/
在该pom.xml文件中,dependencies为HtmlUnit添加一个新部分并为其添加依赖项。该pom.xml文件将如下所示:
<dependency> <groupId>net.sourceforge.htmlunit
</groupId>
<artifactId>htmlunit</artifactId>
<version>2.51.0</version>
</dependency>
获取HTML
使用Java进行网页抓取的第二步是从目标URL中检索HTML作为 Java对象。让我们从导入开始:
import com.gargoylesoftware.htmlunit.
WebClient;
import com.gargoylesoftware.htmlunit.html.
DomNode;
import com.gargoylesoftware.htmlunit.html.
DomNodeList;
import com.gargoylesoftware.htmlunit.html.
HtmlElement;
import com.gargoylesoftware.htmlunit.html.
HtmlPage;
如上一节所述,执行通配符导入(例如import com.gargoylesoftware.htmlunit.html.*.)并不是一个好习惯。我们依旧不使用通配符,只导入我们需要的内容。这里导入的是我们将在本Java网页抓取教程中使用的内容。
在这个例子中,我们将抓取这个Librivox页面:
https://librivox.org/the-first-men-in-the-
moon-by-hg-wells
HtmlUnit使用WebClient类来获取页面。第一步是创建此类的实例。在这个例子中,不需要CSS渲染,也没有使用JavaScript。我们可以设置选项来禁用这两个。
WebClient webClient = new WebClient();
webClient.getOptions().setCssEnabled
(false);
webClient.getOptions().
setJavaScriptEnabled
(false);
HtmlPage page = webClient.getPage
(“https://librivox.org/the-first-men-in-
the-moon-by-hg-wells”);
请注意,getPage()函数可以抛出
IOException.您需要在try-catch中引用它。
以下是函数返回HtmlPage实例的一个实现示例:
public static HtmlPage getDocument
(String url) {
HtmlPage page = null;
try (final WebClient webClient = new WebClient()) { webClient.getOptions().setCssEnabled
(false); webClient.getOptions().
setJavaScriptEnabled
(false);
page = webClient.getPage(url);
} catch (IOException e) {
e.printStackTrace();
}
return page;
}
然后我们可以继续下一步。
查询HTML
有三类方法可以配合HTMLPage使用。第一个方法是利用DOM的方法,会使用getElementById(),getElementByName()等,然后返回一个元素。这些也跟getElementsById()一样有类似的对应项,会返回所有匹配项。这类方法会返回一个DomElement对象或一个DomElement对象列表。
HtmlPage page = webClient.getPage(“https://en.wikipedia.
org/wiki/Jsoup”);
DomElement firstHeading = page.
getElementById (“firstHeading”);
System.out.print(firstHeading.
asNormalizedText()); // prints Jsoup
第二类方法是使用XPath。在本Java网页抓取教程中,我们将使用Java创建一个网页抓取工具。
导航到此页面:
https://librivox.org/the-first-men-in-the-moon-by-hg-wells
右键单击书名,然后单击检查。如果您已经熟悉XPath,您应该能够看到选择书名的XPath是
//div[@class=”content-wrap clearfix”]/h1.
通过Xpath选择元素
有两种方法可以使用XPath—getByXPath()和getFirstByXPath().它们返回HtmlElement而不是DomElement。请注意,引号等特殊字符需要使用反斜杠进行转义:
HtmlElement book = page.getFirstByXPath(“//div[@class=
\”content-wrap clearfix\”]/h1″);
System.out.print(book.
asNormalizedText());
最后,第三类方法是使用CSS选择器。这类方法是querySelector()和querySelectorAll()。他们分别返回DomNode和DomNodeList
<DomNode>。
为了使这个Java网络爬虫教程更加真实,让我们打印页面中的所有章节名称、读者名称和阅读持续时间。第一步是确定可以选择所有行的选择器。接下来,我们将使用querySelectorAll()方法选择所有行。最后,我们将对所有行运行一个循环并调用querySelector()以提取每个单元格的内容。
String selector = “.chapter-download
tbody tr”;
DomNodeList<DomNode> rows = page.
querySelectorAll(selector);
for (DomNode row : rows) {
String chapter = row.querySelector
(“td:nth-child(2) a”).asNormalizedText();
String reader = row.querySelector
(“td:nth-child(3) a”).asNormalizedText();
String duration = row.querySelector
(“td:nth-child(4)”).asNormalizedText();
System.out.println(chapter + “\t ” +
reader + “\t ” + duration);
}
结论
—— 结论 ——
几乎每个企业都需要网络抓取来分析数据并在市场上保持竞争力。了解网页抓取的基础知识以及如何使用Java构建网页抓取工具可以最终帮助企业做出更明智、更快速的决策,这对于企业取得成功至关重要。在本文中,我们看到了两个Java网页抓取示例。
如果您已经了解Java,则可能不需要探索用于网络抓取的任何其他语言。