1. 程式人生 > >crawler學習篇(jar爬取)

crawler學習篇(jar爬取)

新建maven專案

匯入依賴的jar包

<dependencies>
		<!-- 新增Httpclient支援 -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.2</version>
		</dependency>

		<!-- 新增jsoup支援 -->
		<dependency>
			<groupId>org.jsoup</groupId>
			<artifactId>jsoup</artifactId>
			<version>1.10.1</version>
		</dependency>

		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.16</version>
		</dependency>

	</dependencies>

 

需要爬取的網址 :http://central.maven.org/

/**
 * 爬取網路資源的類
 * @author Admin
 *
 *1、通過httpclient和jsoup爬取網頁,分析所有url
 *2、過濾無效url,迭代解析
 *3、利用非同步執行緒池對爬取的效能進行優化
 *
 */
public class StartCrawler {
	
	// 要過濾掉的url字尾
	public static String[] excludeUrls = new String[] { ".pom", ".xml", ".md5", ".sha1", ".asc", ".gz", ".zip", "../" };
	//佇列   先進先出
	public static Queue<String> waitForCrawlerUrls = new LinkedList<String>();// 等待再次爬取的Url
	public static long total = 0;//計數
	public static boolean exeFlag = true;//預設解析爬取佇列裡面的網址
	

	/**
	 * 通過網址url,利用httpclient技術,獲得當前url對應的網路內容
	 * @param url
	 */
//	public static void parseUrl(String url,String realDir) {
	public static void parseUrl() {
//		利用非同步執行緒池   對爬取的效能進行優化
		//存放10個非同步執行緒的執行緒池
		ExecutorService executorService = Executors.newFixedThreadPool(10);//例項化一個執行緒池 池中放10個執行緒
		while(exeFlag) {//需要滿足的條件
			if(waitForCrawlerUrls.size() > 0) {//佇列裡有連結
				executorService.execute(new Runnable() {
					//使用執行緒池中的非同步執行解析邏輯
					public void run() {
						
						while(waitForCrawlerUrls.size() > 0) {
							String url = waitForCrawlerUrls.poll();//(隊列當中的取值)摘取佇列的第一個元素,並且移除
							
							//CloseableHttpClient可以被用於從客戶端傳送HTTP請求到服務端
							CloseableHttpClient httpClient = HttpClients.createDefault();//獲取httpclient的一個例項
							HttpGet httpGet = new HttpGet(url);//獲得是什麼請求
							//設定連線時長5秒和等待伺服器響應資料時長8秒
							RequestConfig config = RequestConfig.custom().setConnectTimeout(5000).setSocketTimeout(8000).build();
							httpGet.setConfig(config);
							CloseableHttpResponse response = null;
							
							//執行
							try {
								//要抓異常是因為避免訪問超時
								response = httpClient.execute(httpGet);
								if(response != null) {
									HttpEntity entity = response.getEntity();//獲取內容
									//凡是text/html(網頁)這一型別的需要進行再次解析
									if("text/html".equals(entity.getContentType().getValue())) {
										String pageContent = EntityUtils.toString(entity,"utf-8");//獲取網頁內容
										parsePageContent(pageContent,url);//再次解析頁面的內容
									}

								}else {//未得到響應
									System.out.println("連線時間過長");
									addUrl(url);//再次把連結加到隊列當中去
								}
								
							} catch (ClientProtocolException e) {
								e.printStackTrace();
							} catch (IOException e) {
								e.printStackTrace();
							}finally {
								try {
									if(response != null) {
										response.close();
									}
									
									if(httpClient != null) {
										httpClient.close();
									}
									
								} catch (IOException e) {
									e.printStackTrace();
								}
							}
						}
						
					}
				});
			}else {//佇列裡沒有連結
				if(((ThreadPoolExecutor)executorService).getActiveCount() == 0) {//要求執行緒池中沒有還在執行的執行緒
					exeFlag = false;
					break;
				}
			}
		}
		
		//避免解析不過來讓執行緒休眠
		try {
			Thread.sleep(1000);//(給執行緒解析的時間,避免出現問題)
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
	}
	
	/**
	 * 通過網頁爬蟲爬蟲框架jsoup對網頁內容進行解析(再次解析頁面的內容)
	 * @param pageContent
	 */
	public static void parsePageContent(String pageContent,String realDir) {
		Document doc = Jsoup.parse(pageContent);//jsp頁面中的doc樹
		Elements aEles = doc.select("a");//通過doc獲取a標籤
		for (Element aEle : aEles) {
			String aHref = aEle.attr("href");
//			System.out.println(realDir + aHref);//所有的連結地址
			
			String url = realDir + aHref;
			/**
			 * 連結分為三種(以這個為例)
			 * 目標連結(jar)
			 * 過濾的連結(不需要的連結)
			 * 迭代解析的連結(需要再次解析的連結)
			 */
			
			if(null == url || "".equals(url) ) return;
			
			boolean f = true;//預設就是我想要的連結
			for (String excludeUrl : excludeUrls) {
				if(url.endsWith(excludeUrl)) {//url.endsWith(excludeUrl)  連結url以excludeUrl結尾
					f = false;//不是需要的連結
					break;
				}
			}
			
			if(f && url.endsWith(".jar")) {
				System.out.println("爬了第"+(++total)+"個目標,連結地址url為:"+ url);
			}else {//迭代解析的連結
				addUrl(url);//加入到佇列裡面去,需要再一次爬取的
			}
		}
	}

	/**
	 * 新增到爬蟲佇列裡面,等待再一次爬取
	 * @param url
	 */
	private static void addUrl(String url) {
		System.out.println(url + "新增成功");
		waitForCrawlerUrls.add(url);
	}
	
	/**
	 * 給佇列提供初始值
	 */
	public 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();
	}
	

	public static void main(String[] args) {
		init();
	}
	
	//未進行優化的程式碼
	public static void version1() {

		//連結地址來自於隊列當中
		while(waitForCrawlerUrls.size() > 0) {
			String url = waitForCrawlerUrls.poll();//(隊列當中的取值)摘取佇列的第一個元素,並且移除
			
			//CloseableHttpClient可以被用於從客戶端傳送HTTP請求到服務端
			CloseableHttpClient httpClient = HttpClients.createDefault();//獲取httpclient的一個例項
			HttpGet httpGet = new HttpGet(url);//獲得是什麼請求
			CloseableHttpResponse response = null;
			
			//執行
			try {
				//要抓異常是因為避免訪問超時
				response = httpClient.execute(httpGet);
				HttpEntity entity = response.getEntity();//獲取內容
//				System.out.println(entity.getContentType().toString());
//				System.out.println(entity.getContentType().getValue());
				//凡是text/html(網頁)這一型別的需要進行再次解析
				if("text/html".equals(entity.getContentType().getValue())) {
					String pageContent = EntityUtils.toString(entity,"utf-8");//獲取網頁內容
					parsePageContent(pageContent,url);//再次解析頁面的內容
//					System.out.println(pageContent);
				}
				
			} catch (ClientProtocolException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}finally {
				try {
					if(response != null) {
						response.close();
					}
					
					if(httpClient != null) {
						httpClient.close();
					}
					
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		
	}
	
	
}