Java 爬虫工具Jsoup详解

时间:2021-07-17 04:34:13

 Java 爬虫工具Jsoup详解

Jsoup是一款 Java 的 HTML 解析器,可直接解析某个 URL 地址、HTML 文本内容。它提供了一套非常省力的 API,可通过 DOM,CSS 以及类似于 jQuery 的操作方法来取出和操作数据。

jsoup 的主要功能如下:

1. 从一个 URL,文件或字符串中解析 HTML;
2. 使用 DOM 或 CSS 选择器来查找、取出数据;
3. 可操作 HTML 元素、属性、文本;

jsoup 是基于 MIT 协议发布的,可放心使用于商业项目。

jsoup 可以从包括字符串、URL 地址以及本地文件来加载 HTML 文档,并生成 Document 对象实例。

简单而言,Jsoup就是先取html页面代码然后解析这些页面通过Jsoup携带的满足我们绝大多数需求的各种选择器从这个页面中获取我们所需要的重要数据的一款功能强大的html解析器,但也只是相对而言,这里的页面这是死的静态页面,如果你想获取动态生成的页面数据那么你得用到其他的java 爬虫技术,我会不定时更新这些技术一起探讨。下面我们来具体谈谈如何运用Jsoup

一、如何取页面

Jsoup提供了用来解析html页面的方法 parse(),我们通过解析它可以获取整个页面的dom对象,通过这个对象来获取你所需要的页面所须有的参数。获取页面的方法有很多,这里就简单的列举几个:

① 通过Jsoup携带的connect()方法

?
1
String htmlPage = Jsoup.connect("https://www.baidu.com").get().toString();

这个方法说需要的参数就是一个String类型的url链接,但是你的注意把这些链接的protrol加上,以免问题, 其实这个方法解决了我们很多问题,我们完全可以把Jsoup解析html抽取成一段通用工具类,然后通过改变拼接的url参数获取到很多我们想要的东西,举个例子:京东和淘宝的商品链接都是固定的,通过改变其三方商品ID来获取商品详情参数。

?
1
2
3
4
5
String url = "https://item.jd.com/11476104681.html";
 
完全可以替换成
 
String url = "https://item.jd.com/"+skuId+".html";

通过改变他的三方商品ID你就可以获取这个页面一些基本数据,像商品的图片和标题什么的都可以轻松获取,而价格因为做了一些相关方面的处理得动态的获取,这里先不做说明,后面慢慢会讲解。

②通过httpclient直接获取这个页面的静态页面

先贴一部分httpclient获取页面工具

?
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
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
 
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.ParseException;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;
/**
 * HTTP请求工具类.
 * @author LuoLong
 * @since 20150513
 *
 */
public class HttpClientUtils {
  /**
   * post方式请求.
   * @param url 请求地址.
   * @param params 请求参数
   * @return String
   */
  public static String post(String url, Map<String, String> params) {
    DefaultHttpClient httpclient = new DefaultHttpClient();
    String body = null;
 
    HttpPost post = postForm(url, params);
 
    body = invoke(httpclient, post);
 
    httpclient.getConnectionManager().shutdown();
 
    return body;
  }
 
  /**
   * get方式请求.
   * @param url 请求地址.
   * @return String
   */
  public static String get(String url) {
    DefaultHttpClient httpclient = new DefaultHttpClient();
    String body = null;
 
    HttpGet get = new HttpGet(url);
    body = invoke(httpclient, get);
 
    httpclient.getConnectionManager().shutdown();
 
    return body;
  }
  /**
   * 请求方法.
   * @param httpclient DefaultHttpClient.
   * @param httpost 请求方式.
   * @return String
   */
  private static String invoke(DefaultHttpClient httpclient,
      HttpUriRequest httpost) {
 
    HttpResponse response = sendRequest(httpclient, httpost);
    String body = paseResponse(response);
 
    return body;
  }
 
  /**
   *
   * @param response
   * @return
   */
  @SuppressWarnings({ "deprecation", "unused" })
  private static String paseResponse(HttpResponse response) {
    HttpEntity entity = response.getEntity();
 
    String charset = EntityUtils.getContentCharSet(entity);
 
    String body = null;
    try {
      body = EntityUtils.toString(entity);
    } catch (ParseException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
 
    return body;
  }
 
  private static HttpResponse sendRequest(DefaultHttpClient httpclient,
      HttpUriRequest httpost) {
    HttpResponse response = null;
 
    try {
      response = httpclient.execute(httpost);
    } catch (ClientProtocolException e) {
      e.printStackTrace();
    } catch (IOException e) {
      e.printStackTrace();
    }
    return response;
  }
 
  @SuppressWarnings("deprecation")
  private static HttpPost postForm(String url, Map<String, String> params) {
 
    HttpPost httpost = new HttpPost(url);
    List<NameValuePair> nvps = new ArrayList<NameValuePair>();
 
    Set<String> keySet = params.keySet();
    for (String key : keySet) {
      nvps.add(new BasicNameValuePair(key, params.get(key)));
    }
    try {
      httpost.setEntity(new UrlEncodedFormEntity(nvps, HTTP.UTF_8));
    } catch (UnsupportedEncodingException e) {
      e.printStackTrace();
    }
 
    return httpost;
  }
}

通过get()方法就可以获取html页面的String类型数据

?
1
2
3
4
String content = HttpClientUtils.get(url);
或者你可以直接把页面下载到本地,然后解析此html文档获取
File input = new File(FilePath);
Document doc = Jsoup.parse(input, "UTF-8", url);

二、解析页面获取需要的数据

当你获取到页面的dom对象后,那么下面的操作就非常简单了,你只需要通过操作这个dom对象来获取页面所有的静态资源,动态加载的资源不在此列,后面在做讲解。

先贴一段百度网页的源代码:

?
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
44
45
46
47
48
49
50
51
52
53
54
55
56
</form>
   <div id="m"></div>
  </div>
  </div>
  <div id="u">
  <a class="toindex" href="/" rel="external nofollow" >百度首页</a>
  <a href="javascript:;" rel="external nofollow" name="tj_settingicon" class="pf">设置<i class="c-icon c-icon-triangle-down"></i></a>
  <a href="https://passport.baidu.com/v2/?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2F" rel="external nofollow" rel="external nofollow" name="tj_login" class="lb" onclick="return false;">登录</a>
  </div>
  <div id="u1">
  <a href="http://news.baidu.com" rel="external nofollow" name="tj_trnews" class="mnav">新闻</a>
  <a href="http://www.hao123.com" rel="external nofollow" name="tj_trhao123" class="mnav">hao123</a>
  <a href="http://map.baidu.com" rel="external nofollow" name="tj_trmap" class="mnav">地图</a>
  <a href="http://v.baidu.com" rel="external nofollow" name="tj_trvideo" class="mnav">视频</a>
  <a href="http://tieba.baidu.com" rel="external nofollow" name="tj_trtieba" class="mnav">贴吧</a>
  <a href="http://xueshu.baidu.com" rel="external nofollow" name="tj_trxueshu" class="mnav">学术</a>
  <a href="https://passport.baidu.com/v2/?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2F" rel="external nofollow" rel="external nofollow" name="tj_login" class="lb" onclick="return false;">登录</a>
  <a href="http://www.baidu.com/gaoji/preferences.html" rel="external nofollow" name="tj_settingicon" class="pf">设置</a>
  <a href="http://www.baidu.com/more/" rel="external nofollow" name="tj_briicon" class="bri" style="display: block;">更多产品</a>
  </div>
 </div>
 </div>
 <div class="s_tab" id="s_tab">
 <b>网页</b>
 <a href="http://news.baidu.com/ns?cl=2&amp;rn=20&amp;tn=news&amp;word=" rel="external nofollow" wdfield="word" onmousedown="return c({'fm':'tab','tab':'news'})">新闻</a>
 <a href="http://tieba.baidu.com/f?kw=&amp;fr=wwwt" rel="external nofollow" wdfield="kw" onmousedown="return c({'fm':'tab','tab':'tieba'})">贴吧</a>
 <a href="http://zhidao.baidu.com/q?ct=17&amp;pn=0&amp;tn=ikaslist&amp;rn=10&amp;word=&amp;fr=wwwt" rel="external nofollow" wdfield="word" onmousedown="return c({'fm':'tab','tab':'zhidao'})">知道</a>
 <a href="http://music.baidu.com/search?fr=ps&amp;ie=utf-8&amp;key=" rel="external nofollow" wdfield="key" onmousedown="return c({'fm':'tab','tab':'music'})">音乐</a>
 <a href="http://image.baidu.com/search/index?tn=baiduimage&amp;ps=1&amp;ct=201326592&amp;lm=-1&amp;cl=2&amp;nc=1&amp;ie=utf-8&amp;word=" rel="external nofollow" wdfield="word" onmousedown="return c({'fm':'tab','tab':'pic'})">图片</a>
 <a href="http://v.baidu.com/v?ct=301989888&amp;rn=20&amp;pn=0&amp;db=0&amp;s=25&amp;ie=utf-8&amp;word=" rel="external nofollow" wdfield="word" onmousedown="return c({'fm':'tab','tab':'video'})">视频</a>
 <a href="http://map.baidu.com/m?word=&amp;fr=ps01000" rel="external nofollow" wdfield="word" onmousedown="return c({'fm':'tab','tab':'map'})">地图</a>
 <a href="http://wenku.baidu.com/search?word=&amp;lm=0&amp;od=0&amp;ie=utf-8" rel="external nofollow" wdfield="word" onmousedown="return c({'fm':'tab','tab':'wenku'})">文库</a>
 <a href="//www.baidu.com/more/" rel="external nofollow" onmousedown="return c({'fm':'tab','tab':'more'})">更多&raquo;</a>
 </div>
 <div class="qrcodeCon">
 <div id="qrcode">
  <div class="qrcode-item qrcode-item-1">
  <div class="qrcode-img"></div>
  <div class="qrcode-text">
   <p><b>手机百度</b></p>
  </div>
  </div>
 </div>
 </div>
 <div id="ftCon">
 <div class="ftCon-Wrapper">
  <div id="ftConw">
  <p id="lh"><a id="setf" href="//www.baidu.com/cache/sethelp/help.html" rel="external nofollow" onmousedown="return ns_c({'fm':'behs','tab':'favorites','pos':0})" target="_blank">把百度设为主页</a><a onmousedown="return ns_c({'fm':'behs','tab':'tj_about'})" href="http://home.baidu.com" rel="external nofollow" >关于百度</a><a onmousedown="return ns_c({'fm':'behs','tab':'tj_about_en'})" href="http://ir.baidu.com" rel="external nofollow" >About&nbsp;&nbsp;Baidu</a><a onmousedown="return ns_c({'fm':'behs','tab':'tj_tuiguang'})" href="http://e.baidu.com/?refer=888" rel="external nofollow" >百度推广</a></p>
  <p id="cp">&copy;2017&nbsp;Baidu&nbsp;<a href="http://www.baidu.com/duty/" rel="external nofollow" onmousedown="return ns_c({'fm':'behs','tab':'tj_duty'})">使用百度前必读</a>&nbsp;<a href="http://jianyi.baidu.com/" rel="external nofollow" class="cp-feedback" onmousedown="return ns_c({'fm':'behs','tab':'tj_homefb'})">意见反馈</a>&nbsp;京ICP证030173号&nbsp;<i class="c-icon-icrlogo"></i>&nbsp;<a id="jgwab" target="_blank" href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=11000002000001" rel="external nofollow" >京公网安备11000002000001号</a>&nbsp;<i class="c-icon-jgwablogo"></i></p>
  </div>
 </div>
 </div>
 <div id="wrapper_wrapper">
 </div>
</div>
<div class="c-tips-container" id="c-tips-container"></div>

在贴上Jsoup自身携带的常用的几个获取dom对象具体元素的方法:

?
1
2
3
4
5
6
method              description
getElementsByClass()       通过Class属性来定位元素,获取的是所有带这个class属性的集合
getElementsByTag();       通过标签名字来定位元素,获取的是所有带有这个标签名字的元素结合
getElementById();        通过标签的ID来定位元素,这个是精准定位,因为页面的ID基本不会重复
getElementsByAttributeValue();  通过属性和属性名来定位元素,获取的也是一个满足条件的集合;
getElementsByAttributeValueMatching()    通过正则匹配属性

比如说我现在要获取百度首页这个title,那么我们得先确定这玩意在哪,通过查看我们发现它是id=”u”的div标签的一个子元素,那么不管那么多我们先通过这个Id取到这个对象然后在获取这个Title,下面是具体操作

  1. //获取页面对象 
  2. String startPage="https://www.baidu.com"
  3.  
  4. Document document = Jsoup.connect(startPage).userAgent("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36").get(); 
  5.  
  6. //定位元素父级 
  7. Element parentElement = document.getElementById("u"); 
  8.  
  9. //定位具体元素 
  10. Element titleElement = parentElement.getElementsByTag("a").get(0); 
  11.  
  12. //获取所需数据 
  13. String title = titleElement.text(); 
  14.  
  15. System.out.println(title); 

又或者我需要获取页面《手机百度》这个数据:

  1. String startPage="https://www.baidu.com"
  2.  
  3. Document document = Jsoup.connect(startPage).userAgent("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36").get(); 
  4.  
  5. Element elementById = document.getElementById("qrcode"); 
  6.  
  7. String text = elementById.getAllElements().get(0).getAllElements().get(1).getElementsByTag("b").text(); 
  8.  
  9. System.out.println(text); 

这就是一个很简单的爬虫编写工具,Jsoup功能很强大,对直接爬取没有动态加载的静态资源页面再适合不过。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

原文链接:http://blog.csdn.net/smile_miracle/article/details/70677570