Java原生代码实现简易版爬虫

爬虫这个操作,应该都听说过吧,更多的应该是听过Python进行爬虫操作,但是身为主攻Java的人,怎么可以不用Java来实现爬虫呢?趁着期末考试的时间,用Java原生实现了非常简单的爬虫代码,没有采用任何第三方jar包。

先来简要说一说什么是爬虫吧:爬虫就是一个网络机器人,它通过url地址浏览网页,并将所浏览过的信息保存起来。如果一个页面中有许多url地址信息,那么爬虫会先将这些url地址存储在一个工作队列中,待完成当前爬取信息工作后再从工作队列中获取下一个带爬取信息的url信息,重复刚刚的步骤。

现在来进入正题——如何用Java原生代码实现简易的爬虫

假设我们现在有这样的一个网页

java_web_2_1.png

我希望能够将这道菜的名称以及做法步骤仅仅以文字信息保存下来。我们先看下这个网页的源代码

java_web_2_2.png

从源代码可以看出,菜名和步骤在<h2 id="steps"></h2><div class="steps"></div>这两个html标签之中,所以我们希望爬虫可以将这两个标签内的内容存储起来,这个时候,就可以用上正则表达式

Pattern urlPattern = compile("<h2 id=\"steps\">.+?</h2>.+?<div class=\"steps\">.+?</div>");

这样,我们就能够在整个页面的代码中,截取到了我们目标信息——菜品名以及做菜步骤。我们截取到的html代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
<h2 id="steps">
家常小炒扇贝肉的做法 &nbsp;
</h2>

<div class="steps">
<ol>

<li class="container" itemprop="recipeInstructions">
<p class="text" >新鲜扇贝洗净,用清水泡半小时</p>
<img src="http://s2.cdn.xiachufang.com/015b030b7e5245219d9d0afe8cddd4bd_640w_428h.jpg?imageView2/2/w/300/interlace/1/q/90" alt="家常小炒扇贝肉的做法 步骤1" width="300">
</li>

<li class="container" itemprop="recipeInstructions">
<p class="text" >葱姜蒜改刀切丝、辣椒切小段</p>
<img src="http://s2.cdn.xiachufang.com/52eff0fe10104c37b4f63883479d89c2_640w_428h.jpg?imageView2/2/w/300/interlace/1/q/90" alt="家常小炒扇贝肉的做法 步骤2" width="300">
</li>

<li class="container" itemprop="recipeInstructions">
<p class="text" >取小碗,放蚝油、酱油、1大匙清水调成料汁</p>
<img src="http://s1.cdn.xiachufang.com/cf437c2aaf3c4a83941298fd432cd8d6_640w_428h.jpg@2o_50sh_1pr_1l_300w_90q_1wh" alt="家常小炒扇贝肉的做法 步骤3" width="300">
</li>

<li class="container" itemprop="recipeInstructions">
<p class="text" >水开后把扇贝过水煮一下,扒出肉待用</p>
<img src="http://s2.cdn.xiachufang.com/e00073232c654908a78d9b0a740165c7_640w_428h.jpg?imageView2/2/w/300/interlace/1/q/90" alt="家常小炒扇贝肉的做法 步骤4" width="300">
</li>

<li class="container" itemprop="recipeInstructions">
<p class="text" >炒锅倒植物油,转中火,放葱姜蒜、小辣椒炒香</p>
<img src="http://s2.cdn.xiachufang.com/62126eb57b10497bad8ffe4060a2907b_3456w_2304h.jpg?imageView2/2/w/300/interlace/1/q/90" alt="家常小炒扇贝肉的做法 步骤5" width="300">
</li>

<li class="container" itemprop="recipeInstructions">
<p class="text" >转大火,放焯过的扇贝肉,放之前调好的汁(步骤3),</p>
<img src="http://s2.cdn.xiachufang.com/4e9e64a4afac4a698e9c360c818a6a2e_640w_428h.jpg?imageView2/2/w/300/interlace/1/q/90" alt="家常小炒扇贝肉的做法 步骤6" width="300">
</li>

<li class="container" itemprop="recipeInstructions">
<p class="text" >翻炒几下即可出锅</p>
<img src="http://s2.cdn.xiachufang.com/3a03f11296f14258bc1708e0b9a83822_640w_428h.jpg?imageView2/2/w/300/interlace/1/q/90" alt="家常小炒扇贝肉的做法 步骤7" width="300">
</li>
</ol>
</div>

接下来,我们再进一步的分析这段html代码,发现做菜步骤信息实际上是存储在了<p class="text" ></p>之中,因此,最后,我们再运用一次正则表达式,获取真正我们想要获取的内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public FoodBook(String context) {
steps = new TreeSet<>();
Pattern pattern = Pattern.compile("<p class=\"text\" >(.+?)</p>");
Matcher matcher = pattern.matcher(context);
boolean isFind = matcher.find();
while (isFind) {
steps.add(matcher.group(1));
isFind = matcher.find();
}
pattern = Pattern.compile("<h2 id=\"steps\">(.+?)</h2>");
matcher = pattern.matcher(context);
if (matcher.find()) {
foodName = matcher.group(1).replaceAll(" +", "")
.replace("&nbsp;", "");
}
}

如何从这个代码中获取我们想要的内容是已经解决了,但是还有一个最重要的问题——如何去访问网页呢?也就是爬虫最重要的一步。这个其实在Java的java.io.*java.net.*包中都已经实现了。我们所需要引用的包如下

1
2
3
4
5
6
7
8
9
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

看到了吧,在java.net.*包中有URL类和URLConnection类,这个两个类就是最重要的,通过这两个类的带有的方法,我们就可以轻松的实现访问url并获得网页内容的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override
public String getContext(String url) {
StringBuffer context = new StringBuffer();
BufferedReader br = null;
try {
URL realUrl = new URL(url);
URLConnection connection = realUrl.openConnection();
connection.connect();
br = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8"));
String tempLine;
while ((tempLine = br.readLine()) != null) {
context.append(tempLine);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return context.toString();
}

首先我们有一个url的字符串,我们需要将这个字符串转为能够开启连接的URL对象,然后利用URL对象的openConnection()获得一个URLConnection对象,只有通过URLConnection对象的.connect()方法,才能算是真正的开始连接url操作进行访问;随后,我们利用输入输出流将网页信息存到缓冲输入输出流BufferReader中。这样,我们就成功的爬取到了对应url的网页信息。最后,我们只需要对内容进行正则匹配工作获得我们想要的内容即可

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public ArrayList<FoodBook> getFoodBooks(String context) {
ArrayList<FoodBook> results = new ArrayList<FoodBook>();
Pattern urlPattern = compile("<h2 id=\"steps\">.+?</h2>.+?<div class=\"steps\">.+?</div>");
Matcher urlMatcher = urlPattern.matcher(context);
boolean isFind = urlMatcher.find();
System.out.println(isFind);
while (isFind) {
results.add(new FoodBook(urlMatcher.group()));
isFind = urlMatcher.find();
}
return results;
}

最后,我们就得到下面的结果

java_web_2_3.png

哔哩哔哩站点查看运行视频

https://www.bilibili.com/video/av18444787/

源代码地址

https://github.com/chuntaojun/Java-Spider