零基礎(chǔ)寫(xiě)Java知乎爬蟲(chóng)之抓取知乎答案
前期我們抓取標(biāo)題是在該鏈接下:
http://www.zhihu.com/explore/recommendations
但是顯然這個(gè)頁(yè)面是無(wú)法獲取答案的。
一個(gè)完整問(wèn)題的頁(yè)面應(yīng)該是這樣的鏈接:
http://www.zhihu.com/question/22355264
仔細(xì)一看,啊哈我們的封裝類(lèi)還需要進(jìn)一步包裝下,至少需要個(gè)questionDescription來(lái)存儲(chǔ)問(wèn)題描述:
import java.util.ArrayList;
public class Zhihu {
public String question;// 問(wèn)題
public String questionDescription;// 問(wèn)題描述
public String zhihuUrl;// 網(wǎng)頁(yè)鏈接
public ArrayList<String> answers;// 存儲(chǔ)所有回答的數(shù)組
// 構(gòu)造方法初始化數(shù)據(jù)
public Zhihu() {
question = "";
questionDescription = "";
zhihuUrl = "";
answers = new ArrayList<String>();
}
@Override
public String toString() {
return "問(wèn)題:" + question + "\n" + "描述:" + questionDescription + "\n"
+ "鏈接:" + zhihuUrl + "\n回答:" + answers + "\n";
}
}
我們給知乎的構(gòu)造函數(shù)加上一個(gè)參數(shù),用來(lái)設(shè)定url值,因?yàn)閡rl確定了,這個(gè)問(wèn)題的描述和答案也就都能抓到了。
我們將Spider的獲取知乎對(duì)象的方法改一下,只獲取url即可:
static ArrayList<Zhihu> GetZhihu(String content) {
// 預(yù)定義一個(gè)ArrayList來(lái)存儲(chǔ)結(jié)果
ArrayList<Zhihu> results = new ArrayList<Zhihu>();
// 用來(lái)匹配url,也就是問(wèn)題的鏈接
Pattern urlPattern = Pattern.compile("<h2>.+?question_link.+?href=\"(.+?)\".+?</h2>");
Matcher urlMatcher = urlPattern.matcher(content);
// 是否存在匹配成功的對(duì)象
boolean isFind = urlMatcher.find();
while (isFind) {
// 定義一個(gè)知乎對(duì)象來(lái)存儲(chǔ)抓取到的信息
Zhihu zhihuTemp = new Zhihu(urlMatcher.group(1));
// 添加成功匹配的結(jié)果
results.add(zhihuTemp);
// 繼續(xù)查找下一個(gè)匹配對(duì)象
isFind = urlMatcher.find();
}
return results;
}
接下來(lái),就是在Zhihu的構(gòu)造方法里面,通過(guò)url獲取所有的詳細(xì)數(shù)據(jù)。
我們先要對(duì)url進(jìn)行一個(gè)處理,因?yàn)橛械尼槍?duì)回答的,它的url是:
http://www.zhihu.com/question/22355264/answer/21102139
有的針對(duì)問(wèn)題的,它的url是:
http://www.zhihu.com/question/22355264
那么我們顯然需要的是第二種,所以需要用正則把第一種鏈接裁切成第二種,這個(gè)在Zhihu中寫(xiě)個(gè)函數(shù)即可。
// 處理url
boolean getRealUrl(String url) {
// 將http://www.zhihu.com/question/22355264/answer/21102139
// 轉(zhuǎn)化成http://www.zhihu.com/question/22355264
// 否則不變
Pattern pattern = Pattern.compile("question/(.*?)/");
Matcher matcher = pattern.matcher(url);
if (matcher.find()) {
zhihuUrl = " } else {
return false;
}
return true;
}
接下來(lái)就是各個(gè)部分的獲取工作了。
先看下標(biāo)題:
正則把握住那個(gè)class即可,正則語(yǔ)句可以寫(xiě)成:zm-editable-content\">(.+?)<
運(yùn)行下看看結(jié)果:
哎喲不錯(cuò)哦。
接下來(lái)抓取問(wèn)題描述:
啊哈一樣的原理,抓住class,因?yàn)樗鼞?yīng)該是這個(gè)的唯一標(biāo)識(shí)。
驗(yàn)證方法:右擊查看頁(yè)面源代碼,ctrl+F看看頁(yè)面中有沒(méi)有其他的這個(gè)字符串。
后來(lái)經(jīng)過(guò)驗(yàn)證,還真出了問(wèn)題:
標(biāo)題和描述內(nèi)容前面的class是一樣的。
那只能通過(guò)修改正則的方式來(lái)重新抓?。?/p>
// 匹配標(biāo)題
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);
}
最后就是循環(huán)抓取答案了:
初步暫定正則語(yǔ)句:/answer/content.+?<div.+?>(.*?)</div>
改下代碼之后我們會(huì)發(fā)現(xiàn)軟件運(yùn)行的速度明顯變慢了,因?yàn)樗枰L問(wèn)每個(gè)網(wǎng)頁(yè)并且把上面的內(nèi)容抓下來(lái)。
比如說(shuō)編輯推薦有20個(gè)問(wèn)題,那么就需要訪問(wèn)網(wǎng)頁(yè)20次,速度也就慢下來(lái)了。
試驗(yàn)一下,看上去效果不錯(cuò):
OK,那就先這樣好了~下次繼續(xù)進(jìn)行一些細(xì)節(jié)的調(diào)整,比如多線程,IO流寫(xiě)入本地等等。
附項(xiàng)目源碼:
Zhihu.java
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Zhihu {
public String question;// 問(wèn)題
public String questionDescription;// 問(wèn)題描述
public String zhihuUrl;// 網(wǎng)頁(yè)鏈接
public ArrayList<String> answers;// 存儲(chǔ)所有回答的數(shù)組
// 構(gòu)造方法初始化數(shù)據(jù)
public Zhihu(String url) {
// 初始化屬性
question = "";
questionDescription = "";
zhihuUrl = "";
answers = new ArrayList<String>();
// 判斷url是否合法
if (getRealUrl(url)) {
System.out.println("正在抓取" + zhihuUrl);
// 根據(jù)url獲取該問(wèn)答的細(xì)節(jié)
String content = Spider.SendGet(zhihuUrl);
Pattern pattern;
Matcher matcher;
// 匹配標(biāo)題
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();
}
}
}
// 根據(jù)自己的url抓取自己的問(wèn)題和描述和答案
public boolean getAll() {
return true;
}
// 處理url
boolean getRealUrl(String url) {
// 將http://www.zhihu.com/question/22355264/answer/21102139
// 轉(zhuǎn)化成http://www.zhihu.com/question/22355264
// 否則不變
Pattern pattern = Pattern.compile("question/(.*?)/");
Matcher matcher = pattern.matcher(url);
if (matcher.find()) {
zhihuUrl = " } else {
return false;
}
return true;
}
@Override
public String toString() {
return "問(wèn)題:" + 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) {
// 定義一個(gè)字符串用來(lái)存儲(chǔ)網(wǎng)頁(yè)內(nèi)容
String result = "";
// 定義一個(gè)緩沖字符輸入流
BufferedReader in = null;
try {
// 將string轉(zhuǎn)成url對(duì)象
URL realUrl = new URL(url);
// 初始化一個(gè)鏈接到那個(gè)url的連接
URLConnection connection = realUrl.openConnection();
// 開(kāi)始實(shí)際的連接
connection.connect();
// 初始化 BufferedReader輸入流來(lái)讀取URL的響應(yīng)
in = new BufferedReader(new InputStreamReader(
connection.getInputStream(), "UTF-8"));
// 用來(lái)臨時(shí)存儲(chǔ)抓取到的每一行的數(shù)據(jù)
String line;
while ((line = in.readLine()) != null) {
// 遍歷抓取到的每一行并將其存儲(chǔ)到result里面
result += line;
}
} catch (Exception e) {
System.out.println("發(fā)送GET請(qǐng)求出現(xiàn)異常!" + e);
e.printStackTrace();
}
// 使用finally來(lái)關(guān)閉輸入流
finally {
try {
if (in != null) {
in.close();
}
} catch (Exception e2) {
e2.printStackTrace();
}
}
return result;
}
// 獲取所有的編輯推薦的知乎內(nèi)容
static ArrayList<Zhihu> GetRecommendations(String content) {
// 預(yù)定義一個(gè)ArrayList來(lái)存儲(chǔ)結(jié)果
ArrayList<Zhihu> results = new ArrayList<Zhihu>();
// 用來(lái)匹配url,也就是問(wèn)題的鏈接
Pattern pattern = Pattern
.compile("<h2>.+?question_link.+?href=\"(.+?)\".+?</h2>");
Matcher matcher = pattern.matcher(content);
// 是否存在匹配成功的對(duì)象
Boolean isFind = matcher.find();
while (isFind) {
// 定義一個(gè)知乎對(duì)象來(lái)存儲(chǔ)抓取到的信息
Zhihu zhihuTemp = new Zhihu(matcher.group(1));
// 添加成功匹配的結(jié)果
results.add(zhihuTemp);
// 繼續(xù)查找下一個(gè)匹配對(duì)象
isFind = matcher.find();
}
return results;
}
}
Main.java
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
// 定義即將訪問(wèn)的鏈接
String url = " // 訪問(wèn)鏈接并獲取頁(yè)面內(nèi)容
String content = Spider.SendGet(url);
// 獲取編輯推薦
ArrayList<Zhihu> myZhihu = Spider.GetRecommendations(content);
// 打印結(jié)果
System.out.println(myZhihu);
}
}
以上就是抓取知乎答案的全部記錄,非常的詳盡,有需要的朋友可以參考下
- JAVA使用爬蟲(chóng)抓取網(wǎng)站網(wǎng)頁(yè)內(nèi)容的方法
- 零基礎(chǔ)寫(xiě)Java知乎爬蟲(chóng)之將抓取的內(nèi)容存儲(chǔ)到本地
- Java爬蟲(chóng)實(shí)戰(zhàn)抓取一個(gè)網(wǎng)站上的全部鏈接
- Java爬蟲(chóng)抓取視頻網(wǎng)站下載鏈接
- java爬蟲(chóng)Gecco工具抓取新聞實(shí)例
- java正則表達(dá)式簡(jiǎn)單使用和網(wǎng)頁(yè)爬蟲(chóng)的制作代碼
- 基于Java HttpClient和Htmlparser實(shí)現(xiàn)網(wǎng)絡(luò)爬蟲(chóng)代碼
- java實(shí)現(xiàn)簡(jiǎn)單的爬蟲(chóng)之今日頭條
- Java實(shí)現(xiàn)的爬蟲(chóng)抓取圖片并保存操作示例
- 一篇文章教會(huì)你使用java爬取想要的資源
相關(guān)文章
Springboot?集成spring?cache緩存的解決方案
這篇文章主要介紹了Springboot?集成spring?cache緩存,使用緩存最關(guān)鍵的一點(diǎn)就是保證緩存與數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性,本文給大家介紹最常用的緩存操作模式,對(duì)Springboot?集成spring?cache緩存操作流程感興趣的朋友一起看看吧2022-06-06MyBatis配置的應(yīng)用與對(duì)比jdbc的優(yōu)勢(shì)
這篇文章主要介紹了MyBatis配置的使用與相對(duì)于jdbc的優(yōu)勢(shì),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07SpringBoot+Vue+Redis實(shí)現(xiàn)單點(diǎn)登錄(一處登錄另一處退出登錄)
小編接到一個(gè)需求,需要實(shí)現(xiàn)用戶(hù)在瀏覽器登錄后,跳轉(zhuǎn)到其他頁(yè)面,當(dāng)用戶(hù)在其它地方又登錄時(shí),前面用戶(hù)登錄的頁(yè)面退出登錄,這篇文章主要介紹了SpringBoot+Vue+Redis實(shí)現(xiàn)單點(diǎn)登錄,需要的朋友可以參考下2019-12-12利用Java代碼寫(xiě)一個(gè)并行調(diào)用模板
這篇文章主要介紹了利用Java代碼寫(xiě)一個(gè)并行調(diào)用模板,文章基于Java的相關(guān)內(nèi)容展開(kāi)寫(xiě)一個(gè)并行調(diào)用模板的詳細(xì)介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-05-05Java 數(shù)據(jù)結(jié)構(gòu)與算法系列精講之時(shí)間復(fù)雜度與空間復(fù)雜度
對(duì)于一個(gè)算法,其時(shí)間復(fù)雜度和空間復(fù)雜度往往是相互影響的,當(dāng)追求一個(gè)較好的時(shí)間復(fù)雜度時(shí),可能會(huì)使空間復(fù)雜度的性能變差,即可能導(dǎo)致占用較多的存儲(chǔ)空間,這篇文章主要給大家介紹了關(guān)于Java時(shí)間復(fù)雜度、空間復(fù)雜度的相關(guān)資料,需要的朋友可以參考下2022-02-02