欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何使用Java爬蟲批量爬取圖片

 更新時(shí)間:2023年04月14日 09:50:37   作者:CrazyDragon_King  
這篇文章主要介紹了如何使用Java爬蟲批量爬取圖片,對于爬蟲的入門來說,圖片相對來說是比較容易獲取的,因?yàn)榇蟛糠謭D片都不是敏感數(shù)據(jù),所以不會遇到什么反爬措施,對于入門爬蟲來說是比較合適的,需要的朋友可以參考下

Java爬取圖片

現(xiàn)在開始學(xué)習(xí)爬蟲,對于爬蟲的入門來說,圖片相對來說是比較容易獲取的,因?yàn)榇蟛糠謭D片都不是敏感數(shù)據(jù),所以不會遇到什么反爬措施,對于入門爬蟲來說是比較合適的。

使用技術(shù):Java基礎(chǔ)知識、HttpClient 4.x 、Jsoup

學(xué)習(xí)目標(biāo):下載靜態(tài)資源圖片。

爬取思路

對于這種圖片的獲取,其實(shí)本質(zhì)上就是就是文件的下載(HttpClient)。但是因?yàn)椴恢皇谦@取一張圖片,所以還會有一個(gè)頁面解析的處理過程(Jsoup)。

Jsoup:解析html頁面,獲取圖片的鏈接。

HttpClient:請求圖片的鏈接,保存圖片到本地。

具體步驟

首先進(jìn)入首頁分析,主要有以下幾個(gè)分類(這里不是全部分類,但是這幾個(gè)也足夠了,這只是學(xué)習(xí)技術(shù)而已。),我們的目標(biāo)就是獲取每個(gè)分類下的圖片。

這里來分析一下網(wǎng)站的結(jié)構(gòu),我這里就簡單一點(diǎn)吧。 下面這張圖片是大致的結(jié)構(gòu),這里選取一個(gè)分類標(biāo)簽進(jìn)行說明。 一個(gè)分類標(biāo)簽頁含有多個(gè)標(biāo)題頁,然后每個(gè)標(biāo)題頁含有多個(gè)圖片頁。(對應(yīng)標(biāo)題頁的幾十張圖片)

在這里插入圖片描述

對網(wǎng)站的結(jié)構(gòu)有了大致了解之后,就可以著 手開始爬取圖片了。 這里還有一個(gè)需要注意,大概是前輩們做得太過了,導(dǎo)致這個(gè)網(wǎng)站已經(jīng)開始有反爬蟲機(jī)制了。不過,幸好它還不是很強(qiáng)大,我們還是可以繞過去的。這個(gè)網(wǎng)站的反爬蟲機(jī)制主要就是:UA、Referer。

這是從知乎上面學(xué)習(xí)到的經(jīng)驗(yàn)。

具體代碼

導(dǎo)入項(xiàng)目依賴jar包坐標(biāo)或者直接下載對應(yīng)的jar包,導(dǎo)入項(xiàng)目也可。

<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>

實(shí)體類 Picture 和 工具類 HeaderUtil

實(shí)體類:把屬性封裝成一個(gè)對象,這樣調(diào)用方便一點(diǎn)。

package com.picture;

public class Picture {
	private String title;
	private String url;
	
	public Picture(String title, String url) {
		this.title = title;
		this.url = url;
	}
	public String getTitle() {
		return this.title;
	}
	public String getUrl() {
		return this.url;
	}
}

工具類:不斷變換 UA(我也不知道有沒有用,不過我是使用自己的ip,估計(jì)用處不大了)

package com.picture;

public class HeaderUtil {
	public static String[] headers = {
			"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36",
		    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36",
		    "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:30.0) Gecko/20100101 Firefox/30.0",
		    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) AppleWebKit/537.75.14 (KHTML, like Gecko) Version/7.0.3 Safari/537.75.14",
		    "Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; Win64; x64; Trident/6.0)",
		    "Mozilla/5.0 (Windows; U; Windows NT 5.1; it; rv:1.8.1.11) Gecko/20071127 Firefox/2.0.0.11",
		    "Opera/9.25 (Windows NT 5.1; U; en)",
		    "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
		    "Mozilla/5.0 (compatible; Konqueror/3.5; Linux) KHTML/3.5.5 (like Gecko) (Kubuntu)",
		    "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.8.0.12) Gecko/20070731 Ubuntu/dapper-security Firefox/1.5.0.12",
		    "Lynx/2.8.5rel.1 libwww-FM/2.14 SSL-MM/1.4.1 GNUTLS/1.2.9",
		    "Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.7 (KHTML, like Gecko) Ubuntu/11.04 Chromium/16.0.912.77 Chrome/16.0.912.77 Safari/535.7",
		    "Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:10.0) Gecko/20100101 Firefox/10.0 "
	};
}

下載類

多線程實(shí)在是太快了,再加上我只有一個(gè)ip,沒有代理ip可以用(我也不太了解),使用多線程被封ip是很快的。

package com.picture;

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
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.util.EntityUtils;

import com.m3u8.HttpClientUtil;

public class SinglePictureDownloader {
	private String referer;
	private CloseableHttpClient httpClient;
	private Picture picture;
	private String filePath;
	
	
	
	public SinglePictureDownloader(Picture picture, String referer, String filePath) {
		this.httpClient = HttpClientUtil.getHttpClient();
		this.picture = picture;
		this.referer = referer;
		this.filePath = filePath;
	}
	
	public void download() {
		HttpGet get = new HttpGet(picture.getUrl());
		Random rand = new Random();
		//設(shè)置請求頭
		get.setHeader("User-Agent", HeaderUtil.headers[rand.nextInt(HeaderUtil.headers.length)]);
		get.setHeader("referer", referer);
		System.out.println(referer);
		HttpEntity entity = null;
		try (CloseableHttpResponse response = httpClient.execute(get)) {
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode == 200) {
				entity = response.getEntity();
				if (entity != null) {
					File picFile = new File(filePath, picture.getTitle());
					try (OutputStream out = new BufferedOutputStream(new FileOutputStream(picFile))) {
						entity.writeTo(out);
						System.out.println("下載完畢:" + picFile.getAbsolutePath());
					}
				}
			}
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				//關(guān)閉實(shí)體,關(guān)于 httpClient 的關(guān)閉資源,有點(diǎn)不太了解。
				EntityUtils.consume(entity);
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

這是獲取 HttpClient 連接的工具類,避免頻繁創(chuàng)建連接的性能消耗。(但是因?yàn)槲疫@里是使用單線程來爬取,所以用處就不大了。我就是可以只使用一個(gè)HttpClient連接來爬取,這是因?yàn)槲覄傞_始是使用多線程來爬取的,但是基本獲取幾張圖片就被禁掉了,所以改成單線程爬蟲。所以這個(gè)連接池也就留下來了。)

package com.m3u8;

import org.apache.http.client.config.RequestConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

public class HttpClientUtil {
	private static final int TIME_OUT = 10 * 1000;
	private static PoolingHttpClientConnectionManager pcm;   //HttpClient 連接池管理類
	private static RequestConfig requestConfig;
	
	static {
		requestConfig = RequestConfig.custom()
				.setConnectionRequestTimeout(TIME_OUT)
				.setConnectTimeout(TIME_OUT)
				.setSocketTimeout(TIME_OUT).build();
		
		pcm = new PoolingHttpClientConnectionManager();
		pcm.setMaxTotal(50);
		pcm.setDefaultMaxPerRoute(10);  //這里可能用不到這個(gè)東西。
	}
	
	public static CloseableHttpClient getHttpClient() {
		return HttpClients.custom()
				.setConnectionManager(pcm)
				.setDefaultRequestConfig(requestConfig)
				.build();
	}
}

最重要的類:解析頁面類 PictureSpider

package com.picture;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
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.util.EntityUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.select.Elements;

import com.m3u8.HttpClientUtil;

/**
 * 首先從頂部分類標(biāo)題開始,依次爬取每一個(gè)標(biāo)題(小分頁),每一個(gè)標(biāo)題(大分頁。)
 * */
public class PictureSpider {

	private CloseableHttpClient httpClient;
	private String referer;
	private String rootPath;
	private String filePath;
	
	public PictureSpider() {
		httpClient = HttpClientUtil.getHttpClient();
	}
	
	/**
	 * 開始爬蟲爬??!
	 * 
	 * 從爬蟲隊(duì)列的第一條開始,依次爬取每一條url。
	 * 
	 * 分頁爬?。号?0頁
	 * 每個(gè)url屬于一個(gè)分類,每個(gè)分類一個(gè)文件夾
	 * */
	public void start(List<String> urlList) {
		urlList.stream().forEach(url->{
			this.referer = url;
			
			String dirName = url.substring(22, url.length()-1);  //根據(jù)標(biāo)題名字去創(chuàng)建目錄
			//創(chuàng)建分類目錄
			File path = new File("D:/DragonFile/DBC/mzt/", dirName); //硬編碼路徑,需要用戶自己指定一個(gè)
			if (!path.exists()) {
				path.mkdir();
				rootPath = path.toString();
			}
			
			for (int i = 1; i <= 10; i++) {  //分頁獲取圖片數(shù)據(jù),簡單獲取幾頁就行了
				this.page(url + "page/"+ 1);  
			}
		});
	}
	

	/**
	  * 標(biāo)題分頁獲取鏈接
	 * */
	public void page(String url) {
		System.out.println("url:" + url);
		String html = this.getHtml(url);   //獲取頁面數(shù)據(jù)
		Map<String, String> picMap = this.extractTitleUrl(html);  //抽取圖片的url
		
		if (picMap == null) {
			return ;
		}
		//獲取標(biāo)題對應(yīng)的圖片頁面數(shù)據(jù)
		this.getPictureHtml(picMap);
	}
	
	private String getHtml(String url) {
		String html = null;
		HttpGet get = new HttpGet(url);
		get.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36");
		get.setHeader("referer", url);
		try (CloseableHttpResponse response = httpClient.execute(get)) {
			int statusCode = response.getStatusLine().getStatusCode();
			if (statusCode == 200) {
				HttpEntity entity = response.getEntity();
				if (entity != null) {
					html = EntityUtils.toString(entity, "UTf-8");   //關(guān)閉實(shí)體?
				}
			}
			else {
				System.out.println(statusCode);
			}
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} 
		return html;
	}
	
	private Map<String, String> extractTitleUrl(String html) {
		if (html == null) {
			return null;
		}
		Document doc = Jsoup.parse(html, "UTF-8");
		Elements pictures = doc.select("ul#pins > li");
		
		//不知為何,無法直接獲取 a[0],我不太懂這方面的知識。
		//那我就多處理一步,這里先放下。
		Elements pictureA = pictures.stream()
				.map(pic->pic.getElementsByTag("a").first())
				.collect(Collectors.toCollection(Elements::new));
		
		return pictureA.stream().collect(Collectors.toMap(
				pic->pic.getElementsByTag("img").first().attr("alt"),
				pic->pic.attr("href")));
	}
	
	/**
	 * 進(jìn)入每一個(gè)標(biāo)題的鏈接,再次分頁獲取圖片的鏈接
	 * */
	private void getPictureHtml(Map<String, String> picMap) {
		//進(jìn)入標(biāo)題頁,在標(biāo)題頁中再次分頁下載。
		picMap.forEach((title, url)->{
			//分頁下載一個(gè)系列的圖片,每個(gè)系列一個(gè)文件夾。
			File dir = new File(rootPath, title.trim());
			if (!dir.exists()) {
				dir.mkdir();
				filePath = dir.toString();  //這個(gè) filePath 是每一個(gè)系列圖片的文件夾
			}
			
			for (int i = 1; i <= 60; i++) {
				String html = this.getHtml(url + "/" + i);
				if (html == null) {
					//每個(gè)系列的圖片一般沒有那么多,
					//如果返回的頁面數(shù)據(jù)為 null,那就退出這個(gè)系列的下載。
					return ; 
				}
				Picture picture = this.extractPictureUrl(html);
				System.out.println("開始下載");
				//多線程實(shí)在是太快了(快并不是好事,我改成單線程爬取吧)
				
				SinglePictureDownloader downloader = new SinglePictureDownloader(picture, referer, filePath);
				downloader.download();
				try {
					Thread.sleep(1500);   //不要爬的太快了,這里只是學(xué)習(xí)爬蟲的知識。不要擾亂別人的正常服務(wù)。
					System.out.println("爬取完一張圖片,休息1.5秒。");
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});
	}
	
	/**
	 * 獲取每一頁圖片的標(biāo)題和鏈接
	 * */
	private Picture extractPictureUrl(String html) {
		Document doc = Jsoup.parse(html, "UTF-8");
		//獲取標(biāo)題作為文件名
		String title = doc.getElementsByTag("h2")
				.first()
				.text();

		//獲取圖片的鏈接(img 標(biāo)簽的 src 屬性)
		String url = doc.getElementsByAttributeValue("class", "main-image")
				.first()
				.getElementsByTag("img")
				.attr("src");
		
		//獲取圖片的文件擴(kuò)展名
		title = title + url.substring(url.lastIndexOf("."));		
		return new Picture(title, url);
	}
}

啟動類 BootStrap

這里有一個(gè)爬蟲隊(duì)列,但是我最終連第一個(gè)都沒有爬取完,這是因?yàn)槲矣?jì)算失誤了,少算了兩個(gè)數(shù)量級。但是,程序的功能是正確的。

package com.picture;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * 爬蟲啟動類
 * */
public class BootStrap {
	public static void main(String[] args) {		
		//反爬措施:UA、refer 簡單繞過就行了。
		//refer   https://www.mzitu.com
		
		//使用數(shù)組做一個(gè)爬蟲隊(duì)列
		String[] urls = new String[] {
			"https://www.mzitu.com/xinggan/",     
			"https://www.mzitu.com/zipai/"   
		};
		
		// 添加初始隊(duì)列,啟動爬蟲
		List<String> urlList = new ArrayList<>(Arrays.asList(urls));
		PictureSpider spider = new PictureSpider();
		spider.start(urlList);
	}
}

爬取結(jié)果

在這里插入圖片描述

在這里插入圖片描述

注意事項(xiàng)

這里有一個(gè)計(jì)算失誤,代碼如下:

for (int i = 1; i <= 10; i++) {  //分頁獲取圖片數(shù)據(jù),簡單獲取幾頁就行了
	this.page(url + "page/"+ 1);  
}

這個(gè) i 的取值過大了,因?yàn)槲矣?jì)算的時(shí)候失誤了。如果按照這個(gè)情況下載的話,總共會下載:4 * 10 * (30-5) * 60 = 64800 張。(每一頁是含有30個(gè)標(biāo)題頁,大概5個(gè)是廣告。) 我一開始以為只有幾百張圖片! 這是一個(gè)估計(jì)值,但是真實(shí)的下載量和這個(gè)不會差太多的(沒有數(shù)量級的差距)。所以我下載了一會發(fā)現(xiàn)只下載了第一個(gè)隊(duì)列里面的圖片。當(dāng)然了,作為一個(gè)爬蟲學(xué)習(xí)的程序,它還是很合格的。

這個(gè)程序只是用來學(xué)習(xí)的,我設(shè)置每張圖片的下載間隔時(shí)間是1.5秒,而且是單線程的程序,所以速度上會顯得很慢。但是那樣也沒有關(guān)系,只要程序的功能正確就行了,應(yīng)該沒有人會真的等到圖片下載完吧。

那估計(jì)要好久了:64800*1.5s = 97200s = 27h,這也只是一個(gè)粗略的估計(jì)值,沒有考慮程序的其他運(yùn)行時(shí)間,不過其他時(shí)間可以基本忽略了。

總結(jié)

雖然說Java比較麻煩(相對于python,確實(shí)是不夠簡潔了)。但是我們學(xué)習(xí)的是技術(shù)呀,這點(diǎn)小困難還是能克服的,如果你只是想要下載圖片,那還是去看python的爬蟲吧。Java的HttpClient也就夠?qū)W一段時(shí)間了,發(fā)現(xiàn)這個(gè)東西真的是挺好用的,但是我也只是學(xué)習(xí)了一點(diǎn)點(diǎn)。這個(gè)類庫,不僅可以用來寫爬蟲(做爬蟲只是它功能比較強(qiáng)大而已),而且還能做很多有趣的事情。爬蟲,還是很有意思的,有很多有趣的地方,可以讓我們探索。但是,也要注意,本意是學(xué)習(xí)技術(shù),不能搞破壞。

到此這篇關(guān)于如何使用Java爬蟲批量爬取圖片的文章就介紹到這了,更多相關(guān)Java爬蟲批量爬取圖片內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 談?wù)凧ava中對象,類和this,super,static關(guān)鍵字的使用

    談?wù)凧ava中對象,類和this,super,static關(guān)鍵字的使用

    對象:對象是類的一個(gè)實(shí)例,有狀態(tài)和行為。類:類是一個(gè)模板,它描述一類對象的行為和狀態(tài)。本文就來和大家聊聊Java中對象,類和關(guān)鍵字的使用,需要的可以參考一下
    2022-08-08
  • 詳解SpringBoot中@ConditionalOnClass注解的使用

    詳解SpringBoot中@ConditionalOnClass注解的使用

    這篇文章主要和大家詳細(xì)介紹一下springboot中@ConditionalOnClass注解的用法,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下
    2022-08-08
  • JavaWeb 中Cookie實(shí)現(xiàn)記住密碼的功能示例

    JavaWeb 中Cookie實(shí)現(xiàn)記住密碼的功能示例

    cookie是一種WEB服務(wù)器通過瀏覽器在訪問者的硬盤上存儲信息的手段。Cookie的目的就是為用戶帶來方便,為網(wǎng)站帶來增值。這篇文章主要介紹了JavaWeb 中Cookie實(shí)現(xiàn)記住密碼的功能示例,需要的朋友可以參考下
    2017-06-06
  • Java中特殊運(yùn)算符及其應(yīng)用詳解

    Java中特殊運(yùn)算符及其應(yīng)用詳解

    當(dāng)涉及位操作和位級運(yùn)算時(shí),Java?提供了一組特殊的運(yùn)算符,即左移(<<)和右移(>>)運(yùn)算符,下面小編就帶大家深入了解一下它們的具體應(yīng)用吧
    2023-08-08
  • java實(shí)現(xiàn)上傳文件到服務(wù)器和客戶端

    java實(shí)現(xiàn)上傳文件到服務(wù)器和客戶端

    這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)上傳文件到服務(wù)器和客戶端,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • java獲取ip地址示例

    java獲取ip地址示例

    在JSP里,獲取客戶端的IP地址的方法是:request.getRemoteAddr(),這種方法在大部分情況下都是有效的。但是在通過了Apache,Squid等反向代理軟件就不能獲取到客戶端的真實(shí)IP地址了
    2014-04-04
  • 基于springboot設(shè)置Https請求過程解析

    基于springboot設(shè)置Https請求過程解析

    這篇文章主要介紹了基于springboot設(shè)置Https請求過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-08-08
  • Java線程之間的共享與協(xié)作詳解

    Java線程之間的共享與協(xié)作詳解

    這篇文章主要介紹了Java線程之間的共享與協(xié)作詳解,進(jìn)程是操作系統(tǒng)進(jìn)行資源分配的最小單位,線程是進(jìn)程的一個(gè)實(shí)體,是CPU調(diào)度和分派的基本單位,它是比經(jīng)常更小的、能夠獨(dú)立運(yùn)行的基本單位
    2022-07-07
  • Hibernate緩存機(jī)制實(shí)例代碼解析

    Hibernate緩存機(jī)制實(shí)例代碼解析

    這篇文章主要介紹了Hibernate緩存機(jī)制實(shí)例代碼解析,介紹了查詢緩存,一級二級緩存等內(nèi)容,分享了相關(guān)代碼示例,小編覺得還是挺不錯(cuò)的,具有一定借鑒價(jià)值,需要的朋友可以參考下
    2018-02-02
  • java從命令行獲取數(shù)據(jù)的三種方式代碼實(shí)例

    java從命令行獲取數(shù)據(jù)的三種方式代碼實(shí)例

    這篇文章主要介紹了java從命令行獲取數(shù)據(jù)的三種方式代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-12-12

最新評論