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

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

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

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

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

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

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

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

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

[java] view plaincopy

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

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

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

[java] view plaincopy

  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

  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

  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

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

Spider.java

[java] view plaincopy

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

Main.java

[java] view plaincopy

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

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

时间: 2024-08-02 09:09:51

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

[Java]知乎下巴第0集:让我们一起来做一个知乎爬虫吧哦耶

身边的小伙伴们很多都喜欢刷知乎,当然我也不例外, 但是手机刷太消耗流量,电脑又不太方便. 于是,就诞生了这一款小软件:铛铛铛铛!知乎下巴=.= 知乎下巴,音译就是知乎下吧 ~ 首先我们来缕一缕思绪,想想到底要做什么,列个简单的需求. 需求如下: 1.模拟访问知乎官网(http://www.zhihu.com/) 2.下载指定的页面内容,包括:今日最热,本月最热,编辑推荐 3.下载指定分类中的所有问答,比如:投资,编程,挂科 4.下载指定回答者的所有回答 5.最好有个一键点赞的变态功能(这样我就可

[Java]知乎下巴第1集:爬虫世界百度不仅仅可以拿来测网速

上一集中我们说到需要用Java来制作一个知乎爬虫,那么这一次,我们就来研究一下如何使用代码获取到网页的内容. 首先,没有HTML和CSS和JS和AJAX经验的建议先去W3C(点我点我)小小的了解一下. 说到HTML,这里就涉及到一个GET访问和POST访问的问题. 如果对这个方面缺乏了解可以阅读W3C的这篇:<GET对比POST>. 啊哈,在此不再赘述. 然后咧,接下来我们需要用Java来爬取一个网页的内容. 这时候,我们的百度就要派上用场了. 没错,他不再是那个默默无闻的网速测试器了,他即将

[转] [Java] 知乎下巴第5集:使用HttpClient工具包和宽度爬虫

原文地址:http://blog.csdn.net/pleasecallmewhy/article/details/18010015 下载地址:https://code.csdn.net/wxg694175346/zhihudown 说到爬虫,使用Java本身自带的URLConnection可以实现一些基本的抓取页面的功能,但是对于一些比较高级的功能,比如重定向的处理,HTML标记的去除,仅仅使用URLConnection还是不够的. 在这里我们可以使用HttpClient这个第三方jar包,下

Java陷阱一箩筐----面试题集及解答

Java陷阱一箩筐----面试题集及解答 面试是没什么道理可讲的,它的题目有的不合情理.脱离实际.有在纸上写的,有当面考你的,也有在电话里问的,给你IDE的估计很少. 当然这些都是Java的基本题,那些面试的人大多数不会问你Hibernate有多先进,Eclipse的三个组成部分,或command design pattern,他们都是老一辈了,最喜欢问的就是基础知识.别小看了这些基础,我朋友水平一流,结果就栽在一到基础知识的问题下,和高薪无缘. 问: 第一,谈谈final, finally,

Redis笔记整理(二):Java API使用与Redis分布式集群环境搭建

[TOC] Redis笔记整理(二):Java API使用与Redis分布式集群环境搭建 Redis Java API使用(一):单机版本Redis API使用 Redis的Java API通过Jedis来进行操作,因此首先需要Jedis的第三方库,因为使用的是Maven工程,所以先给出Jedis的依赖: <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactI

Java学习笔记(2)----散列集/线性表/队列/集合/图(Set,List,Queue,Collection,Map)

1. Java集合框架中的所有实例类都实现了Cloneable和Seriablizable接口.所以,它们的实例都是可复制和可序列化的. 2. 规则集存储的是不重复的元素.若要在集合中存储重复的元素,就需要使用线性表.线性表不仅可以存储重复的元素,而且允许用户指定存储的位置.用户可以通过下标来访问线性表中的元素. 3. Java集合支持三种类型的规则集:散列集HashSet.链式散列集LinkedHashSet和树形集TreeSet.HashSet以一个不可预知的顺序存储元素:LinkedHas

Eclipse中java获得mysql的查询结果集

不废话,先上代码,再上解释说明 1 package com.ningmeng; 2 3 import java.sql.*; 4 /** 5 * 1:获取查询结果集 6 * @author biexiansheng 7 * 8 */ 9 public class Test03 { 10 11 public static void main(String[] args) { 12 try { 13 Class.forName("com.mysql.jdbc.Driver"); 14 Sy

ES7.4 学习日记——Java REST Client :连接到集群

1.ES7版本变化 废弃了type,没有类型的概念: 废弃TransportClient,只能使用restclient. 2.Maven依赖 连接客户端主要有Rest Low Level Client和Rest High Level Client两种可以使用,两者的主要区别在于: Rest Low Level Client:低级别的REST客户端,通过http与集群交互,用户需自己编组请求JSON串,及解析响应JSON串.兼容所有ES版本.最小Java版本要求为1.7. Rest High Le

java:redis(redis的集群配置)

服务器集群作用: 服务器集群就是指将很多服务器集中起来一起进行同一种服务,在客户端看来就象是只有一个服务器 集群可以利用多个计算机进行并行计算从而获得很高的计算速度,也可以用多个计算机做备份,从而使得任何一个机器坏了整个系统还是能正常运行.一旦在服务器上安装并运行了群集服务,该服务器即可加入群集.群集化操作可以减少单点故障数量,并且实现了群集化资源的高可用性. redis的集群配置: (.编辑network文件 HOSTNAME=redis(自己定义的hostname) vi /etc/sysc