《網頁爬蟲》

ξ你且笑對℡發表於2018-11-26

1.初始版本

package com.zyjl.crawler;

import java.io.IOException;

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

/**
 * 爬取網路資源的類(網路爬蟲)
 * 一級(通過httpclient和jsoup爬取網頁,分析所有url)
 * @author Administrator
 *
 */
public class StartCrawler01 {
	
	/**
	 * 解析URL(通過網址URL,利用httpclient技術,獲取當前URL對應的網路內容)
	 * @param url
	 */
	public static void parseUrl(String url,String realDir) {
		CloseableHttpClient HttpClient = HttpClients.createDefault(); //獲取httpclient的例項
		HttpGet httpGet = new HttpGet(url); //設定提交方式:get
		CloseableHttpResponse response = null;
		try {
			 response = HttpClient.execute(httpGet); //執行
			 HttpEntity entity = response.getEntity(); //獲取內容
//			 System.out.println(entity.getContentType().toString()); //Content-Type: text/html
//			 System.out.println(entity.getContentType().getName()); //Content-Type
//			 System.out.println(entity.getContentType().getValue()); //text/html
			 //如果是text/html型別的URL,需要再次解析
			 if("text/html".equals(entity.getContentType().getValue())) {
				 String pageContent = EntityUtils.toString(entity,"utf-8"); //獲取網頁內容
//				 System.out.println("網頁內容:"+pageContent);
				 parsePageContent(pageContent, realDir); //通過網頁爬蟲框架jsoup解析網頁內容的方法
			 }
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			//關閉response資源
			if(response != null) {
				response.close();
			}
			//關閉HttpClient資源
			if(HttpClient != null) {
				HttpClient.close();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	/**
	 * 通過網頁爬蟲框架jsoup對網頁內容進行解析
	 * @param pageContent
	 */
	public static void parsePageContent(String pageContent, String realDir) {
		Document doc = Jsoup.parse(pageContent); //獲取jsp的dom樹
		Elements aEles = doc.select("a"); //通過選擇器獲取dom樹的所有a標籤
		for (Element aEle : aEles) {
			String aUrl = aEle.attr("href"); //獲取a標籤的href
			System.out.println("URL:"+realDir+aUrl);
//			Element after = aEle.after("href");
//			System.out.println("aHref:"+after);
		}
	}

	/**
	 * 程式入口
	 * @param args
	 */
	public static void main(String[] args) {
		String url="http://central.maven.org/maven2/HTTPClient/HTTPClient/";
		parseUrl(url,url);
	}

}

2,第一步(過濾)優化

package com.zyjl.crawler;

import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;

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

/**
 * 爬取網路資源的類(網路爬蟲)
 * 二級(過濾無效url,迭代解析)
 * @author Administrator
 *
 */
public class StartCrawler02 {
	//堆列:先進先出(堆疊:先進後出)
	public static String[] excludeUrls = new String[] { ".pom", ".xml", ".md5", ".sha1", ".asc", ".gz", ".zip", "../" }; // 要過濾的url字尾
	public static Queue<String> waitForCrawlerUrls = new LinkedList<String>();// 等待再次爬取的Url
	public static long total = 0; //計算第n條被爬取的連結
	
	/**
	 * 解析URL(通過網址URL,利用httpclient技術,獲取當前URL對應的網路內容)
	 * @param url
	 */
	public static void parseUrl() {
		while(waitForCrawlerUrls.size()>0) {
			
			String URL = waitForCrawlerUrls.poll(); //摘取佇列的第一個元素,並且移除
		
		CloseableHttpClient HttpClient = HttpClients.createDefault(); //獲取httpclient的例項
		HttpGet httpGet = new HttpGet(URL); //設定提交方式:get
		CloseableHttpResponse response = null;
		try {
			 response = HttpClient.execute(httpGet); //執行
			 HttpEntity entity = response.getEntity(); //獲取內容
			 //如果是text/html型別的URL,需要再次解析
			 if("text/html".equals(entity.getContentType().getValue())) {
				 String pageContent = EntityUtils.toString(entity,"utf-8"); //獲取網頁內容
				 //System.out.println("網頁內容:"+pageContent);
				 parsePageContent(pageContent, URL); //通過網頁爬蟲框架jsoup解析網頁內容的方法
			 }
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		try {
			//關閉response資源
			if(response != null) {
				response.close();
			}
			//關閉HttpClient資源
			if(HttpClient != null) {
				HttpClient.close();
			}
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
	  }
		
	}
	
	/**
	 * 通過網頁爬蟲框架jsoup對網頁內容進行解析
	 * @param pageContent
	 */
	public static void parsePageContent(String pageContent, String realDir) {
		Document doc = Jsoup.parse(pageContent); //獲取jsp的dom樹
		Elements aEles = doc.select("a"); //通過選擇器獲取dom樹的所有a標籤
		long i = 0; //計算讀取所有連結的第幾條
		for (Element aEle : aEles) {
			String aHref = aEle.attr("href"); //獲取a標籤的href
			String URL = realDir+aHref; //所有連結
			
			if(i == 0) {
				System.out.println("\r"+"開始爬取新的網頁..."+"\r");
			}
			
			System.out.println("爬取到第"+(++i)+"條連結:"+URL);
			/**
			 * 1:目標連結
			 * 2:需要過濾掉的連結
			 * 3:需要迭代解析的連結
			 */
			if(URL == null || URL.equals("")) return; //如果URL為空,就return掉
			boolean f = true; //預設就是符合要求的目標連結
			for (String excludeUrl : excludeUrls) { //遍歷迴圈需要過濾掉的連結字尾
				if(URL.endsWith(excludeUrl)) { //如果與連結字尾匹配,就返回false(過濾掉)
					f = false;
					break; //停止
				}
			}
			if(f && URL.endsWith(".jar")) { //是否需要迭代解析的連結
				System.err.println("\r"+"爬取的第"+(++total)+"條目標連結,URL:"+URL+"\r");
			}else { //需要再次迭代爬取的連結
				addUrl(URL);
			}
		}
	}
	
	/**
	 * 新增到爬蟲佇列,等待再次爬取
	 * @param URL
	 */
	private static void addUrl(String URL) {
		System.out.println("連結:"+URL+"被新增到了爬蟲 佇列");
		waitForCrawlerUrls.add(URL);
	}
	
	/**
	 * 初始化方法
	 */
	private static void init() {
		String URL="http://central.maven.org/maven2/HTTPClient/HTTPClient/";
		addUrl(URL);
		addUrl("http://central.maven.org/maven2/commons-cli/commons-cli/");
		parseUrl();
	}

	/**
	 * 程式入口
	 * @param args
	 */
	public static void main(String[] args) {
		init();
	}

}

3.效能優化

package com.zyjl.crawler;

import java.io.IOException;
import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;

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

/**
 * 爬取網路資源的類(網路爬蟲)
 * 三級(利用非同步執行緒池對爬取的效能進行優化)
 * @author Administrator
 *
 */
public class StartCrawler03 {

	    //堆列:先進先出(堆疊:先進後出)
		public static String[] excludeUrls = new String[] { ".pom", ".xml", ".md5", ".sha1", ".asc", ".gz", ".zip", "../" }; // 要過濾的url字尾
		public static Queue<String> waitForCrawlerUrls = new LinkedList<String>();// 等待再次爬取的Url
		public static long total = 0; //計算第n條被爬取的連結
		public static boolean exeFlag = true; //預設解析爬蟲佇列中的網址URL
		
		/**
		 * 解析URL(通過網址URL,利用httpclient技術,獲取當前URL對應的網路內容)
		 */
		public static void parseUrl() {
			ExecutorService service = Executors.newFixedThreadPool(10); //建立執行緒池(存放了10個非同步執行緒)
			while(exeFlag) { //正在執行爬蟲列隊
				if(waitForCrawlerUrls.size()>0) { //如果有等待再次爬取的Url,就執行以下邏輯
					//使用執行緒池中的非同步執行緒執行解析邏輯
					service.execute(new Runnable() {
						public void run() {
							// TODO Auto-generated method stub
							while(waitForCrawlerUrls.size()>0) { //如果堆列waitForCrawlerUrls中有等待再次爬取的Url
								String URL = waitForCrawlerUrls.poll(); //摘取佇列的第一個元素,並且移除
								CloseableHttpClient HttpClient = HttpClients.createDefault(); //獲取httpclient的例項
								HttpGet httpGet = new HttpGet(URL); //設定提交方式:get
								//設定配置(連線時長:8秒,等待伺服器響應資料時長:10秒)
								RequestConfig config = RequestConfig.custom().setConnectTimeout(8000).setSocketTimeout(10000).build();
								httpGet.setConfig(config);
								CloseableHttpResponse response = null;
								try {
									 response = HttpClient.execute(httpGet); //執行
									 if(response != null) {
										 HttpEntity entity = response.getEntity(); //獲取內容
										 //如果是text/html型別的URL,需要再次解析
										 if("text/html".equals(entity.getContentType().getValue())) {
											 String pageContent = EntityUtils.toString(entity,"utf-8"); //獲取網頁內容
											 //System.out.println("網頁內容:"+pageContent);
											 parsePageContent(pageContent, URL); //通過網頁爬蟲框架jsoup解析網頁內容的方法
										 }
									 }else {
										 System.err.println("連結時間過長!!!");
										 addUrl(URL); //將連結URL重新加到爬蟲佇列
									 }
								} catch (ClientProtocolException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								} catch (IOException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
								try {
									//關閉response資源
									if(response != null) {
										response.close();
									}
									//關閉HttpClient資源
									if(HttpClient != null) {
										HttpClient.close();
									}
								} catch (IOException e) {
									// TODO Auto-generated catch block
									e.printStackTrace();
								}
								
							}
							
						}
					});
					
				}else { //如果沒有等待再次爬取的Url
					//獲取正在執行爬蟲任務的執行緒數
                    if(((ThreadPoolExecutor)service).getActiveCount() == 0) { //如果執行緒數為0,就關閉
                    	exeFlag = false;
						break;
					}
				}
			}
			
			try {
				Thread.sleep(1500);//休眠1.5s是為了,給執行緒解析網頁網頁的時間,不然容易出現問題
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		  
		}
		
		
		/**
		 * 通過網頁爬蟲框架jsoup對網頁內容進行解析
		 * 
		 * 1:獲取所有目標連結
		 * 2:過濾掉不符合要求的連結
		 * 3:拿到需要迭代解析符合要求的目標連結
		 * @param pageContent
		 */
		public static void parsePageContent(String pageContent, String realDir) {
			// * 1:獲取所有目標連結
			Document doc = Jsoup.parse(pageContent); //獲取jsp的dom樹
			Elements aEles = doc.select("a"); //通過選擇器獲取dom樹的所有a標籤
			long i = 0; //計算讀取當前網頁所有連結的第幾條
			for (Element aEle : aEles) {
				String aHref = aEle.attr("href"); //獲取a標籤的href
				String URL = realDir+aHref; //所有連結
				
				if(i == 0) {
					System.out.println("\r"+"開始爬取新的網頁..."+"\r");
				}
				System.out.println("讀取到第"+(++i)+"條連結:"+URL);
				
				// * 2:過濾掉不符合要求的連結
				if(URL == null || URL.equals("")) return; //如果URL為空,就return掉
				boolean f = true; //預設就是符合要求的目標連結
				for (String excludeUrl : excludeUrls) { //遍歷迴圈需要過濾掉的連結字尾(過濾所有不符合要求的連結)
					if(URL.endsWith(excludeUrl)) { //如果與連結字尾匹配,就返回false(過濾掉)
						f = false;
						break; //停止
					}
				}
				 //* 3:拿到需要迭代解析符合要求的目標連結
				if(f && URL.endsWith(".jar")) { //如果是符合要求需要迭代解析的連結
					System.err.println("\r"+"爬取到第"+(++total)+"條目標連結,URL:"+URL+"\r");
				}else { //如果不是,新增到爬蟲列隊,再次迭代爬取的連結
					addUrl(URL);
				}
			}
		}
		
		
		/**
		 * 新增到爬蟲佇列,等待再次爬取
		 * @param URL
		 */
		private static void addUrl(String URL) {
			waitForCrawlerUrls.add(URL);
			System.out.println("連結:"+URL+"被新增到了爬蟲 佇列");
		}
		
		
		/**
		 * 初始化方法
		 */
		private static void init() {
			String URL="http://central.maven.org/maven2/HTTPClient/HTTPClient/";
			addUrl(URL);
			addUrl("http://central.maven.org/maven2/commons-cli/commons-cli/");
			parseUrl();
		}

		
		/**
		 * 程式入口
		 * @param args
		 */
		public static void main(String[] args) {
			init();
		}
	
}

相關文章