HttpClient+jsoup实现java爬虫

我们爬取的是一个电影网站http://www.coupling.pw/fl/dy/,下载图片、获取名字、百度云链接、密码

HttpClient+jsoup实现java爬虫

为了简单,就直接用maven项目,当然你也可以直接导入jar包

httpclient、jsoup、commons-io、commons-lang3、junit,只用到了这几个包。
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.6</version>
</dependency>
    <dependency>
        <groupId>org.jsoup</groupId>
        <artifactId>jsoup</artifactId>
        <version>1.11.3</version>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.6</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.7</version>
    </dependency>
    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>

然后是pojo

package com.xiaoyu.pojo;

import java.util.Date;

/**
 * Created by hasee on 2019/4/16.
 */
public class Movie {
    private String name;
    private String picname;
    private String mlink;
    private String pass;
    private Date updtime;

    public Movie() {
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPicname() {
        return picname;
    }

    public void setPicname(String picname) {
        this.picname = picname;
    }



    public String getPass() {
        return pass;
    }

    public void setPass(String pass) {
        this.pass = pass;
    }

    public Date getUpdtime() {
        return updtime;
    }

    public void setUpdtime(Date updtime) {
        this.updtime = updtime;
    }

    @Override
    public String toString() {
        return "Movie{" +
                "name='" + name + '\'' +
                ", picname='" + picname + '\'' +
                ", mlink='" + mlink + '\'' +
                ", pass='" + pass + '\'' +
                ", updtime=" + updtime +
                '}';
    }

    public String getMlink() {
        return mlink;
    }

    public void setMlink(String mlink) {
        this.mlink = mlink;
    }
}

然后是工具类

public class HttpUtils {
    //创建连接池管理器
    private  PoolingHttpClientConnectionManager cm;

    public HttpUtils() {
        cm=new PoolingHttpClientConnectionManager();
        //设置最大连接数
        cm.setMaxTotal(100);
        //设置每个主机的最大连接数
        cm.setDefaultMaxPerRoute(10);
        //使用连接池管理器发送请求;
    }


    public  String doGetHtml(String url){
       //从连接池里取HttpClient对象
        CloseableHttpClient httpClient= HttpClients.custom().setConnectionManager(cm).build();
        HttpGet httpGet=new HttpGet(url);
        //配置请求信息
        httpGet.setConfig(getConfig());

        //3.按回车,发送请求,返回响应,使用HttpClient对象发送请求
        CloseableHttpResponse response= null;
        try {
            response = httpClient.execute(httpGet);
            //4.分析响应获取数据
            //判断状态码是否是200
            if(response.getStatusLine().getStatusCode()==200){
                if(response.getEntity()!=null) {
                    String content = EntityUtils.toString(response.getEntity() ,"utf8");
                    return content;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public  String doGetImage(String url){
        //从连接池里取HttpClient对象
        CloseableHttpClient httpClient= HttpClients.custom().setConnectionManager(cm).setConnectionManagerShared(true).build(); //设置共享连接池


        HttpGet httpGet=new HttpGet(url);
        //配置请求信息
        httpGet.setConfig(getConfig());

        //3.按回车,发送请求,返回响应,使用HttpClient对象发送请求
        CloseableHttpResponse response= null;
        try {
            response = httpClient.execute(httpGet);
            //4.分析响应获取数据
            //判断状态码是否是200
            if(response.getStatusLine().getStatusCode()==200){
                if(response.getEntity()!=null) {
                    //获取图片后缀
                    String extName=url.substring(url.lastIndexOf("."));
                    //创建图片名
                    String picName= UUID.randomUUID().toString()+extName;
                    //下载图片名
                    OutputStream os= new FileOutputStream(new File("C:\\Users\\hasee\\Desktop\\http\\"+picName));
                    response.getEntity().writeTo(os);
                    return picName;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                if(response!=null) {
                    response.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public  List<Movie> parse(String html) throws  Exception{
        List<Movie> list=new ArrayList<Movie>();
        Document doc= Jsoup.parse(html);
        Elements elements=doc.select("article");
        for(Element e:elements){
            //三秒爬一次
           // Thread.sleep(3000);
            Movie movie=new Movie();
            Element link=e.getElementsByTag("a").first();
            Element image=link.getElementsByTag("img").first();
            //获取图片链接
            String src=image.attr("src");
            String picname= this.doGetImage(src);
            //存图片
            movie.setPicname(picname);

            Element a=e.select("div header h2 a").first();
            //爬取标题
            String name=a.text();
            movie.setName(name);
            //获取进入的链接
            String url=a.attr("href");
            String infoHtml=this.doGetHtml(url);
            Document doc2= Jsoup.parse(infoHtml);
            Element p=doc2.select(".entry-content p:has(a)").first();
            //链接
            if(p!=null) {
                String mlink = p.getElementsByTag("a").first().attr("href");
                movie.setMlink(mlink);
                //提取码
                String pass = p.text().substring(url.lastIndexOf(":")).replace("\"", "");
                movie.setPass(pass);
            }
            //爬起时间
            movie.setUpdtime(new Date());
            list.add(movie);
        }
        return list;
    }

    private  RequestConfig getConfig(){
        RequestConfig config= RequestConfig.custom().setConnectTimeout(1000)//设置最长连接时间,单位毫秒
                .setConnectionRequestTimeout(500)//获取连接的最长时间
                .setSocketTimeout(10*1000)//设置数据传递最长时间
                .build();
        return config;
    }
}

然后是测试代码,这里也就直接输出,没有存入数据库

@Test
    public  void TestMain() throws Exception {
        HttpUtils httpUtils=new HttpUtils();
        //获取html
        String html= httpUtils.doGetHtml("http://www.coupling.pw/fl/dy");
        //jsoup解析html
        List<Movie> list=httpUtils.parse(html);
        if(list!=null&&(list.size()!=0)) {
            for (int i = 0; i < list.size(); i++) {
                System.out.println(list.get(i).toString());
            }
        }
    }

这样就介绍了,用httpclient爬取html,用jsoup解析HTML,jsoup的的selector选择器参考https://www.open-open.com/jsoup/

Selector选择器概述

  • tagname: 通过标签查找元素,比如:a
  • ns|tag: 通过标签在命名空间查找元素,比如:可以用 fb|name 语法来查找 <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.logo

伪选择器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等