zoukankan      html  css  js  c++  java
  • [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 + " " + "描述:" + questionDescription + " "  
    20.                 + "链接:" + zhihuUrl + " 回答:" + answers + " ";  
    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 + " " + "描述:" + questionDescription + " "  
    74.                 + "链接:" + zhihuUrl + " 回答:" + answers.size() + " ";  
    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. }  


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

     

  • 相关阅读:
    线程池的创建方式
    lock和synchronized如何选择?
    Java中常用的url签名防篡改方法
    jvm异常记录
    mysql索引
    PHP:PDO prepare预处理
    零度CC JavaScript获取页面、屏幕尺寸大小
    【转】 Javascript中document.execCommand()的用法
    获取元素的属性-border问题
    CSS样式定义的优先级顺序总结
  • 原文地址:https://www.cnblogs.com/shareshow/p/4786959.html
Copyright © 2011-2022 走看看