中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎么使用Java IO流和網絡制作一個簡單的圖片爬蟲

發布時間:2023-04-07 17:16:47 來源:億速云 閱讀:114 作者:iii 欄目:開發技術

這篇文章主要介紹“怎么使用Java IO流和網絡制作一個簡單的圖片爬蟲”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“怎么使用Java IO流和網絡制作一個簡單的圖片爬蟲”文章能幫助大家解決問題。

    Java IO流和網絡的簡單應用

    最近看到了 URL 類的用法,簡單的做了一個Java 版的爬蟲。發現還挺有趣的,就拿出來分享一下。通過關鍵字爬取百度圖片,這個和我們使用搜索引擎搜索百度圖片是一樣的,只是通過爬蟲可以學習技術的使用。(這個程序只是用來學習使用的,沒有其它用途!)

    怎么使用Java IO流和網絡制作一個簡單的圖片爬蟲

    怎么使用Java IO流和網絡制作一個簡單的圖片爬蟲

    Java IO 流和 URL 類

    Java IO流

    Java 的 IO 流是實現輸入/輸出的基礎,它可以方便的實現數據的輸入/輸出操作,在 Java 中把不同的輸入/輸出源(鍵盤、文件、網絡連接等)抽象表述為”流“(Stream),通過流的方法運行Java 程序使用相同的方式來訪問不同的輸入/輸出源。

    因為 IO流 已經對各種輸入輸出源做了一個抽象處理,所以我們可以使用相對一致的代碼處理各種的源,只需要把它們作為輸入輸出流來進行處理就行了,這就是面向抽象的好處。

    URL 類

    URI 和 URL

    先來了解一下什么是 URL 吧,說 URL 之前先簡單了解URI
    **URI,統一資源標識符(Uniform Resource Identifier)**是采用一種特定語法標識一個資源的字符串。所標識的資源可能是服務器上的一個文件或者其它任何內容。URI 的語法是由一個模式和一個模式特定部分組成,模式和模式特定部分用一個冒號分隔,如下所示:

    模式:模式特定部分

    URI 中的模式特定部分沒有特定的語法,很多都采用一種層次結構形式,如:
    //authority/path?query

    **URL,統一資源定位符(Uniform Resource Location)**是URI的一個子集,它除了標識一個資源外 ,還會為資源提供一個特定的網絡位置,客戶端可以用它來獲取這個資源的一個表示。
    注意:URL和URI并不是完全相同的,通用的URI可以告訴你一個資源是什么,但是無法告訴你它在哪里,以及如何得到這個資源。
    在Java中,這二者都有相應的實現,java.net.URI 類(只標識資源)與 java.net.URL 類(既能標識資源,又能獲取資源)

    URL 中的網絡位置通常包括用來訪問服務器的協議(FTP、HTTP等)、服務器的主機名或IP地址,以及文件在該服務器上的路徑。典型的 URL 類似于 https://www.baidu.com/。它表示百度服務器上的一個 html 文件(百度搜索的首頁),它可以通過 HTTP 協議訪問雖然沒有直接在 URL 后面加上 html 文件的名字。如果使用 tomcat 的話,通常是 http://127.0.0.1:8080/foods/index.html 這種形式,其實二者是相同的。

    好了,簡單的了解就到此為止了,感興趣的話,可以查閱相關書籍了解更詳細的知識,上面只是提到一些基礎的概念。

    URL類

    java.net.URL類是對統一資源定位符的抽象表示。它不依賴于繼承來配置不同類型的URL的實例,而使用了策略設計模式。協議處理器就是策略,URL 類構成上下文,通過它來選擇不同的策略。(值得一提的是:
    java 的 IO流也是使用了一種設計模式:裝飾器模式。

    例如如下代碼:

    DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(new File())))。

    URL 類包含很多的構造方法,我也只是第一次使用,就使用了最簡單的一種形式:(剛開始學習,根本不需要了解這么多,先用著再說,慢慢掌握知識。)

    public URL(String url) throws MalformedURLException

    Java 爬蟲

    Talk is cheap, show me the code!
    前面主要是一下簡單的基礎知識,如果已經了解可以直接看下面這部分。

    項目的基本結構:

    怎么使用Java IO流和網絡制作一個簡單的圖片爬蟲

    Client
    package dragon;
    
    import java.io.File;
    import java.io.IOException;
    
    public class Client {
    	public static final String downloadFilePath = "D:\\DragonDataFile\\cat";
    	public static void main(String[] args) throws IOException {
    		//初始化創建文件下載目錄
    		File dir = new File(Client.downloadFilePath);
    		if (!dir.exists()) {
    			dir.mkdirs();
    		}
    		//啟動下載窗口
    		new Window("龍");
    	}
    }
    DataProcessUtil
    package dragon;
    
    import java.io.BufferedInputStream;
    import java.io.IOException;
    import java.net.URL;
    import java.net.URLConnection;
    import java.util.LinkedList;
    import java.util.List;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    import java.util.stream.Collectors;
    
    public class DataProcessUtil {
    	
    	//根據鏈接獲取 html 文件數據。
    	public static String getData(String link) throws IOException {
    		URL url = new URL(link);
    		URLConnection connection = url.openConnection();
    		StringBuilder strBuilder = new StringBuilder();
    		try (
    			BufferedInputStream bis = new BufferedInputStream(connection.getInputStream())){
    			int hasRead = 0;
    			byte[] b = new byte[1024];
    			while ((hasRead = bis.read(b)) != -1) {
    				strBuilder.append(new String(b, 0, hasRead));
    			}
    		}
    		return strBuilder.toString();
    	}
    	
    	public static List<String> getLinkList(String str){
    		String regx = "\"objURL\":\"(.*?)\",";
    		Pattern p = Pattern.compile(regx);
    		Matcher m = p.matcher(str);
    		List<String> strs = new LinkedList<>();
    		while (m.find()) {
    			strs.add(m.group(0));
    		}
    		//使用 Stream API 進行處理并返回。
    		 return strs.stream()
    				.map(s->s.substring(10, s.length()-2))
    				.collect(Collectors.toList());
    	}
    }

    說明:
    獲取html頁面的信息,并進行處理,使用正則表達式從html中提取圖片的鏈接。
    (正則表達式是參考其它人的實現,這個涉及到對html內容的分析)

    String regx = "\"objURL\":\"(.*?)\",";
    //使用 Stream API 進行處理并返回。
    		 return strs.stream()
    				.map(s->s.substring(10, s.length()-2))
    				.collect(Collectors.toList());

    使用Java 8新增加的 Stream 對數據進行遍歷,提取所有的圖片的 URL 組成一個列表集合返回。

    DownLoadUtil
    package dragon;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.net.URL;
    import java.util.Date;
    import java.util.List;
    import java.util.Random;
    
    public class DownLoadUtil {
    	public static void downLoad(List<String> strs) {
    		strs.stream().forEach(u->{
    			try {
    				URL url = new URL(u);
    				String contentType  = url.openConnection().getContentType();
    				if (contentType != null && contentType.contains("image/")) {
    					//獲取圖片的類型:content type
    					String filetype = null;
    					if (contentType.contains("jpeg")) {
    						filetype = ".jpeg";
    					} else if (contentType.contains("jpg")) {
    						filetype = ".jpg";
    					} else{
    						filetype = ".png";
    					} //gif 格式圖片,似乎無法正常顯示		
    					
    					//使用當前日期的毫秒數+隨機數+contentType 作為文件名
    					Random rand = new Random(System.currentTimeMillis());
    					String filename = new Date().getTime()+rand.nextInt(10000)+filetype;		
    					Runnable r = ()->{
    						int flag = 0;
    						File imageFile = new File(Client.downloadFilePath, filename);
    						try(
    							BufferedInputStream bis = new BufferedInputStream(url.openConnection().getInputStream());
    							BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(imageFile))){
    							int hasRead = 0;
    							byte[] b = new byte[1024];
    							while ((hasRead = bis.read(b)) != -1) {
    								bos.write(b, 0, hasRead);
    							}
    						} catch (IOException e) {
    							System.out.println("下載失敗!");
    							//對于下載失敗的圖片進行刪除,不然會出現錯誤!圖片只能正常現實一部分
    							if (imageFile.exists()) {
    								boolean b = imageFile.delete();
    								System.out.println("下載失敗,刪除圖片"+b);
    							}
    							flag = 1;
    							e.printStackTrace();
    						}
    						if (flag == 0)
    							System.out.println("下載完成:"+filename);
    					};
    					Thread t = new Thread(r);
    					t.start();   //啟動下載線程。
    				}
    			} catch (IOException e) {
    				e.printStackTrace();
    				System.out.println("鏈接錯誤!");
    			}
    		});
    	}
    }

    注意:這里遇到一個問題,就是圖片的下載過程受到網絡因素的影響,有時候會下載失敗,但是如果圖片已經開始下載,仍然提示下載失敗,那么這張圖片可以能會出現異常,比如出現一下奇怪的顏色,我對下載失敗的圖片,進行了處理,發現,似乎沒有效果。
    單純的判斷大小無法解決圖片變形的問題,還有一種情況需要考慮!在最下面,會有詳細說明解決方法。

    Window
    package dragon;
    
    import java.awt.FlowLayout;
    import java.io.IOException;
    import java.util.List;
    import javax.swing.Box;
    import javax.swing.JButton;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    import javax.swing.JOptionPane;
    import javax.swing.JTextField;
    
    public class Window extends JFrame{
    	/**
    	 * 自動生成的序列化版本號
    	 */
    	private static final long serialVersionUID = 7809323808831342296L;
    	private JLabel label_keyWord, label_Page;
    	private JTextField textField, textPage;
    	private JButton download;
    	
    	public Window(String name) {
    		super(name);
    		this.init();
    		//設置布局
    		this.setLayout(new FlowLayout());
    		this.setBounds(400, 400, 250, 150);
    		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    		this.setVisible(true);
    	}
    	
    	private void init() {
    		label_keyWord = new JLabel("關鍵字");
    		label_Page = new JLabel("頁數");
    		textField = new JTextField(10);
    		textPage = new JTextField(10);
    		download = new JButton("下載");
    		
    		download.addActionListener(e->{
    			String keyWord = textField.getText().trim();
    			String page = textPage.getText().trim();
    			int download_page = 0;   
    			if (keyWord.length() == 0 || page.length() == 0) {
    				JOptionPane.showMessageDialog(null, "關鍵字或頁數不能為空!", "警告", JOptionPane.WARNING_MESSAGE);
    				return ;
    			}
    			
    			try {
    				download_page = Integer.parseInt(page);  //匹配單個數字,如果數字很多使用正則表達式
    			} catch (NumberFormatException exp) {
    				JOptionPane.showMessageDialog(null, "頁數必須為數字!", "警告", JOptionPane.WARNING_MESSAGE);
    				return ;
    			}
    			
    			String link = null;
    			for (int i = 1; i <= download_page; i++) {
    				//分頁下載圖片!
    				link = "http://image.baidu.com/search/flip?tn=baiduimage&ie=utf-8&word="+keyWord+"&pn="+i*20;
    				this.download(link);
    			}
    		});
    
    		Box boxH1 = Box.createHorizontalBox();
    		boxH1.add(label_keyWord);
    		boxH1.add(Box.createHorizontalStrut(10));
    		boxH1.add(textField);
    		Box boxH2 = Box.createHorizontalBox();
    		boxH2.add(label_Page);
    		boxH2.add(Box.createHorizontalStrut(23));
    		boxH2.add(textPage);
    		Box boxH3 = Box.createHorizontalBox();
    		boxH3.add(download);
    		
    		Box boxV = Box.createVerticalBox();
    		boxV.add(boxH1);
    		boxV.add(Box.createVerticalStrut(10));
    		boxV.add(boxH2);
    		boxV.add(Box.createVerticalStrut(10));
    		boxV.add(boxH3);
    		this.add(boxV);
    	}
    	
    	private void download(String link) {
    		try {
    			String str = DataProcessUtil.getData(link);
    			List<String> links = DataProcessUtil.getLinkList(str);
    			//嘗試下載!使用線程進行下載,防止阻塞!
    			Thread t = new Thread(()->{
    				DownLoadUtil.downLoad(links);
    			});
    			t.start();
    		} catch (IOException e1) {
    			e1.printStackTrace();
    			JOptionPane.showMessageDialog(null, "啥都沒有!", "警告", JOptionPane.WARNING_MESSAGE);
    		}
    	}
    }

    說明:
    當圖片沒有下載完成時,不要再次點擊下載按鈕,否則會報錯。因為線程不能被再次啟動。

    運行結果

    怎么使用Java IO流和網絡制作一個簡單的圖片爬蟲

    基本原理

    我來簡單畫一個示意圖,大家湊合著看:

    怎么使用Java IO流和網絡制作一個簡單的圖片爬蟲

    說明:首先通過百度圖片的URL來獲取百度圖片那個頁面的信息(html的內容),我們平時在瀏覽器使用,看到的都是瀏覽器處理好的頁面,如果使用爬蟲爬取的就是原始的html頁面,在瀏覽器按 F12 也可以看到。因為圖片的鏈接都在html 中,所以我們需要取出這些圖片,這里就用到了**正則表達式(Regular Expression)**的知識了,通過正則表達式可以取出需要的信息(資源的URL或者說資源的地址)。其實獲取html的過程和獲取圖片的過程,都是一樣的。

    這里說一下,這個步驟:

    	//根據鏈接獲取 html 文件數據。
    	public static String getData(String link) throws IOException {
    		URL url = new URL(link);
    		URLConnection connection = url.openConnection();
    		StringBuilder strBuilder = new StringBuilder();
    		try (
    			BufferedInputStream bis = new BufferedInputStream(connection.getInputStream())){
    			int hasRead = 0;
    			byte[] b = new byte[1024];
    			while ((hasRead = bis.read(b)) != -1) {
    				strBuilder.append(new String(b, 0, hasRead));
    			}
    		}
    		return strBuilder.toString();
    	}

    通過參數 link,創建一個 URL 對象,然后通過使用URLConnection connection = url.openConnection();獲取 URLConnection 對象,在通過 URLConnection 對象的getInputStream() 方法,獲取輸入流即可。這樣就完成了對資源的獲取。我這里強調資源,因為下載圖片其實和這個過程是一樣的。

    總結

    這個小軟件雖然功能很簡單,但是也用到了很多知識點,比較適合初學者進行學習(Java IO流、網絡、Stream、線程的知識),知識雖然用到的都不難(一些基礎知識),但是融合起來使用,還是很有意思的。

    對于圖片的奇怪顏色問題,可以確定是圖片的大小和原來圖片的大小不一致導致的,至于為什么是這樣的,估計需要具備一定的圖形學知識,才能解答,這個超出了這個東西的范圍了。所以為了判斷哪些圖片出錯,我就使用大小判斷的方法,對最后生成的文件大小和網絡圖片文件大小進行比對,刪除了一些無法下載的圖片,但是有一些圖片居然無法刪除,我查閱了資料,大多說它被另一個進程占用,但是我這個圖片應該是沒有的。后來,經過檢查發現是多線程惹得禍,因為是多線程,并且代碼執行速度太快了(對的,和這個也有關系),因為我的文件命名是當前時間的毫秒數+一個種子為當前時間的隨機數,在多線程的情況下,重復的概率居然還挺高的。
    所以,原因就出現了,當發現圖片大小不對,試圖刪除圖片時,圖片被另一個線程占用,無法刪除。(關于名字重復的問題,就是兩個線程在同一個毫秒啟動了,所以隨機數也是相等的(種子相等),因此有些圖片就會和其它圖片寫入同一個圖片文件,導致出現異常情況。)

    總結一下:
    圖片異常的情況有兩種:
    1.網絡原因,導致圖片無法完整下載,這是無法解決的,只能刪除。
    2.圖片名字重復,導致多張圖片數據被寫入同一張圖片當中,這是程序錯誤,可以避免的。

    解決方法:
    對于第一種情況,只需要把錯誤的圖片刪除即可;
    對于第二種情況,要避免圖片名字重復,所以我重新設計了圖片的命名方法,
    采用:當前時間的毫秒數+UUID隨機數(查閱資料,這個挺好用的)作為文件的命名方式。注:UUID 也有一個缺點,就是名字太長了。

    修改后的源文件:

    package dragon;
    
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.net.URL;
    import java.net.URLConnection;
    import java.util.List;
    import java.util.UUID;
    
    public class DownLoadUtil {
    	public static void downLoad(List<String> strs) {
    		strs.stream().forEach(u->{
    			try {
    				URL url = new URL(u);
    				URLConnection urlConnection = url.openConnection();
    				String contentType  = urlConnection.getContentType();
    				//獲取資源文件的大小
    				long size = urlConnection.getContentLengthLong();
    				if (contentType != null && contentType.contains("image/")) {
    					//獲取圖片的類型:content type
    					String filetype = null;
    					if (contentType.contains("jpeg")) {
    						filetype = ".jpeg";
    					} else if (contentType.contains("jpg")) {
    						filetype = ".jpg";
    					} else{
    						filetype = ".png";
    					} //gif 格式圖片,似乎無法正常顯示		
    					
    					//使用當前時間戳+隨機數+contentType 作為文件名
    					String filename = System.currentTimeMillis()+UUID.randomUUID().toString()+filetype;
    					//使用線程進行下載
    					Runnable r = ()->{
    						File imageFile = new File(Client.downloadFilePath, filename);
    						try(
    							BufferedInputStream bis = new BufferedInputStream(urlConnection.getInputStream());
    							BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(imageFile))){
    							int hasRead = 0;
    							byte[] b = new byte[1024];
    							while ((hasRead = bis.read(b)) != -1) {
    								bos.write(b, 0, hasRead);
    							}
    						} catch (IOException e) {
    							System.out.println("下載失敗!");
    							e.printStackTrace();
    						}
    						//對下載失敗的圖片進行刪除。
    						if (imageFile.length() != size) {
    							boolean result = imageFile.delete();
    							System.out.println(imageFile.length()+"  "+size+" "+filename+" 刪除結果:"+result);
    							//大小不符合,說明圖片下載有問題,刪除圖片。
    						} else {
    							System.out.println("下載完成:"+filename);
    						}
    					};
    					Thread t = new Thread(r);
    					t.start();   //啟動下載線程。
    				}
    			} catch (IOException e) {
    				e.printStackTrace();
    				System.out.println("鏈接錯誤!");
    			}
    		});
    	}
    }

    運行截圖
    這樣網絡原因錯誤的圖片直接刪除,代碼原因的錯誤,已經改正了。

    怎么使用Java IO流和網絡制作一個簡單的圖片爬蟲

    注:還有一些圖片無法顯示,這個可能是官方不允許我們進行爬取,有的圖片,爬取的就是不允許爬取那種圖片,還有一些圖片,不支持格式。

    關于“怎么使用Java IO流和網絡制作一個簡單的圖片爬蟲”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    蒲城县| 沅江市| 合山市| 五莲县| 泗洪县| 化德县| 渝中区| 汤阴县| 子洲县| 铁力市| 信阳市| 新平| 松江区| 大埔县| 乳源| 天祝| 蓬溪县| 博乐市| 察隅县| 大化| 雷州市| 织金县| 花垣县| 施秉县| 宜州市| 泉州市| 金湖县| 班玛县| 武平县| 高尔夫| 荣成市| 安平县| 灵山县| 潜山县| 蒙山县| 二手房| 泸溪县| 吉隆县| 稷山县| 屏南县| 崇仁县|