前期我們抓取標題是在該連結下:
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);
}
}
以上就是抓取知乎答案的全部記錄,非常的詳盡,有需要的朋友可以參考下