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. }  


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

     

  • 相关阅读:
    C#操作REDIS例子
    A C# Framework for Interprocess Synchronization and Communication
    UTF8 GBK UTF8 GB2312 之间的区别和关系
    开源项目选型问题
    Mysql命令大全——入门经典
    RAM, SDRAM ,ROM, NAND FLASH, NOR FLASH 详解(引用)
    zabbix邮件报警通过脚本来发送邮件
    centos启动提示unexpected inconsistency RUN fsck MANUALLY
    rm 或者ls 报Argument list too long
    初遇Citymaker (六)
  • 原文地址:https://www.cnblogs.com/shareshow/p/4786959.html
Copyright © 2011-2022 走看看