java网络爬虫基础httpclient及jsoup

一、网络爬虫概述

网络爬虫:是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。

应用场景:
1、实现搜索引擎
当我们经常查询某类数据,可能会在几个或多个不同的渠道来回检索,此时我们便可以通过抓取定向数据进行处理,存储到个人存储库中,比如用到ES,再从ES中进行全文检索就更方便地获取精准数据了。
2、数据分析
当我们需要在某一方面做决策时,可能会需要到大数据做支撑,毕竟在互联网时代,以技术为基础,以数据为驱动,比如当下火爆的自媒体运营,需要分析文章的数据,自己的文章数据分析可以看到,但我还想看竞争对手的或者整个行业的综合数据怎么办?如果手工一遍篇统计相加时非常慢的,此时便可以通过爬虫爬取指定的内容数据,再进行加工处理就可以了。
3、内容生产
当一个新网站刚刚建立,没有内容怎么办,便可以通过爬虫,进行抓取网络资源进行数据采集、加工处理、存储。

二、httpclient

1、无参get请求

通过爬虫获取百度首页
引入依赖包:

    <dependencies>
        <!--HTTP通信库,类似浏览器使用-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
        </dependency>
        <!--log4j-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>

日志配置:

log4j.rootLogger=DEBUG,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c] - %m%n

实现代码:

public class Crawlerdemo1 {
    public static void main(String[] args) {
        //创建一个httpClient对象(打开浏览器)
        CloseableHttpClient httpClient = HttpClients.createDefault();

        //发起get请求,创建HttpGet对象(输入网址)
        HttpGet httpGet = new HttpGet("http://www.baidu.com");
        CloseableHttpResponse response=null;
        try {
            //使用httpclient发起请求,返回响应(打开网址)
            response = httpClient.execute(httpGet);

            //解析响应获取数据
            //获取响应里的状态行里的状态码,请求成功就获取响应体
            if(response.getStatusLine().getStatusCode()==200){
                //获取响应体对象
                HttpEntity httpEntity = response.getEntity();
                //解析响应体,获取静态数据
                String content = EntityUtils.toString(httpEntity,"utf8");
                System.out.println(content.length());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(response!=null){
                    response.close();
                }
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

结果:

2287

2、带参get请求

通过爬虫获取百度关键词搜索页

public class Crawlerdemo2Parm {
    public static void main(String[] args) {
        //创建一个httpClient对象(打开浏览器)
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //请求地址:http://www.baidu.com/s?wd=seo
        HttpGet  httpGet = null;
        try {
            //创建URIBuilder
            URIBuilder uriBuilder = new URIBuilder("http://www.baidu.com/s");
            //设置参数
            uriBuilder.setParameter("wd","seo");

            //发起get请求,创建HttpGet对象(输入网址)
            httpGet = new HttpGet(uriBuilder.build());
        } catch (URISyntaxException e) {
            e.printStackTrace();
        }

        CloseableHttpResponse response=null;
        try {
            //使用httpclient发起请求,返回响应(打开网址)
            response = httpClient.execute(httpGet);

            //解析响应获取数据
            //获取响应里的状态行里的状态码,请求成功就获取响应体
            if(response.getStatusLine().getStatusCode()==200){
                //获取响应体对象
                HttpEntity httpEntity = response.getEntity();
                //解析响应体,获取静态数据
                String content = EntityUtils.toString(httpEntity,"utf8");
                System.out.println(content.length());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(response!=null){
                    response.close();
                }
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

结果:

325779

3、无参post请求

post请求只需将不带参数的get修改为post请求方式即可

HttpPost httpPost = new HttpPost("http://www.baidu.com");

结果:

2287

4、带参post请求

没找post提交后返回页面的网页

public class CrawlerPostParmdemo2 {
    public static void main(String[] args) {
        //创建一个httpClient对象(打开浏览器)
        CloseableHttpClient httpClient = HttpClients.createDefault();
        //请求地址:https://www.baidu.com/s?wd=seo
        //创建HttpPost对象,设置uri访问地址
        HttpPost httpPost = new HttpPost("https://www.baidu.com/s");
        //通过集合封装参数
        List<NameValuePair> parms = new ArrayList<NameValuePair>();
        parms.add(new BasicNameValuePair("wd","seo"));
        //创建表单Entity对象
        UrlEncodedFormEntity formEntity = null;
        try {
            formEntity = new UrlEncodedFormEntity(parms, "utf8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }

        //将表单Entity对象设置进post请求中
        httpPost.setEntity(formEntity);

        CloseableHttpResponse response=null;
        try {
            //使用httpclient发起请求,返回响应(打开网址)
            response = httpClient.execute(httpPost);

            //解析响应获取数据
            //获取响应里的状态行里的状态码,请求成功就获取响应体
            if(response.getStatusLine().getStatusCode()==200){
                //获取响应体对象
                HttpEntity httpEntity = response.getEntity();
                //解析响应体,获取静态数据
                String content = EntityUtils.toString(httpEntity,"utf8");
                System.out.println(content.length());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(response!=null){
                    response.close();
                }
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

5、连接池

每次请求都要创建HttpClient,为了避免频繁的创建连接和关闭连接,可以使用连接池。

public class HttpClientPool {
    public static void main(String[] args) {
        //创建连接池管理器
        PoolingHttpClientConnectionManager poolCM = new PoolingHttpClientConnectionManager();
        //设置最大连接数
        poolCM.setMaxTotal(100);
        //设置一个主机下最大连接数
        poolCM.setDefaultMaxPerRoute(10);

        //使用连接池创建连接
        doGet(poolCM);
        doGet(poolCM);
    }
    private static void doGet(PoolingHttpClientConnectionManager poolCM) {
        //创建httpClient连接对象
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(poolCM).build();
        //创建get请求
        HttpGet httpGet = new HttpGet("http://www.baidu.com");
        CloseableHttpResponse response=null;
        try {
             response = httpClient.execute(httpGet);

             if(response.getStatusLine().getStatusCode()==200){
                 HttpEntity httpEntity = response.getEntity();
                 String content = EntityUtils.toString(httpEntity, "utf8");
                 System.out.println(content.length());
             }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(response!=null){
                    response.close();
                }
                //此处不用关闭httpClient,由连接池管理
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

6、请求参数

在进行请求时,可能会因为网络等原因无法连接,此时需要设置相关的请求最大时间参数。

public class CrawlerConfigdemo {
    public static void main(String[] args) {
        //创建一个httpClient对象(打开浏览器)
        CloseableHttpClient httpClient = HttpClients.createDefault();

        //发起get请求,创建HttpGet对象(输入网址)
        HttpGet httpGet = new HttpGet("http://www.baidu.com");

        //设置请求参数
        RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(1000) //设置创建连接的最长时间,单位是毫秒
                .setConnectionRequestTimeout(500) //设置连接请求的最长时间
                .setSocketTimeout(10 * 1000) //设置数据传输的最长时间
                .build();

        //给请求设置请求参数
        httpGet.setConfig(requestConfig);

        CloseableHttpResponse response=null;
        try {
            //使用httpclient发起请求,返回响应(打开网址)
            response = httpClient.execute(httpGet);

            //解析响应获取数据
            //获取响应里的状态行里的状态码,请求成功就获取响应体
            if(response.getStatusLine().getStatusCode()==200){
                //获取响应体对象
                HttpEntity httpEntity = response.getEntity();
                //解析响应体,获取静态数据
                String content = EntityUtils.toString(httpEntity,"utf8");
                System.out.println(content.length());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(response!=null){
                    response.close();
                }
                httpClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

三、Jsoup

1、jsoup介绍

当我们通过HttpClient抓取到数据后,需要对数据进行处理,可以使用字符串处理工具解析页面,也可以使用正则表达式解析页面,但这些方法会带来很大的开发成本,可以使用专门解析html的技术Jsoup解析。

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

Jsoup参考文档:https://www.open-open.com/jsoup/

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

引入依赖

        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.11.3</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.7</version>
        </dependency>

2、jsoup3种解析方式

1)、url解析

    @Test
    public void testUrl() throws Exception {
        //解析url地址,第一个参数是url,第二个参数是超时时间
        Document doc = Jsoup.parse(new URL("http://www.baidu.com"), 10000);
        //使用标签选择器选择title标签中的内容
        String title = doc.getElementsByTag("title").first().text();
        System.out.println(title);
    }

运行结果:

百度一下,你就知道

PS:虽然使用Jsoup可以替代HttpClient直接发起请求解析数据,但是往往不会这样用,因为实际的开发过程中,需要使用到多线程,连接池,代理等等方式,而jsoup对这些的支持并不是很好,所以我们一-般把jsoup仅仅作为Html解析工具使用。

2)、字符串解析

	@Test
    public void testString() throws IOException {
        //使用工具类获取文件字符串
        String string = FileUtils.readFileToString(new File("C:\\Users\\ztnetbook\\Desktop\\file.html"), "utf8");
        //解析字符串
        Document doc = Jsoup.parse(string);
        //使用标签选择器选择title标签中的内容
        String title = doc.getElementsByTag("title").first().text();
        System.out.println(title);
    }

运行结果:

百度一下,你就知道

3)、文件解析

    @Test
    public void testFile() throws IOException {
        //解析文件
        Document doc = Jsoup.parse(new File("C:\\Users\\ztnetbook\\Desktop\\file.html"), "utf8");
        //使用标签选择器选择title标签中的内容
        String title = doc.getElementsByTag("title").first().text();
        System.out.println(title);
    }

运行结果:

百度一下,你就知道

3、使用dom方式遍历文档

元素获取。
1)、根据id查询元素getElementById

//标签是唯一的
Element id = doc.getElementById("Id");

2)、根据标签获取元素getElementsByTag

Element span = doc.getElementsByTag("span").first();

3)、根据class获取元素getElementsByClass

//class可以全部获取,也可以单独获取一个
Element byClass = doc.getElementsByClass("class_a class_b").first();

4)、根据属性获取元素getElementsByAttribute

Element abc = doc.getElementsByAttribute("abc").first();
Elements abc = doc.getElementsByAttributeValue("abc", "123");

元素中获取数据。

Element element = doc.getElementById("Id");

1).从元素中获取 id

String id = element.id();

2).从元素中获取classNames

String className = element.className();
Set<String> set = element.classNames();

3).从元素中获取属性的值attr

String attr = element.attr("abc");

4).从元素中获取所有属性attributes

Attributes attributes = element.attributes();

5).从元素中获取文本内容text

String text = element.text();

4、Selector选择器

1)、tagname:通过标签查找元素,比如: span

Elements elements = doc.select("span");

2)、#id:通过ID查找元素,比如: #id

Element element = doc.select("#id").first();

3)、.class:通过class名称查找元素,比如: .class_ ao

Elements elements = doc.select(".class_a");

4)、[attribute]:利用属性查找元素,比如: [abc]。

Element element = doc.select("[abc]").first();

5)、[attr=value]:利用属性值来查找元素,比如: [class=s_ name]

Elements elements = doc.select("[class=s_ name]");

5、Selector选择器组合

el#id: 元素+ID,比如:h3#id
el.class: 元素+class,比如:li.class_a
el[attr]: 元素+属性名,比如:span[abc]
任意组合: 比如:span[abc].s_ name
ancestor child: 查找某个元素下子元素,比如: .city_ con li查找"city_ con"下的所有li
parent > child: 查找某个父元素下的直接子元素,比如:.abc>ul>li 查找abc第一级(直接子元素)的ul, 再找所有ul下的第一级li。
parent> *: 查找某个父元素下所有直接子元素。

上一篇:java之Jsoup爬取网页内容


下一篇:JavaWeb12.4【XML:Jsoup解析器对象的使用】