Java爬虫-快速入门

HttpClient+JSoup详解 (附各种Demo)

写在前面:记录了学习数据挖掘以来的学习历程,先上之前的一些总结,随着学习的加深会慢慢更新。

Java爬虫-快速入门 目录

1.所需环境

2.HttpClient与Jsoup简介

3.为什么要和JSoup共同使用?

4.项目maven依赖

5.HttpClient的入门使用

(1)简介

(2)上一个简单的示例Demo:

6.JSoup的入门使用

(1)主要类的简介

(2)使用dom方法(遍历一个Document对象)来查找元素 -不推荐

(3)使用选择器语法来查找元素

(4)从元素抽取属性,文本和HTML

(5)这个示例是当时看完某教程后的练手Demo:

7.一个完整的Demo

(1)GetResult类,用HttpClient来抓取页面

(2)GetImg类,用Jsoup解析获取的页面

(3)测试单元test1.java

1.所需环境

我使用的环境是IDEA+Maven创建的maven project。其他方法亦可。

关于IDEA的下载安装及使用,可以参考这里:https://www.cnblogs.com/demingblog/p/5817125.html

关于Maven的下载安装,官网下载地址:http://maven.apache.org/download.cgi

关于Maven的相关配置请参考:https://blog.csdn.net/cs4380/article/details/79158268

2.HttpClient与Jsoup简介

1.什么是HttpClient?

HttpClient 是Apache Jakarta Common 下的子项目,可以用来提供高效的、最新的、功能丰富的支持 HTTP 协议的客户端编程工具包,并且它支持 HTTP 协议最新的版本和建议。

2.为什么使用HttpClient?

HTTP 协议可能是现在 Internet 上使用得最多、最重要的协议了,越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源。虽然在 JDK 的 java net包中已经提供了访问 HTTP 协议的基本功能,但是对于大部分应用程序来说,JDK 库本身提供的功能还不够丰富和灵活。

它的主要功能有:

(1) 实现了所有 HTTP 的方法(GET,POST,PUT,HEAD 等)

(2) 支持自动转向

(3) 支持 HTTPS 协议

(4) 支持代理服务器等

3.什么是JSoup?

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

它的主要功能有:

(1) 从一个URL,文件或字符串中解析HTML;

(2) 使用DOM或CSS选择器来查找、取出数据;

(3) 可操作HTML元素、属性、文本;

3.为什么要和JSoup共同使用?

httpClient 属于专业的抓取网页的库,可以设置代理,抓取失败可以重试抓取

在我的实际使用中,单独用jsoup也可以直接抓取网页,但是在抓取上,jsoup比较弱,API简单,功能也简单,主要是扩展htmlparser的功能吧,解析html。测试过程中jsoup抓取页面经常报错(time out等等)。

因此,我们可以用httpclient抓取网页,再用Jsoup.parse解析页面。

4.项目maven依赖

HttpClient 4.5.6:

org.apache.httpcomponents

httpclient

4.5.6

JSoup 1.8.3

org.jsoup

jsoup

1.8.3

5.HttpClient的入门使用

(1)简介

使用HttpClient发送请求、接收响应很简单,一般需要如下几步即可。

1. 创建HttpClient对象。

2. 创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。

3. 如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。

4. 调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse。

5. 调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。

6. 释放连接。无论执行方法是否成功,都必须释放连接

(2)上一个简单的示例Demo:

import org.apache.http.HttpEntity;

import org.apache.http.client.methods.CloseableHttpResponse;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.CloseableHttpClient;

import org.apache.http.impl.client.HttpClients;

import org.apache.http.util.EntityUtils;

import java.io.IOException;

public class test {

//声明需要爬取的网址

static String URL="http://www.baidu.com";

//主函数入口

public static void main(String args[]){

//建立一个新的请求客户端

CloseableHttpClient httpClient=HttpClients.createDefault();

//使用HttpGet的方式请求网址

HttpGet httpGet = new HttpGet(URL);

//获取网址的返回结果

CloseableHttpResponse response=null;

try {

response=httpClient.execute(httpGet);

} catch (IOException e) {

e.printStackTrace();

}

//获取返回结果中的实体

HttpEntity entity = response.getEntity();

//将返回的实体输出

try {

System.out.println(EntityUtils.toString(entity));

} catch (IOException e) {

e.printStackTrace();

}

}

}

6.JSoup的入门使用

我学习JSoup的过程中,这个作者的博客专栏提供了很大的帮助:专栏:使用JSOUP实现网络爬虫

同时,可以参考jsoup Cookbook(中文版),也非常的棒。

这里就根据CookBook来对jsoup的使用进行归纳

(1)主要类的简介

JSoup API中有6个包提供用于开发jsoup应用程序的类和接口。包中有很多类。

我们常用到的主要类:Jsoup、Document、Element/Elements

1.Document类:

是Jsoup的HTML文档对象模型,它由很多节点组成。

Document将html文档(String类型)解析为很多的Element和TextNode对象,其中TextNode继承自Node对象

继承链:

Document extends Element extends Node

TextNode extends Node

2.Element/Elements类:

Element类是Node的直接子类,它表示由一个标签名,多个属性和子节点组成的html元素。从这个元素中,你可以提取数据,可以遍历节点树,可以操纵html。

注:Node是节点的抽象模型。Elements, Documents, Comments等都是节点的实例。

Elements对象类似一个由多个Element对象组成的集合,有一接口为List,可以使用Element.select()方法去得到Elements 对象。

注:判断Elements对象是否为空需要.isEmpty()方法,而Element可以用==null

注:Elements.select()不能得到Element对象

3.Jsoup类:

通常用来建立连接,获取响应或发送响应

一个简单示例:

Document doc = Jsoup.connect(url).timeout(2000).get();

(2)使用dom方法(遍历一个Document对象)来查找元素 -不推荐

Elements这个对象提供了一系列类似于DOM的方法来查找元素,抽取并处理其中的数据。

1.查找元素 getElementById(String id) getElementsByTag(String tag) getElementsByClass(String className) getElementsByAttribute(String key) (and related methods) Element siblings: siblingElements(), firstElementSibling(), lastElementSibling(); nextElementSibling(), previousElementSibling() Graph: parent(), children(), child(int index)2.元素数据 attr(String key)获取属性attr(String key, String value)设置属性 attributes()获取所有属性 id(), className() and classNames() text()获取文本内容text(String value) 设置文本内容 html()获取元素内HTMLhtml(String value)设置元素内的HTML内容 outerHtml()获取元素外HTML内容 data()获取数据内容(例如:script和style标签) tag() and tagName()3.操作HTML和文本 append(String html), prepend(String html) appendText(String text), prependText(String text) appendElement(String tagName), prependElement(String tagName) html(String value)

引用自CookBook-dom

(3)使用选择器语法来查找元素

jsoup与其他解析器的区别就是可以使用类似jquery的选择器语法来搜索及过滤出所需的元素

这里我们还要介绍最重要的选择器语法

jsoup elements对象支持类似于CSS (或jquery)的选择器语法,来实现非常强大和灵活的查找功能。.

这个select 方法在Document, Element,或Elements对象中都可以使用。且是上下文相关的,因此可实现指定元素的过滤,或者链式选择访问。

Select方法将返回一个Elements集合,并提供一组方法来抽取和处理结果。

1.Selector选择器概述 tagname: 通过标签查找元素,比如:a ns|tag: 通过标签在命名空间查找元素,比如:可以用 fb|name 语法来查找 元素 #id: 通过ID查找元素,比如:#logo .class: 通过class名称查找元素,比如:.masthead [attribute]: 利用属性查找元素,比如:[href] [^attr]: 利用属性名前缀来查找元素,比如:可以用[^data-] 来查找带有HTML5 Dataset属性的元素 [attr=value]: 利用属性值来查找元素,比如:[width=500] [attr^=value], [attr$=value], [attr*=value]: 利用匹配属性值开头、结尾或包含属性值来查找元素,比如:[href*=/path/] [attr~=regex]: 利用属性值匹配正则表达式来查找元素,比如: img[src~=(?i)\.(png|jpe?g)] *: 这个符号将匹配所有元素 Selector选择器组合使用 el#id: 元素+ID,比如: div#logo el.class: 元素+class,比如: div.masthead el[attr]: 元素+class,比如: a[href] 任意组合,比如:a[href].highlight ancestor child: 查找某个元素下子元素,比如:可以用.body p 查找在"body"元素下的所有 p元素 parent > child: 查找某个父元素下的直接子元素,比如:可以用div.content > p 查找 p 元素,也可以用body > * 查找body标签下所有直接子元素 siblingA + siblingB: 查找在A元素之前第一个同级元素B,比如:div.head + div siblingA ~ siblingX: 查找A元素之前的同级X元素,比如:h1 ~ p el, el, el:多个选择器组合,查找匹配任一选择器的唯一元素,例如:div.masthead, div.logo2.伪选择器selectors :lt(n): 查找哪些元素的同级索引值(它的位置在DOM树中是相对于它的父节点)小于n,比如:td:lt(3) 表示小于三列的元素 :gt(n):查找哪些元素的同级索引值大于n,比如: div p:gt(2)表示哪些div中有包含2个以上的p元素 :eq(n): 查找哪些元素的同级索引值与n相等,比如:form input:eq(1)表示包含一个input标签的Form元素 :has(seletor): 查找匹配选择器包含元素的元素,比如:div:has(p)表示哪些div包含了p元素 :not(selector): 查找与选择器不匹配的元素,比如: div:not(.logo) 表示不包含 class=logo 元素的所有 div 列表 :contains(text): 查找包含给定文本的元素,搜索不区分大不写,比如: p:contains(jsoup) :containsOwn(text): 查找直接包含给定文本的元素 :matches(regex): 查找哪些元素的文本匹配指定的正则表达式,比如:div:matches((?i)login) :matchesOwn(regex): 查找自身包含文本匹配指定正则表达式的元素 注意:上述伪选择器索引是从0开始的,也就是说第一个元素索引值为0,第二个元素index为1等*/

引用自CookBook-选择器

select()的使用还可以参考这一篇博文,写的也很好。

(4)从元素抽取属性,文本和HTML

在解析获得一个Document实例对象,并查找到一些元素之后,如何取得在这些元素中的数据呢?

方法 要取得一个属性的值,可以使用Node.attr(String key) 方法 对于一个元素中的文本,可以使用Element.text()方法 对于要取得元素或属性中的HTML内容,可以使用Element.html(), 或 Node.outerHtml()方法

示例: String html = "

An example link.

"; Document doc = Jsoup.parse(html);//解析HTML字符串返回一个Document实现 Element link = doc.select("a").first();//查找第一个a元素

String text = doc.body().text(); // "An example link"//取得字符串中的文本 String linkHref = link.attr("href"); // "http://example.com/"//取得链接地址 String linkText = link.text(); // "example""//取得链接地址中的文本

String linkOuterH = link.outerHtml(); // "example" String linkInnerH = link.html(); // "example"//取得链接内的html内容

tips: 上述方法是元素数据访问的核心办法。此外还其它一些方法可以使用:

Element.id() Element.tagName() Element.className() and Element.hasClass(String className) 这些访问器方法都有相应的setter方法来更改数据.

引用自CookBook-抽取元素

(5)这个示例是当时看完某教程后的练手Demo:

import org.jsoup.Jsoup;

import org.jsoup.nodes.Document;

import org.jsoup.nodes.Element;

import org.jsoup.select.Elements;

import java.io.IOException;

class test2{

public static void main(String args[]){

//设置需要爬取的网页,这里为方便起见就直接用Jsoup自带的api来爬取网页了

//这个网页是桂林电子科技大的信息科技学院的学院新闻版块页面

String url = "http://www.guit.edu.cn/xwzx/mtxk.htm";

//声明Document类,来存储爬取到的html文档

Document doc = null;

try {

doc = Jsoup.connect(url).timeout(2000).get();

//调用Jsoup类中的connect()方法,url为需要爬取的页面

//timeout()来设置超时时间,get()方法来获取响应页面

} catch (IOException e) {

e.printStackTrace();

}

//System.out.println(doc);//测试用

//使用select选择器

Elements elements = doc.select(".box-list").select(".oh").select("a");

//System.out.println(elements);//测试用

for(Element e:elements){

if(e.text().length()>8){

//逐条输出新闻信息

System.out.println(e.text());

}

}

}

}

7.一个完整的Demo

当时写这个Demo时,这篇博文给了我很大帮助。我对其中的代码加以完善和详细注释,附在下面。

(1)GetResult类,用HttpClient来抓取页面

import org.apache.http.client.config.RequestConfig;

import org.apache.http.client.methods.CloseableHttpResponse;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.CloseableHttpClient;

import org.apache.http.impl.client.HttpClientBuilder;

import org.apache.http.util.EntityUtils;

public class GetResult {

public static String getResult(String url) throws Exception {

//这里用了try-with-resource语法,在try()括号中的资源会在try语句块执行完之后自动释放

try (CloseableHttpClient httpClient = HttpClientBuilder.create().build();

CloseableHttpResponse response = httpClient.execute(new HttpGetConfig(url)))

{

String result = EntityUtils.toString(response.getEntity());

return result;

} catch (Exception e) {

System.out.println("获取失败");

return "";

}

//所以不需要再finally中释放资源。

}

}

//内部类,继承HttpGet,为了设置请求超时的参数

class HttpGetConfig extends HttpGet {

public HttpGetConfig(String url) {

super(url);

setDefaulConfig();

}

private void setDefaulConfig() {

this.setConfig(RequestConfig.custom()

.setConnectionRequestTimeout(10000)

.setConnectTimeout(10000)

.setSocketTimeout(10000).build());

this.setHeader("User-Agent", "spider");

}

}

(2)GetImg类,用Jsoup解析获取的页面

import org.jsoup.Jsoup;

import org.jsoup.nodes.Document;

import org.jsoup.nodes.Element;

import org.jsoup.select.Elements;

import java.io.File;

import java.io.FileOutputStream;

import java.io.InputStream;

import java.io.OutputStream;

import java.net.URL;

import java.net.URLConnection;

public class GetImg {

public class GetImg {

public GetImg(String url) throws Exception{

//获取工具类GetResult返回的html,并用Jsoup解析

String result = GetResult.getResult(url);

Document document = Jsoup.parse(result);

//若HTML文档包含相对URLs路径,需要将这些相对路径转换成绝对路径的URLs

document.setBaseUri(url);//指定base URI

//获取所有的img元素

Elements elements = document.select("img");

int i=1;

for (Element e : elements) {

//获取每个src的绝对路径

String src = e.absUrl("src");

URL urlSource = new URL(src);

URLConnection urlConnection = urlSource.openConnection();

//设置图片名字

String imageName = src.substring(src.lastIndexOf("/") + 1,i++);

//控制台输出图片的src

System.out.println(e.absUrl("src"));

//通过URLConnection得到一个流,将图片写到流中,并且新建文件保存

InputStream in = urlConnection.getInputStream();

OutputStream out = new FileOutputStream(new File("E:\\IDEA\\imgs\\", imageName));

byte[] buf = new byte[1024];

int l = 0;

while ((l = in.read(buf)) != -1) {

out.write(buf, 0, l);

}

}

}

}

想要了解更多关于URL的处理,可以查看CookBook-URL

(3)测试单元test1.java

import org.junit.Test;

/**

* 2018-9-8 单元测试

* @author ljx

*/

public class test1 {

@Test

public void testGetResult() throws Exception{

GetImg getImg = new GetImg("https://www.bilibili.com/");

}

}

关于JUnit我这里就不班门弄斧了,也是正在学习,想要了解的转这里。