前期我們抓取標題是在該鏈接下:
http://www.zhihu.com/explore/recommendations
但是顯然這個頁面是無法獲取答案的。
一個完整問題的頁面應該是這樣的鏈接:
http://www.zhihu.com/question/22355264
仔細一看,啊哈我們的封裝類還需要進一步包裝下,至少需要個questionDescription來存儲問題描述:
import java.util.ArrayList; public class Zhihu { public String question;// 問題 public String questionDescription;// 問題描述 public String zhihuUrl;// 網頁鏈接 public ArrayList<String> answers;// 存儲所有回答的數組 // 構造方法初始化數據 public Zhihu() { question = ""; questionDescription = ""; zhihuUrl = ""; answers = new ArrayList<String>(); } @Override public String toString() { return "問題:" + question + "\n" + "描述:" + questionDescription + "\n" + "鏈接:" + zhihuUrl + "\n回答:" + answers + "\n"; } }
我們給知乎的構造函數加上一個參數,用來設定url值,因為url確定了,這個問題的描述和答案也就都能抓到了。
我們將Spider的獲取知乎對象的方法改一下,只獲取url即可:
static ArrayList<Zhihu> GetZhihu(String content) { // 預定義一個ArrayList來存儲結果 ArrayList<Zhihu> results = new ArrayList<Zhihu>(); // 用來匹配url,也就是問題的鏈接 Pattern urlPattern = Pattern.compile("<h2>.+?question_link.+?href=\"(.+?)\".+?</h2>"); Matcher urlMatcher = urlPattern.matcher(content); // 是否存在匹配成功的對象 boolean isFind = urlMatcher.find(); while (isFind) { // 定義一個知乎對象來存儲抓取到的信息 Zhihu zhihuTemp = new Zhihu(urlMatcher.group(1)); // 添加成功匹配的結果 results.add(zhihuTemp); // 繼續查找下一個匹配對象 isFind = urlMatcher.find(); } return results; }
接下來,就是在Zhihu的構造方法里面,通過url獲取所有的詳細數據。
我們先要對url進行一個處理,因為有的針對回答的,它的url是:
http://www.zhihu.com/question/22355264/answer/21102139
有的針對問題的,它的url是:
http://www.zhihu.com/question/22355264
那么我們顯然需要的是第二種,所以需要用正則把第一種鏈接裁切成第二種,這個在Zhihu中寫個函數即可。
// 處理url boolean getRealUrl(String url) { // 將http://www.zhihu.com/question/22355264/answer/21102139 // 轉化成http://www.zhihu.com/question/22355264 // 否則不變 Pattern pattern = Pattern.compile("question/(.*?)/"); Matcher matcher = pattern.matcher(url); if (matcher.find()) { zhihuUrl = "http://www.zhihu.com/question/" + matcher.group(1); } else { return false; } return true; }
接下來就是各個部分的獲取工作了。
先看下標題:
正則把握住那個class即可,正則語句可以寫成:zm-editable-content\">(.+?)<
運行下看看結果:
哎喲不錯哦。
接下來抓取問題描述:
啊哈一樣的原理,抓住class,因為它應該是這個的唯一標識。
驗證方法:右擊查看頁面源代碼,ctrl+F看看頁面中有沒有其他的這個字符串。
后來經過驗證,還真出了問題:
標題和描述內容前面的class是一樣的。
那只能通過修改正則的方式來重新抓取:
// 匹配標題 pattern = Pattern.compile("zh-question-title.+?<h2.+?>(.+?)</h2>"); matcher = pattern.matcher(content); if (matcher.find()) { question = matcher.group(1); } // 匹配描述 pattern = Pattern .compile("zh-question-detail.+?<div.+?>(.*?)</div>"); matcher = pattern.matcher(content); if (matcher.find()) { questionDescription = matcher.group(1); }
最后就是循環抓取答案了:
初步暫定正則語句:/answer/content.+?<div.+?>(.*?)</div>
改下代碼之后我們會發現軟件運行的速度明顯變慢了,因為他需要訪問每個網頁并且把上面的內容抓下來。
比如說編輯推薦有20個問題,那么就需要訪問網頁20次,速度也就慢下來了。
試驗一下,看上去效果不錯:
OK,那就先這樣好了~下次繼續進行一些細節的調整,比如多線程,IO流寫入本地等等。
附項目源碼:
Zhihu.java
import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Zhihu { public String question;// 問題 public String questionDescription;// 問題描述 public String zhihuUrl;// 網頁鏈接 public ArrayList<String> answers;// 存儲所有回答的數組 // 構造方法初始化數據 public Zhihu(String url) { // 初始化屬性 question = ""; questionDescription = ""; zhihuUrl = ""; answers = new ArrayList<String>(); // 判斷url是否合法 if (getRealUrl(url)) { System.out.println("正在抓取" + zhihuUrl); // 根據url獲取該問答的細節 String content = Spider.SendGet(zhihuUrl); Pattern pattern; Matcher matcher; // 匹配標題 pattern = Pattern.compile("zh-question-title.+?<h2.+?>(.+?)</h2>"); matcher = pattern.matcher(content); if (matcher.find()) { question = matcher.group(1); } // 匹配描述 pattern = Pattern .compile("zh-question-detail.+?<div.+?>(.*?)</div>"); matcher = pattern.matcher(content); if (matcher.find()) { questionDescription = matcher.group(1); } // 匹配答案 pattern = Pattern.compile("/answer/content.+?<div.+?>(.*?)</div>"); matcher = pattern.matcher(content); boolean isFind = matcher.find(); while (isFind) { answers.add(matcher.group(1)); isFind = matcher.find(); } } } // 根據自己的url抓取自己的問題和描述和答案 public boolean getAll() { return true; } // 處理url boolean getRealUrl(String url) { // 將http://www.zhihu.com/question/22355264/answer/21102139 // 轉化成http://www.zhihu.com/question/22355264 // 否則不變 Pattern pattern = Pattern.compile("question/(.*?)/"); Matcher matcher = pattern.matcher(url); if (matcher.find()) { zhihuUrl = "http://www.zhihu.com/question/" + matcher.group(1); } else { return false; } return true; } @Override public String toString() { return "問題:" + question + "\n" + "描述:" + questionDescription + "\n" + "鏈接:" + zhihuUrl + "\n回答:" + answers.size() + "\n"; } }
Spider.java
import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Spider { static String SendGet(String url) { // 定義一個字符串用來存儲網頁內容 String result = ""; // 定義一個緩沖字符輸入流 BufferedReader in = null; try { // 將string轉成url對象 URL realUrl = new URL(url); // 初始化一個鏈接到那個url的連接 URLConnection connection = realUrl.openConnection(); // 開始實際的連接 connection.connect(); // 初始化 BufferedReader輸入流來讀取URL的響應 in = new BufferedReader(new InputStreamReader( connection.getInputStream(), "UTF-8")); // 用來臨時存儲抓取到的每一行的數據 String line; while ((line = in.readLine()) != null) { // 遍歷抓取到的每一行并將其存儲到result里面 result += line; } } catch (Exception e) { System.out.println("發送GET請求出現異常!" + e); e.printStackTrace(); } // 使用finally來關閉輸入流 finally { try { if (in != null) { in.close(); } } catch (Exception e2) { e2.printStackTrace(); } } return result; } // 獲取所有的編輯推薦的知乎內容 static ArrayList<Zhihu> GetRecommendations(String content) { // 預定義一個ArrayList來存儲結果 ArrayList<Zhihu> results = new ArrayList<Zhihu>(); // 用來匹配url,也就是問題的鏈接 Pattern pattern = Pattern .compile("<h2>.+?question_link.+?href=\"(.+?)\".+?</h2>"); Matcher matcher = pattern.matcher(content); // 是否存在匹配成功的對象 Boolean isFind = matcher.find(); while (isFind) { // 定義一個知乎對象來存儲抓取到的信息 Zhihu zhihuTemp = new Zhihu(matcher.group(1)); // 添加成功匹配的結果 results.add(zhihuTemp); // 繼續查找下一個匹配對象 isFind = matcher.find(); } return results; } }
Main.java
import java.util.ArrayList; public class Main { public static void main(String[] args) { // 定義即將訪問的鏈接 String url = "http://www.zhihu.com/explore/recommendations"; // 訪問鏈接并獲取頁面內容 String content = Spider.SendGet(url); // 獲取編輯推薦 ArrayList<Zhihu> myZhihu = Spider.GetRecommendations(content); // 打印結果 System.out.println(myZhihu); } }
以上就是抓取知乎答案的全部記錄,非常的詳盡,有需要的朋友可以參考下