[Java]知乎下巴第3集:来人啊快把知乎的答案装到篮子里去

上次我们已经能把知乎的问题抓出来了,但是答案还木有抓出来。

这一回合,我们就连着把答案也一起从网站中抠出来=。=

 

前期我们抓取标题是在该链接下:

http://www.zhihu.com/explore/recommendations

但是显然这个页面是无法获取答案的。

一个完整问题的页面应该是这样的链接:

http://www.zhihu.com/question/22355264

 

仔细一看,啊哈我们的封装类还需要进一步包装下,至少需要个questionDescription来存储问题描述:

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. import java.util.ArrayList;  
  2.   
  3. public class Zhihu {  
  4.     public String question;// 问题  
  5.     public String questionDescription;// 问题描述  
  6.     public String zhihuUrl;// 网页链接  
  7.     public ArrayList<String> answers;// 存储所有回答的数组  
  8.   
  9.     // 构造方法初始化数据  
  10.     public Zhihu() {  
  11.         question = "";  
  12.         questionDescription = "";  
  13.         zhihuUrl = "";  
  14.         answers = new ArrayList<String>();  
  15.     }  
  16.   
  17.     @Override  
  18.     public String toString() {  
  19.         return "问题:" + question + "\n" + "描述:" + questionDescription + "\n"  
  20.                 + "链接:" + zhihuUrl + "\n回答:" + answers + "\n";  
  21.     }  
  22. }  


我们给知乎的构造函数加上一个参数,用来设定url值,因为url确定了,这个问题的描述和答案也就都能抓到了。

 

我们将Spider的获取知乎对象的方法改一下,只获取url即可:

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. static ArrayList<Zhihu> GetZhihu(String content) {  
  2.     // 预定义一个ArrayList来存储结果  
  3.     ArrayList<Zhihu> results = new ArrayList<Zhihu>();  
  4.     // 用来匹配url,也就是问题的链接  
  5.     Pattern urlPattern = Pattern.compile("<h2>.+?question_link.+?href=\"(.+?)\".+?</h2>");  
  6.     Matcher urlMatcher = urlPattern.matcher(content);  
  7.     // 是否存在匹配成功的对象  
  8.     boolean isFind = urlMatcher.find();  
  9.     while (isFind) {  
  10.         // 定义一个知乎对象来存储抓取到的信息  
  11.         Zhihu zhihuTemp = new Zhihu(urlMatcher.group(1));  
  12.         // 添加成功匹配的结果  
  13.         results.add(zhihuTemp);  
  14.         // 继续查找下一个匹配对象  
  15.         isFind = urlMatcher.find();  
  16.     }  
  17.     return results;  
  18. }  


接下来,就是在Zhihu的构造方法里面,通过url获取所有的详细数据。

 

我们先要对url进行一个处理,因为有的针对回答的,它的url是:

http://www.zhihu.com/question/22355264/answer/21102139

有的针对问题的,它的url是:

http://www.zhihu.com/question/22355264

那么我们显然需要的是第二种,所以需要用正则把第一种链接裁切成第二种,这个在Zhihu中写个函数即可。

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. // 处理url  
  2.     boolean getRealUrl(String url) {  
  3.         // 将http://www.zhihu.com/question/22355264/answer/21102139  
  4.         // 转化成http://www.zhihu.com/question/22355264  
  5.         // 否则不变  
  6.         Pattern pattern = Pattern.compile("question/(.*?)/");  
  7.         Matcher matcher = pattern.matcher(url);  
  8.         if (matcher.find()) {  
  9.             zhihuUrl = "http://www.zhihu.com/question/" + matcher.group(1);  
  10.         } else {  
  11.             return false;  
  12.         }  
  13.         return true;  
  14.     }  


接下来就是各个部分的获取工作了。

 

 

先看下标题:

正则把握住那个class即可,正则语句可以写成:zm-editable-content\">(.+?)<

运行下看看结果:

哎哟不错哦。

接下来抓取问题描述:

啊哈一样的原理,抓住class,因为它应该是这个的唯一标识。

验证方法:右击查看页面源代码,ctrl+F看看页面中有没有其他的这个字符串。

后来经过验证,还真出了问题:

标题和描述内容前面的class是一样的。

那只能通过修改正则的方式来重新抓取:

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. // 匹配标题  
  2.             pattern = Pattern.compile("zh-question-title.+?<h2.+?>(.+?)</h2>");  
  3.             matcher = pattern.matcher(content);  
  4.             if (matcher.find()) {  
  5.                 question = matcher.group(1);  
  6.             }  
  7.             // 匹配描述  
  8.             pattern = Pattern  
  9.                     .compile("zh-question-detail.+?<div.+?>(.*?)</div>");  
  10.             matcher = pattern.matcher(content);  
  11.             if (matcher.find()) {  
  12.                 questionDescription = matcher.group(1);  
  13.             }  



 

 

 

最后就是循环抓取答案了:

初步暂定正则语句:/answer/content.+?<div.+?>(.*?)</div>

 

改下代码之后我们会发现软件运行的速度明显变慢了,因为他需要访问每个网页并且把上面的内容抓下来。

比如说编辑推荐有20个问题,那么就需要访问网页20次,速度也就慢下来了。

试验一下,看上去效果不错:

 

 

OK,那就先这样好了~下次继续进行一些细节的调整,比如多线程,IO流写入本地等等。

 

附项目源码:

Zhihu.java

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. import java.util.ArrayList;  
  2. import java.util.regex.Matcher;  
  3. import java.util.regex.Pattern;  
  4.   
  5. public class Zhihu {  
  6.     public String question;// 问题  
  7.     public String questionDescription;// 问题描述  
  8.     public String zhihuUrl;// 网页链接  
  9.     public ArrayList<String> answers;// 存储所有回答的数组  
  10.   
  11.     // 构造方法初始化数据  
  12.     public Zhihu(String url) {  
  13.         // 初始化属性  
  14.         question = "";  
  15.         questionDescription = "";  
  16.         zhihuUrl = "";  
  17.         answers = new ArrayList<String>();  
  18.   
  19.         // 判断url是否合法  
  20.         if (getRealUrl(url)) {  
  21.             System.out.println("正在抓取" + zhihuUrl);  
  22.             // 根据url获取该问答的细节  
  23.             String content = Spider.SendGet(zhihuUrl);  
  24.             Pattern pattern;  
  25.             Matcher matcher;  
  26.             // 匹配标题  
  27.             pattern = Pattern.compile("zh-question-title.+?<h2.+?>(.+?)</h2>");  
  28.             matcher = pattern.matcher(content);  
  29.             if (matcher.find()) {  
  30.                 question = matcher.group(1);  
  31.             }  
  32.             // 匹配描述  
  33.             pattern = Pattern  
  34.                     .compile("zh-question-detail.+?<div.+?>(.*?)</div>");  
  35.             matcher = pattern.matcher(content);  
  36.             if (matcher.find()) {  
  37.                 questionDescription = matcher.group(1);  
  38.             }  
  39.             // 匹配答案  
  40.             pattern = Pattern.compile("/answer/content.+?<div.+?>(.*?)</div>");  
  41.             matcher = pattern.matcher(content);  
  42.             boolean isFind = matcher.find();  
  43.             while (isFind) {  
  44.                 answers.add(matcher.group(1));  
  45.                 isFind = matcher.find();  
  46.             }  
  47.         }  
  48.     }  
  49.   
  50.     // 根据自己的url抓取自己的问题和描述和答案  
  51.     public boolean getAll() {  
  52.   
  53.         return true;  
  54.     }  
  55.   
  56.     // 处理url  
  57.     boolean getRealUrl(String url) {  
  58.         // 将http://www.zhihu.com/question/22355264/answer/21102139  
  59.         // 转化成http://www.zhihu.com/question/22355264  
  60.         // 否则不变  
  61.         Pattern pattern = Pattern.compile("question/(.*?)/");  
  62.         Matcher matcher = pattern.matcher(url);  
  63.         if (matcher.find()) {  
  64.             zhihuUrl = "http://www.zhihu.com/question/" + matcher.group(1);  
  65.         } else {  
  66.             return false;  
  67.         }  
  68.         return true;  
  69.     }  
  70.   
  71.     @Override  
  72.     public String toString() {  
  73.         return "问题:" + question + "\n" + "描述:" + questionDescription + "\n"  
  74.                 + "链接:" + zhihuUrl + "\n回答:" + answers.size() + "\n";  
  75.     }  
  76. }  


Spider.java

 

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. import java.io.BufferedReader;  
  2. import java.io.InputStreamReader;  
  3. import java.net.URL;  
  4. import java.net.URLConnection;  
  5. import java.util.ArrayList;  
  6. import java.util.regex.Matcher;  
  7. import java.util.regex.Pattern;  
  8.   
  9. public class Spider {  
  10.     static String SendGet(String url) {  
  11.         // 定义一个字符串用来存储网页内容  
  12.         String result = "";  
  13.         // 定义一个缓冲字符输入流  
  14.         BufferedReader in = null;  
  15.         try {  
  16.             // 将string转成url对象  
  17.             URL realUrl = new URL(url);  
  18.             // 初始化一个链接到那个url的连接  
  19.             URLConnection connection = realUrl.openConnection();  
  20.             // 开始实际的连接  
  21.             connection.connect();  
  22.             // 初始化 BufferedReader输入流来读取URL的响应  
  23.             in = new BufferedReader(new InputStreamReader(  
  24.                     connection.getInputStream(), "UTF-8"));  
  25.             // 用来临时存储抓取到的每一行的数据  
  26.             String line;  
  27.             while ((line = in.readLine()) != null) {  
  28.                 // 遍历抓取到的每一行并将其存储到result里面  
  29.                 result += line;  
  30.             }  
  31.         } catch (Exception e) {  
  32.             System.out.println("发送GET请求出现异常!" + e);  
  33.             e.printStackTrace();  
  34.         }  
  35.         // 使用finally来关闭输入流  
  36.         finally {  
  37.             try {  
  38.                 if (in != null) {  
  39.                     in.close();  
  40.                 }  
  41.             } catch (Exception e2) {  
  42.                 e2.printStackTrace();  
  43.             }  
  44.         }  
  45.         return result;  
  46.     }  
  47.   
  48.     // 获取所有的编辑推荐的知乎内容  
  49.     static ArrayList<Zhihu> GetRecommendations(String content) {  
  50.         // 预定义一个ArrayList来存储结果  
  51.         ArrayList<Zhihu> results = new ArrayList<Zhihu>();  
  52.         // 用来匹配url,也就是问题的链接  
  53.         Pattern pattern = Pattern  
  54.                 .compile("<h2>.+?question_link.+?href=\"(.+?)\".+?</h2>");  
  55.         Matcher matcher = pattern.matcher(content);  
  56.         // 是否存在匹配成功的对象  
  57.         Boolean isFind = matcher.find();  
  58.         while (isFind) {  
  59.             // 定义一个知乎对象来存储抓取到的信息  
  60.             Zhihu zhihuTemp = new Zhihu(matcher.group(1));  
  61.             // 添加成功匹配的结果  
  62.             results.add(zhihuTemp);  
  63.             // 继续查找下一个匹配对象  
  64.             isFind = matcher.find();  
  65.         }  
  66.         return results;  
  67.     }  
  68.   
  69. }  


Main.java

 

 

[java] view plaincopy在CODE上查看代码片派生到我的代码片
 
  1. import java.util.ArrayList;  
  2.   
  3. public class Main {  
  4.   
  5.     public static void main(String[] args) {  
  6.         // 定义即将访问的链接  
  7.         String url = "http://www.zhihu.com/explore/recommendations";  
  8.         // 访问链接并获取页面内容  
  9.         String content = Spider.SendGet(url);  
  10.         // 获取编辑推荐  
  11.         ArrayList<Zhihu> myZhihu = Spider.GetRecommendations(content);  
  12.         // 打印结果  
  13.         System.out.println(myZhihu);  
  14.     }  
  15. }  


好的,今天就是这样,晚安。

 

 

posted @ 2015-09-06 19:27  独孤龟  阅读(204)  评论(0编辑  收藏  举报