Java爬蟲 爬取bing必應每日一圖背景圖下載到本地(HttpClient+Jsoup+Jackson)

Tom likes Jerry發表於2020-10-20

bing每日一圖適合作桌面背景,也經常作為我們網站某個頁面的背景,效果非常好,下面我們來介紹Java如何爬取。

這次爬取介紹兩個方法
方法一,爬取目標網頁,通過網頁元素得到圖片路徑
方法二,爬取圖片url地址的介面,通過返回的json資料爬取圖片


方法一:

  1. 分析: 首先我們開啟bing首頁 https://www.bing.com/?mkt=zh-CN,按照正常的思路,我們右鍵檢查,在頁面上尋找目標圖片,然後我們可以找到一個看起來很像背景圖的:在這裡插入圖片描述
    我們複製出來/th?id=OHR.MatiSiTemple_ZH-CN1153907273_UHD.jpg&rf=LaDigue_UHD.jpg&pid=hp&w=1920&h=1080&rs=1&c=4 /代表使用的是相對路徑,我們只需要拼接上網站根路徑(https://www.bing.com)即可, 我們在瀏覽器輸入,發現,這個就是我們想要的圖片地址了。
        但是,瀏覽器最終解析的可能和我們看到的不一樣,因為存在很多js請求,所以我們開啟network,清空,輸入bing主頁,檢視請求的返回值。
    在這裡插入圖片描述
    如圖,果然和我們在頁面上看到的不一樣,我們再嘗試去尋找返回的資料中是否有我們想要的。(我們在頁面中已經找到了圖片的url,所以我們在返回值搜尋url即可),我們就也在返回值中找到了圖片的url,我們找到了兩處,一個在一個id為bgImgProgLoad的div中,另一個在id為bgLink的link中,我們使用哪個都可以
    在這裡插入圖片描述
    在這裡插入圖片描述
    分析至此,我們就已經捋清了要爬取的步驟了,首先傳送Get請求訪問bing首頁,再使用Jsoup解析返回的頁面,獲取到id為bgLink的元素的url,然後再次傳送Get請求圖片,將圖片下載即可。

  2. 編碼 (pom檔案我放到了最後)

因為有兩種方法,所以我程式碼抽取到一個工具類中

  • 工具類 HttpUtils

程式碼中打了部分註釋,如果對哪部分覺得不清楚可以檢視上一篇爬取豆瓣的部落格,註釋非常詳細

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.*;
import java.text.SimpleDateFormat;
import java.util.Date;

public class HttpUtils {
	// 配置HttpClients連線池管理器,可以自動釋放連線池資源
    static PoolingHttpClientConnectionManager manager;
    static {
    	// 為了方便我直接丟到靜態程式碼塊中初始化
        manager = new PoolingHttpClientConnectionManager();
        manager.setMaxTotal(100);
        manager.setDefaultMaxPerRoute(10);
    }


    public static String doGetHtml(String url) {
    	// 建立httpclient物件用於發起請求
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(manager).build();
        // 建立get請求,指定uri
        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/86.0.4240.75 Safari/537.36");

        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() == 200) {
            	// 如果返回狀態碼為200代表成功,則將響應資料返回
                return EntityUtils.toString(response.getEntity(), "utf8");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (response != null) {
                try {
                	// 這裡只需要釋放response,httpclient由連線池管理器釋放
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return "";
    }

	/**
	* 下載圖片的方法基本與get請求類似
	* 主要區別在我們不返回響應字串,而是將響應資料直接通過位元組流寫入檔案
	*/
    public static void doGetImage(String url) {
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(manager).build();
        HttpGet httpGet = new HttpGet(url);
        httpGet.setHeader("user-agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36");

        CloseableHttpResponse response = null;
        try {
            response = httpClient.execute(httpGet);
            if (response.getStatusLine().getStatusCode() == 200) {
            	// 這裡格式化下時間作為檔名
                SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss-S");
                String dateStr = simpleDateFormat.format(new Date());
                // 建立位元組緩衝輸出流,大家要先建立F:/img/bing資料夾,我這裡沒有進行判斷
                BufferedOutputStream bs = new BufferedOutputStream(new FileOutputStream("F:\\img\\bing\\" + dateStr + ".png"));
                // 響應資料直接寫入輸出流中
                response.getEntity().writeTo(bs);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (response != null) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    private static RequestConfig getConfig() {
        RequestConfig config = RequestConfig.custom().setConnectionRequestTimeout(2000)
                .setConnectTimeout(3000)
                .setSocketTimeout(10 * 1000)
                .build();
        return config;
    }

}

  • 測試
    我們建立個Main進行測試下
import cn.jayjia.util.HttpUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;
public class Main {

    public static void main(String[] args) {
        // 方法1 抓取頁面資料
        String html = HttpUtils.doGetHtml("https://www.bing.com/?mkt=zh-CN");
        Document document = Jsoup.parse(html);
        Elements e = document.select("#bgLink");
        System.out.println(e.attr("href"));
        HttpUtils.doGetImage("https://www.bing.com" + e.attr("href"));
    }
}

執行就可以下載成功了。



方法二

  1. 分析
    我們除了可以從網頁拿到資料,那麼能不能直接從介面拿到返回資料呢?這兩種思路我們在爬蟲的時候都應該考慮一下,因此,我們F12,開啟network重新重新整理網頁,點選All觀察所有請求,可以找到這樣一條看起來很像的請求在這裡插入圖片描述
    我們點選進去檢視reponse
    在這裡插入圖片描述
    發現這和上一個獲取到的url是一樣的,看來我們找到了想要的介面,請求這個地址返回的是JSON資料,所以我們可以使用JackSon進行解析,獲取到url就能進行下載了。

  2. 編碼

我們可以使用工具類中的doGetHtml獲取返回的JSON資料,這就是封裝工具類的好處了,解析完JSON資料後,再呼叫doGetImage方法下載即可,所以我們只需要進行解析JSON,那麼就直接寫測試吧

  1. 測試
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class Main {

    public static void main(String[] args) {
        // 方法2 抓取介面資料 https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&nc=1603114218081&pid=hp&mkt=zh-CN
        String json = HttpUtils.doGetHtml("https://www.bing.com/HPImageArchive.aspx?format=js&idx=0&n=1&nc=1603114218081&pid=hp&mkt=zh-CN");
        // 這裡解析JSON我用的Jackson,大家也可以使用別的如GSON等
        ObjectMapper mapper = new ObjectMapper();
        try {
            JsonNode jsonNode = mapper.readTree(json);
            String url  = jsonNode.get("images").get(0).get("url").asText();
            System.out.println(url);
            HttpUtils.doGetImage("http://www.bing.com" + url);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }

    }
}

測試就可以下載成功了。

  1. POM檔案依賴
    <dependencies>
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.7</version>
        </dependency>

        <dependency>
            <groupId>org.jsoup</groupId>
            <artifactId>jsoup</artifactId>
            <version>1.10.3</version>
        </dependency>
        <!--解析JSON-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.10.2</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.10.2</version>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.10.2</version>
        </dependency>
    </dependencies>

相關文章