知乎是一個真實的網絡問答社區,社區氛圍友好、理性、認真,連接各行各業的精英。他們分享著彼此的專業知識、經驗和見解,為中文互聯網源源不斷地提供高質量的信息。
首先花個三五分鐘設計一個Logo=。=作為一個程序員我一直有一顆做美工的心!
好吧做的有點小湊合,就先湊合著用咯。
接下來呢,我們開始制作知乎的爬蟲。
首先,確定第一個目標:編輯推薦。
網頁鏈接:http://www.zhihu.com/explore/recommendations
我們對上次的代碼稍作修改,先實現能夠獲取該頁面內容:
import java.io.*; import java.net.*; import java.util.regex.*; public class Main { 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())); // 用來臨時存儲抓取到的每一行的數據 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 String RegexString(String targetStr, String patternStr) { // 定義一個樣式模板,此中使用正則表達式,括號中是要抓的內容 // 相當于埋好了陷阱匹配的地方就會掉下去 Pattern pattern = Pattern.compile(patternStr); // 定義一個matcher用來做匹配 Matcher matcher = pattern.matcher(targetStr); // 如果找到了 if (matcher.find()) { // 打印出結果 return matcher.group(1); } return "Nothing"; } public static void main(String[] args) { // 定義即將訪問的鏈接 String url = "http://www.zhihu.com/explore/recommendations"; // 訪問鏈接并獲取頁面內容 String result = SendGet(url); // 使用正則匹配圖片的src內容 //String imgSrc = RegexString(result, "src=\"(.+?)\""); // 打印結果 System.out.println(result); } }
運行一下木有問題,接下來就是一個正則匹配的問題了。
首先我們先來獲取該頁面的所有的問題。
右擊標題,審查元素:
啊哈,可以看到標題其實是一個a標簽,也就是一個超鏈接,而其中能夠和其他超鏈接區分開的,應該就是那個class了,也就是類選擇器。
于是我們的正則語句就出來了:question_link.+?href=\"(.+?)\"
調用RegexString函數,并給它傳參:
public static void main(String[] args) { // 定義即將訪問的鏈接 String url = "http://www.zhihu.com/explore/recommendations"; // 訪問鏈接并獲取頁面內容 String result = SendGet(url); // 使用正則匹配圖片的src內容 String imgSrc = RegexString(result, "question_link.+?>(.+?)<"); // 打印結果 System.out.println(imgSrc); }
啊哈,可以看到我們成功抓到了一個標題(注意,只是一個):
等一下啊這一大堆的亂七八糟的是什么玩意?!
別緊張=。=它只是字符亂碼而已。
編碼問題可以參見:HTML字符集
一般來說,對中文支持較好的主流編碼是UTF-8,GB2312和GBK編碼。
網頁可以通過meta標簽的charset來設置網頁編碼,譬如:
<meta charset="utf-8" />
我們右擊,查看頁面源代碼:
可以看到,知乎采用的是UTF-8編碼。
在這里和大家解釋一下查看頁面源代碼和審查元素的區別。
查看頁面源代碼是顯示整個頁面的所有代碼,沒有按照HTML的標簽進行排版,相當于是直接查看源碼,這種方式對于查看整個網頁的信息,比如meta比較有用。
審查元素,或者有的瀏覽器叫查看元素,是針對你右擊的元素進行查看,比如一個div或者img,比較適用于單獨查看某個對象的屬性和標簽。
好的,我們現在知道了問題出在了編碼上,接下來就是對抓取到的內容進行編碼轉換了。
在java中實現很簡單,只需要在InputStreamReader里面指定編碼方式就行:
// 初始化 BufferedReader輸入流來讀取URL的響應 in = new BufferedReader(new InputStreamReader( connection.getInputStream(),"UTF-8"));
此時再運行程序,便會發現可以正常顯示標題了:
好的!非常好!
但是現在才只有一個標題,我們需要的是所有的標題。
我們將正則稍加修改,把搜索到的結果存儲到一個ArrayList里面:
import java.io.*; import java.net.*; import java.util.ArrayList; import java.util.regex.*; public class Main { 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<String> RegexString(String targetStr, String patternStr) { // 預定義一個ArrayList來存儲結果 ArrayList<String> results = new ArrayList<String>(); // 定義一個樣式模板,此中使用正則表達式,括號中是要抓的內容 Pattern pattern = Pattern.compile(patternStr); // 定義一個matcher用來做匹配 Matcher matcher = pattern.matcher(targetStr); // 如果找到了 boolean isFind = matcher.find(); // 使用循環將句子里所有的kelvin找出并替換再將內容加到sb里 while (isFind) { //添加成功匹配的結果 results.add(matcher.group(1)); // 繼續查找下一個匹配對象 isFind = matcher.find(); } return results; } public static void main(String[] args) { // 定義即將訪問的鏈接 String url = "http://www.zhihu.com/explore/recommendations"; // 訪問鏈接并獲取頁面內容 String result = SendGet(url); // 使用正則匹配圖片的src內容 ArrayList<String> imgSrc = RegexString(result, "question_link.+?>(.+?)<"); // 打印結果 System.out.println(imgSrc); } }
這樣就能匹配到所有的結果了(因為直接打印了ArrayList所以會有一些中括號和逗號):
OK,這樣就算是完成了知乎爬蟲的第一步。
但是我們可以看出來,用這樣的方式是沒有辦法抓到所有的問題和回答的。
我們需要設計一個Zhihu封裝類,來存儲所有抓取到的對象。
Zhihu.java源碼:
import java.util.ArrayList; public class Zhihu { public String question;// 問題 public String zhihuUrl;// 網頁鏈接 public ArrayList<String> answers;// 存儲所有回答的數組 // 構造方法初始化數據 public Zhihu() { question = ""; zhihuUrl = ""; answers = new ArrayList<String>(); } @Override public String toString() { return "問題:" + question + "\n鏈接:" + zhihuUrl + "\n回答:" + answers + "\n"; } }
再新建一個Spider類來存放一些爬蟲常用的函數。
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> GetZhihu(String content) { // 預定義一個ArrayList來存儲結果 ArrayList<Zhihu> results = new ArrayList<Zhihu>(); // 用來匹配標題 Pattern questionPattern = Pattern.compile("question_link.+?>(.+?)<"); Matcher questionMatcher = questionPattern.matcher(content); // 用來匹配url,也就是問題的鏈接 Pattern urlPattern = Pattern.compile("question_link.+?href=\"(.+?)\""); Matcher urlMatcher = urlPattern.matcher(content); // 問題和鏈接要均能匹配到 boolean isFind = questionMatcher.find() && urlMatcher.find(); while (isFind) { // 定義一個知乎對象來存儲抓取到的信息 Zhihu zhuhuTemp = new Zhihu(); zhuhuTemp.question = questionMatcher.group(1); zhuhuTemp.zhihuUrl = "http://www.zhihu.com" + urlMatcher.group(1); // 添加成功匹配的結果 results.add(zhuhuTemp); // 繼續查找下一個匹配對象 isFind = questionMatcher.find() && urlMatcher.find(); } return results; } }
最后一個main方法負責調用。
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.GetZhihu(content); // 打印結果 System.out.println(myZhihu); } }
Ok這樣就算搞定了。運行一下看看結果:
好的效果不錯。
接下來就是訪問鏈接然后獲取到所有的答案了。
下一回我們再介紹。
好了,以上就是簡單的介紹了如何使用java來抓取知乎的編輯推薦的內容的全部過程了,非常詳盡,也很簡單易懂,對吧,有需要的小伙伴可以參考下,自由擴展也沒問題哈