【網絡爬蟲】【java】微博爬蟲(一):小試牛刀——網易微博爬蟲(自定義關鍵字爬取微博數據)(附軟件源碼)

一、寫在前面

       (本專欄分爲“java版微博爬蟲”和“python版網絡爬蟲”兩個項目,系列裏所有文章將基於這兩個項目講解,項目完整源碼已經整理到我的Github,有興趣的可以去看下,鏈接地址在文末。)

        網絡爬蟲根據需求的不同也分不同種類:

        1.一種是爬取網頁鏈接,通過url鏈接得到這個html頁面中指定的鏈接,把這些鏈接存儲起來,再依次以這些鏈接爲源,再次爬取連接指向html頁面中的鏈接……如此層層遞歸下去,常用的方法是廣度優先或者深度優先,根據爬取層次需求不同而選擇不同的方法達到最優效果,爬蟲的效率優化是一個關鍵。

        搜索引擎的第一個步驟就是通過爬蟲得到需要索引的鏈接或數據,存放於數據庫,然後對這些數據建立索引,然後定義查詢語句,解析查詢語句並利用檢索器對數據庫裏的數據進行檢索。

        2.一種是爬取數據信息,如文本信息、圖片信息等,有時需要做數據分析,通過某種手段來獲取數據樣本以供後續分析,常用的方法是爬蟲獲取指定數據樣本或利用現有的公共數據庫。本文的微博爬蟲屬於第二種類,根據自定義搜索關鍵字爬取微博信息數據。

 

        對於網絡爬蟲原理,其實並不複雜。基本思路是:由關鍵字指定的url把所有相關的html頁面全抓下來(html即爲字符串),然後解析html文本(通常是正則表達式或者現成工具包如jsoup),提取微博文本信息,然後把文本信息存儲起來

        重點在於對html頁面源碼結構的分析,不同的html需要不同的解析方法;還有就是長時間爬取可能對IP有影響,有時需要獲取代理IP,甚至需要僞裝瀏覽器爬取。

 

        對於微博,通常情況下是必須登錄才能看到微博信息數據(比如騰訊微博),但是有的微博有搜索機制,在非登錄的情況下可以直接通過搜索話題來查找相關信息(如新浪微博、網易微博)。考慮到某些反爬蟲機制,如果一個賬號總是爬取信息可能會有些影響(比如被封號),所以本文采用的爬蟲都是非登錄、直接進入微博搜索頁面爬取。這裏關鍵是初始url地址

 

二、網頁分析

舉個例子,對於有搜索機制的微博,如新浪微博和網易微博:(這裏尤其要注意地址及參數!)

        新浪微博搜索話題地址:http://s.weibo.com/weibo/蘋果手機&nodup=1&page=50

        網易微博搜索話題地址:http://t.163.com/tag/蘋果手機

分別來看下網頁截圖和對應網頁源碼:

(1) 新浪微博:





(2) 網易微博:






        我們需要做的就是把微博的文本提取出來,這裏有些特徵信息:根據關鍵字搜到的微博,其關鍵字會被標紅,在html源碼裏有體現,分別查看兩個網頁的源代碼。

        可以看到,新浪微博的源碼全部爲html標籤,爲了反爬蟲故意將源碼混亂,並且漢字也做了處理,顯示漢字的utf-8編碼而不是直接顯示漢字,不太好找,這裏就需要查找紅色字體color:red的部分,其中<span style=\”color:red;\”>\u82f9\u679c\u624b\u673a<\span>中間夾着的哪些utf8編碼其實就是關鍵字“蘋果手機”。後面的那些utf8編碼就是本條微博的文本內容。

        而網易微博在這方面就要看起來容易許多,至少html裏直接顯示的是漢字,比較好找,而且微博文本部分其實是以json格式體現的,直接解析json就可以提取文本數據了,當然也可以直接用正則。

 

三、爬取微博

這裏先寫個簡單的爬蟲,原理都差不多,就拿網易微博爲例,先說下爬蟲程序需要用到的工具包:

httpclient-4.3.1.jar  -------建立HTTP鏈接,用於從url獲取html

httpcore-4.3.jar

httpmime-4.3.1.jar

httpclient-cache-4.3.1.jar

fluent-hc-4.3.1.jar

fastjson-1.1.41.jar   ------解析json的工具包

jsoup-1.7.3.jar      -------解析xml,html的工具包

dom4j-1.6.1.jar     -------讀寫xml的工具包

commons-lang-2.1.jar

commons-logging-1.2.jar

commons-codec-1.8.jar

 

總體思路:

1. getHTML()方法:從url得到html字符串。

這裏有兩個關鍵點:

(1) 設置用戶cookie策略,屏蔽掉cookie rejected的報錯,當然可以不設置,直接用默認的client,即是CloseableHttpClient client = HttpClients.createDefault();創建的客戶端,但是會報錯;設置cookie的代碼如下:

CookieSpecProvider cookieSpecProvider = new CookieSpecProvider(){
			public CookieSpec create(HttpContext context){
				return new BrowserCompatSpec(){
					@Override
					public void validate(Cookie cookie, CookieOrigin origin) throws MalformedCookieException {
						//Oh, I am easy;
					}
				};
			}
		};
		Registry<CookieSpecProvider> r = RegistryBuilder
				.<CookieSpecProvider> create()
				.register(CookieSpecs.BEST_MATCH, new BestMatchSpecFactory())
				.register(CookieSpecs.BROWSER_COMPATIBILITY, new BrowserCompatSpecFactory())
				.register("easy", cookieSpecProvider)
				.build();

(2) 設置socket超時socketTimeout和連接超時connectTimeout,這很關鍵,如果不設置的話,當網絡不好的情況下,某次請求沒有及時得到響應,程序可能會卡死。但是設置連接超時,超時之後再自動重連就可以避免這個問題。代碼如下:

RequestConfig requestConfig = RequestConfig.custom()
				.setCookieSpec("easy")
				.setSocketTimeout(5000) //設置socket超時時間
				.setConnectTimeout(5000)  //設置connect超時時間
				.build();

2.isExitHTML()方法:

判斷html是否合法(有效html,有微博內容的),有時候會出現頁面不存在的情況,是因爲該關鍵字沒有微博信息,這是頁面有提示:“沒有找到相關的微博呢,換個關鍵詞試試吧!”如下圖;



3.writeWeibo2txt()方法:

        正則解析(這裏主要解析微博文本內容content、用戶id、發文時間prettyTime),得到微博文本數據:控制檯輸出、寫到txt文件;

        (這裏沒有用jsoup去解析html,直接用的正則,有時間後面再寫jsoup解析html的,比正則方便,當然對正則表達式這個工具掌握很熟練的朋友可以忽略……)

        我的博客裏另外有一篇正則教程完全總結,可以去看下:正則表達式總結

        本來網易微博的源代碼裏嵌入了json元素,前面的html標籤的都不是微博文本數據,在這個標籤後面        <scriptid="data_searchTweet" type="application/json">纔是json格式包括的微博文本,本來打算先把json串用正則匹配出來,後來發現,直接匹配關鍵字豈不更好?但這裏要注意的是匹配的時候可能會有很多換行符之類的東西,我這裏只匹配了三個field,正則表達式"id":\s"\d{19}",(\n*?)|(\s*?)"content":\s".*?",(\n*?)|(\s*?)"prettyTime":\s".*?"

        順便推薦一個檢測正則的工具:http://tool.oschina.net/regex?optionGlobl=global

 

把源碼貼上來吧:

/**
 * @note 1.根據搜索關鍵詞從指定url得到相應的html頁面,並驗證其合法性;
 *       2.得到微博樣本:寫到txt文件裏
 * 
 * @author DianaCody
 * @since 2014-09-26 15:08
 *
 */

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.CookieSpecs;
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.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.cookie.Cookie;
import org.apache.http.cookie.CookieOrigin;
import org.apache.http.cookie.CookieSpec;
import org.apache.http.cookie.CookieSpecProvider;
import org.apache.http.cookie.MalformedCookieException;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.cookie.BestMatchSpecFactory;
import org.apache.http.impl.cookie.BrowserCompatSpec;
import org.apache.http.impl.cookie.BrowserCompatSpecFactory;
import org.apache.http.protocol.HttpContext;
import org.apache.http.util.EntityUtils;

public class Weibo163 {
	
	private String getHTML(String url) throws URISyntaxException, ClientProtocolException, IOException {
		//採用用戶自定義cookie策略,不顯示cookie rejected的報錯
		CookieSpecProvider cookieSpecProvider = new CookieSpecProvider(){
			public CookieSpec create(HttpContext context){
				return new BrowserCompatSpec(){
					@Override
					public void validate(Cookie cookie, CookieOrigin origin) throws MalformedCookieException {
						
					}
				};
			}
		};
		Registry<CookieSpecProvider> r = RegistryBuilder
				.<CookieSpecProvider> create()
				.register(CookieSpecs.BEST_MATCH, new BestMatchSpecFactory())
				.register(CookieSpecs.BROWSER_COMPATIBILITY, new BrowserCompatSpecFactory())
				.register("cookie", cookieSpecProvider)
				.build();
		RequestConfig requestConfig = RequestConfig.custom()
				.setCookieSpec("cookie")
				.setSocketTimeout(5000) //設置socket超時時間
				.setConnectTimeout(5000)  //設置connect超時時間
				.build();
		CloseableHttpClient httpClient = HttpClients.custom()
				.setDefaultCookieSpecRegistry(r)
				.setDefaultRequestConfig(requestConfig)
				.build();
		HttpGet httpGet = new HttpGet(url);
		httpGet.setConfig(requestConfig);
		String html = "html獲取失敗"; //用於驗證是否取到正常的html
		try{
			CloseableHttpResponse response = httpClient.execute(httpGet);
			html = EntityUtils.toString(response.getEntity());
			//System.out.println(html); //打印返回的html			
		} catch (IOException e) {
			e.printStackTrace();
		}
		return html;
	}
	
	private boolean isExistHTML(String html) throws InterruptedException {
		boolean isExist = false;
		Pattern pNoResult = Pattern.compile("\\\\u6ca1\\\\u6709\\\\u627e\\\\u5230\\\\u76f8"
				+ "\\\\u5173\\\\u7684\\\\u5fae\\\\u535a\\\\u5462\\\\uff0c\\\\u6362\\\\u4e2a"
				+ "\\\\u5173\\\\u952e\\\\u8bcd\\\\u8bd5\\\\u5427\\\\uff01"); //沒有找到相關的微博呢,換個關鍵詞試試吧!(html頁面上的信息)
		Matcher mNoResult = pNoResult.matcher(html);
		if(!mNoResult.find()) {
			isExist = true;
		}
		return isExist;
	}
	
	private void writeWeibo2txt(String html, String savePath) throws IOException {
		File htmltxt = new File(savePath); //新建一個txt文件用於存放爬取的結果信息
		FileWriter fw = new FileWriter(htmltxt);
		BufferedWriter bw = new BufferedWriter(fw);
		//regex-----"id":\s"\d{19}",(\n*?)|(\s*?)"content":\s".*?",(\n*?)|(\s*?)"prettyTime":\s".*?"
		Pattern p = Pattern.compile("\"id\":\\s\"\\d{19}\",(\\n*?)|(\\s*?)\"content\":\\s\".*?\",(\\n*?)|(\\s*?)\"prettyTime\":\\s\".*?\"");
		Matcher m = p.matcher(html);
		while(m.find()) {
			System.out.println(m.group());
			bw.write(m.group());
		}
		bw.close();
	}
	
	public static void main(String[] args) throws IOException, URISyntaxException, InterruptedException {
		Weibo163 crawler = new Weibo163();
		String searchword = "iPad"; //搜索關鍵字爲"iPad"的微博html頁面
		String html = crawler.getHTML("http://t.163.com/tag/"+searchword);
		String savePath = "e:/weibo/html.txt"; //輸出到txt文件的存放路徑
		if(html != "html獲取失敗") {
			if(crawler.isExistHTML(html)) {
				System.out.println(html);
				crawler.writeWeibo2txt(html, savePath);
			}
		}
		//Pattern p = Pattern.compile("<script id=\"data_searchTweet\" type=\"application/json\">.+?<\script>"); //<script id="data_searchTweet" type="application/json">.*?<\script>
		//Matcher m = p.matcher(html);
		//html = crawler.getHTML("http://s.weibo.com/weibo/"+searchword+"&nodup=1&page="+1);
		
		//System.out.println(html);
	}

}

程序運行截圖:






該java爬蟲項目源碼github地址:https://github.com/DianaCody/Spider_SinaTweetCrawler_java


原創文章,轉載請註明出處:http://blog.csdn.net/dianacody/article/details/39584977


另外一個系列對爬蟲的整理筆記http://www.crifan.com/files/doc/docbook/web_scrape_emulate_login/release/html/web_scrape_emulate_login.html

發佈了91 篇原創文章 · 獲贊 41 · 訪問量 40萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章