signed

QiShunwang

“诚信为本、客户至上”

java+HttpClients实现百度图片批量爬取

2021/4/26 20:50:02   来源:

直接下载代码,修改test类的参数即可使用。
源代码:https://github.com/gaojinheng/crawler-baiduPicture
工具:IntelliJ IDEA,jdk9.0.4

目录

  • 1 页面分析
  • 2 工程文件
  • 3 代码分析
    • 3.1 变量
    • 3.2 引入依赖
    • 3.3 Crawler.java
    • 3.4 HttpUtils.java
    • 3.5 test.java
  • 4 效果演示

1 页面分析

  • 进入https://image.baidu.com/,搜索杨超越
    在这里插入图片描述
    在这里插入图片描述

  • 在网页上右键单击,弹出菜单中选择“检查”,进入开发者模式。
    在这里插入图片描述

  • 选择Network,并且选中XHR后,重新加载页面,并滑动滚轮,更多图片会自动加载,此时我们看见下侧框中出现一些文件名类似的文件。
    在这里插入图片描述

  • 我们选择第一个文件,然后在跳出来的右侧框中选择Preview,我们看到的是一些json格式的数据,我们将折叠的“data”节点点开。
    在这里插入图片描述

  • data里面有30条数据
    在这里插入图片描述

  • 我们再随便点开一条数据,查找http或https链接,并将其复制粘贴到浏览器中打开。最终发现以".jpg"结尾的链接即是图片的链接。(由于图片链接比较好辨认,我们可以考虑用正则表达式)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

  • 我们再回过头来看看这些json数据的链接,我们分别点击这些文件,再点击Headers,查看Request URL,这就是我们获得刚才分析的json数据的url。
    分析:我们要用爬取图片,有两样东西是肯定要设置的,一是要搜索的关键字,二是爬取的图片数量。我们还要找出获取json数据的url之间的变化规律。

    • 关键字:既然我们在这里是搜索杨超越的图片,所以我们推断url中会有“杨超越”三个字。但我们在查看url时,并没有发现“杨超越”,那是因为“杨超越”被编码成了“%E6%9D%A8%E8%B6%85%E8%B6%8A”,其为queryWord和word的值,所以在爬取图片时,我们将queryWord和word的值的值改为我们想要搜索的关键词即可。
    • 图片数量:我们在之前的分析中,看见一个url请求返回了30张图片的数据,所以我们查看url中有没有数字“30”。我们看见所有url中都有“rn=30”,我们可以推断,rn表示图片的数量。
    • 链接变化规律:我们对比不同url,找出变化规律。我们看见了第1、2、3条url中分别有“pn=30”,“pn=60”,“pn=90”,可以推断,我们只要改变pn的值,就能加载更多不同的图片,且pn是rn的整数倍。
      在这里插入图片描述在这里插入图片描述
      在这里插入图片描述

2 工程文件

在这里插入图片描述

3 代码分析

3.1 变量

  • searchName:搜索时图片输入的关键字
  • picNum:一次请求得到图片的url的数量,即rn的值,经测试,rn不大于60
  • pageNum:获取多少页
  • httpUtils:工具类,我们写一个工具类,其内部有三个方法
    • doGetHtml:根据请求地址加载页面数据并返回
    • doGetImage:根据图片的url下载图片
    • doGetUrl:获取的html页面中的url
    private String searchName;//搜索时图片输入的关键字
    private int picNum;//一次请求得到图片的数量,不大于60
    private int pageNum;//获取多少页
    private HttpUtils httpUtils;//工具类

3.2 引入依赖

    <dependencies>
        <!--爬虫所需的依赖-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.2</version>
        </dependency>
        <!--测试-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

3.3 Crawler.java

  • 该类定义了上面提到的四个变量,并用searchName,picNum,pageNum三个变量来组建url,用httpUtil调用doGetHtml方法获取json数据,用doGetUrl方法解析json数据,并返回图片的url集合。最后用doGetImage方法根据图片url下载图片。
package crawler.baiduPicture.service;

import crawler.baiduPicture.util.HttpUtils;

import java.util.HashSet;

public class Crawler {
    private String searchName;//搜索时图片输入的关键字
    private int picNum;//一次请求得到图片的数量,不大于60
    private int pageNum;//获取多少页
    private HttpUtils httpUtils;//工具类

    public void CrawlerBaiduPicture() throws Exception {

        // 创建工具类对象
        HttpUtils httpUtils = new HttpUtils();

        // 按照页码搜索结果进行遍历解析
        for (int i = 0; i < pageNum; i++) {

            // 声明要解析的初始地址
            String url = "https://image.baidu.com/search/acjson?tn=resultjson_com&logid=6200883187742644854&ipn=rj&ct=201326592&is=&fp=result&queryWord=" + searchName + "&cl=2&lm=-1&ie=utf-8&oe=utf-8&adpicid=&st=-1&z=&ic=0&hd=&latest=&copyright=&word=" + searchName + "&s=&se=&tab=&width=&height=&face=0&istype=2&qc=&nc=1&fr=&expermode=&force=&pn=" + (i + 1) * picNum + "&rn=" + picNum + "&gsm=3c&1619267607521=";

            // 获取html页面(这里我们实际获取的是json)
            String html = httpUtils.doGetHtml(url);

            // 根据html获取图片的url的set集合
            HashSet<String> urlSet = httpUtils.doGetUrl(html);
            for (String urlset : urlSet) {
                // 遍历set集合,下载图片
                httpUtils.doGetImage(urlset);
            }
        }
    }

    public void setSearchName(String searchName) {
        this.searchName = searchName;
    }

    public void setPicNum(int picNum) {
        this.picNum = picNum;
    }

    public void setPageNum(int pageNum) {
        this.pageNum = pageNum;
    }
}

3.4 HttpUtils.java

  • 该类主要定义了doGetHtml,doGetImage, doGetUrl方法,并配置了HttpClient连接池和设置了请求信息。注意:doGetUrl方法用正则表达式解析json数据获取图片的url。
package crawler.baiduPicture.util;

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.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class HttpUtils {

    private PoolingHttpClientConnectionManager cm;
    private static int fileName = 1;
    public HttpUtils() {
        this.cm = new PoolingHttpClientConnectionManager();

        //设置最大连接数
        cm.setMaxTotal(100);

        //设置每个主机的最大连接数
        cm.setDefaultMaxPerRoute(10);
    }

    /**
     * 根据请求地址加载页面数据并返回
     * @param url
     * @return
     */
    public String doGetHtml(String url){
        //获取httpClient对象
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();

        //设置httpClient请求对象,设置url地址
        HttpGet httpGet = new HttpGet(url);

        //设置请求信息
        httpGet.setConfig(getConfig());

        //设置User-Agent
        httpGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.90 Safari/537.36");

        CloseableHttpResponse response = null;
        try {
            //使用httpClient发起请求,获取响应
            response = httpClient.execute(httpGet);

            //解析响应,返回结果
            if (response.getStatusLine().getStatusCode() == 200){
                //判断响应体Entity是否不为空,如果不为空就可以使用EntityUtils
                if (response.getEntity() != null){
                    String content = EntityUtils.toString(response.getEntity());
                    return content;
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭response,httpClient已交给连接池管理,不用关闭
            if (response != null){
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

//        返回空字符串
        return "";
    }

    /**
     * 根据图片的url下载图片
     * @param url
     * @return
     */
    public String doGetImage(String url){
        //获取httpClient对象
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();

        //设置httpClient请求对象,设置url地址
        HttpGet httpGet = new HttpGet(url);

        //设置请求信息
        httpGet.setConfig(getConfig());

        CloseableHttpResponse response = null;
        try {
            //使用httpClient发起请求,获取响应

            response = httpClient.execute(httpGet);
            //解析响应,返回结果
            if (response.getStatusLine().getStatusCode() == 200){
                //判断响应体Entity是否不为空,如果不为空就可以使用EntityUtils
                if (response.getEntity() != null){
                    //下载图片
                    //获取图片的后缀
                    String extName = url.substring(url.lastIndexOf("."));//从最后一个.开始截取

                    //下载图片
                    File file = new File("C:\\Users\\86134\\Desktop\\images");
                    if (!file.exists()){
                        file.mkdir();
                    }
                    OutputStream outputStream = new FileOutputStream(new File("C:\\Users\\86134\\Desktop\\images\\" + fileName++ + extName));

                    response.getEntity().writeTo(outputStream);
                }
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            //关闭response,httpClient已交给连接池管理,不用关闭
            if (response != null){
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

//        如果下载失败返回空字符串
        return "";
    }

    /**
     * 获取的html页面中的url
     * @param html
     * @return
     */
    public HashSet<String> doGetUrl(String html) throws Exception {
        HashSet<String> urlSet = new HashSet<String>();


        String regex = "https\\:\\/\\/(.*?)\\.jpg";

        Pattern pat = Pattern.compile(regex);
        Matcher mat = pat.matcher(html);
        while(mat.find()) {
            String group = mat.group();
            urlSet.add(group);
        }
        return urlSet;

    }

    //设置请求信息
    private RequestConfig getConfig() {
        RequestConfig config = RequestConfig.custom()
                .setConnectTimeout(1000)//创建连接的最长时间
                .setConnectionRequestTimeout(500)//获取时间的最长时间
                .setSocketTimeout(10000)//数据传输的最长时间
                .build();
        return config;
    }
}

3.5 test.java

  • 该类用于设置searchName,picNum,pageNum三个变量的值,即搜索关键字、每页获取图片数、获取多少页。并调用Crawler的CrawlerBaiduPicture方法爬取图片。
package crawler.baiduPicture;

import crawler.baiduPicture.service.Crawler;
import org.junit.Test;

public class test {

    private Crawler crawler = new Crawler();
    @Test
    public void intallPicture() throws Exception {
        //设置搜索关键字
        crawler.setSearchName("杨超越");

        //设置每页获取图片数
        crawler.setPicNum(60);

        //设置获取多少页
        crawler.setPageNum(10);

        //爬取图片
        crawler.CrawlerBaiduPicture();


    }
}

4 效果演示

在这里插入图片描述