知乎脚丫第三弹

整合代码,简洁明了

原谅我这个微强迫症患者,按朋友说得,这是病得治!哈哈,可我觉得还行哈,还蛮舒服的。刚开始就扯远了,为了爬虫抓取知乎发现页面上所有的问题,我写3个类,Spider类——获取网页所有信息,及特定内容;Zhihu类——封装类,存储问题标题、问题的描述、问题的链接地址、评论者名字、和所有的评论;Main类——运行整个项目的启动类。其实还写了一个UnitTest类——单元测试,测试某个方法或者模块是否正确。我觉得单元测试还是很有必要的,与其出错逐句找Bug,倒不如保证每个模块的正确,即便最后出错,也能快速定位到err的地方。

区分3个网页

1、知乎发现页面https://www.zhihu.com/explore,这个页面就是第一次爬虫走过的页面,获取所有问题的链接地址。
enter description here
2、问题评论页面https://www.zhihu.com/question/47684665/answer/108154821,这个页面虽然有关于问题的评论,但是并没有全部评论的信息。
enter description here
3、问题所有评论的页面https://www.zhihu.com/question/47684665,这个页面就是终极页面,小虫子能爬到这,就算功德圆满了。
enter description here

把重要的信息封装起来

创建Zhihu类,给Zhihu的构造器一个url参数,这样只要判定url是终极页面的地址,就可以爬取所要的一切信息。怎么判定呢,交给getRealUrl()方法。
其中还有一些正则表达式书写,这就要对网页的源码分析。问题的标题可以用“zh-question-title.+?>.+?>(.+?)<”,只要抓住关键信息,这里就是zh-question-title
enter description here
其他的也是一样,如法炮制。
enter description here问题的描述,这边要注意,有时候不止一个描述,所以正则表达式要涵盖所有的描述语句。
enter description here评论者的名字
注意:对所有评论的获取不是一个正则能搞定的。暂且用/answer/content.+?<div.+?>(.*?)</div>替代。
enter description here
来…来…来,我又来贴代码了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
  /*
* 设计一个Zhihu封装类,来存储所有抓取到的对象
* 5个字段:问题标题、问题的描述、问题对应的链接、评论者的名字、所有的评论信息
*getRealUrl(String url)方法
*/

import java.util.ArrayList;
import java.util.regex.Pattern;
import java.util.regex.Matcher;
public class Zhihu {
public String question;// 问题的标题
public String questionDescription;//对问题的描述
public String zhihuUrl;// 问题对应的链接
public ArrayList<String> answersName;//所有评论者的名字
public ArrayList<String> answers;// 存储所有回答的数组
// 带参数<String url>的构造器
public Zhihu(String url) {
question = "";
questionDescription="";
zhihuUrl = "";
answersName=new ArrayList<String>();
answers = new ArrayList<String>();
if(getRealUrl(url)){
System.out.println("该问题全部回答的页面地址="+zhihuUrl);
//根据该问题所在的页面地址,爬虫子页面所有内容
String allContent=Spider.ReadPage(zhihuUrl);
//抓取问题
Pattern questionPattern=Pattern.compile("zh-question-title.+?><h2.+?>.+?>(.+?)<");
Matcher questionMatcher=questionPattern.matcher(allContent);
if(questionMatcher.find())
question=questionMatcher.group(1);
//抓去问题描述
Pattern descriptionPattern=Pattern.compile("zh-question-detail.+?><div.+?>(.+?)</div>");
Matcher descriptionMatcher=descriptionPattern.matcher(allContent);
if(descriptionMatcher.find())
questionDescription=descriptionMatcher.group(1);
//抓取评论者的姓名
Pattern namePattern=Pattern.compile("data-author-name=\"(.+?)\"");
Matcher nameMatcher=namePattern.matcher(allContent);
Boolean nameFind=nameMatcher.find();
//不停地抓取问题页面上所有评论者的姓名
while(nameFind){
answersName.add(nameMatcher.group(1));
nameFind=nameMatcher.find();
}
//抓取所有回答
Pattern answersPattern=Pattern.compile("/answer/content.+?<div.+?>(.*?)</div>");
Matcher answerMatcher=answersPattern.matcher(allContent);
Boolean answerFind=answerMatcher.find();
while(answerFind){
answers.add(answerMatcher.group(1));
answerFind=answerMatcher.find();
}
}
}
/*
* 把链接:https://www.zhihu.com/question/46727998/answer/108099200
* 转变成:https://www.zhihu.com/question/46727998
* 后者才是所有回答的网页真实地址
*/

public boolean getRealUrl(String url){
Pattern linkPattern=Pattern.compile("question/(.+?)/");
Matcher linkMatcher=linkPattern.matcher(url);
if(linkMatcher.find()){
zhihuUrl="https://www.zhihu.com/question/"+linkMatcher.group(1);
return true;
}
return false;
}

@Override
public String toString() {
return "问题标题:" + question + "\n问题的描述:"+questionDescription+"\n链接:" + zhihuUrl +"\n评论者:"+answersName+ "\n回答:" + answers.size() + "\n";
}
}

于Spider类中添加GetZhihu()方法

抓取知乎发现页面上所有问题的链接地址,并返回该问题链接下的Zhihu类对象,每个问题对应一个Zhihu类对象,把这些Zhihu类对象放到数组中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    /*
* 抓取知乎发现页面上所有问题的链接地址,
* 并返回该问题链接下的Zhihu类对象,
* 每个问题对应一个Zhihu类对象,把这些Zhihu类对象放到数组中
*/

static ArrayList<Zhihu> GetZhihu(String content) {
ArrayList<Zhihu> res=new ArrayList<Zhihu>();
//埋好陷阱,等着掉下去
//形如<h2><a class="question_link" target="_blank" href="/question/46727998/answer/108099200">《守望先锋》有可能出手游吗?</a></h2>
//抓取 这部分,/question/46727998/answer/108099200
Pattern urlPattern=Pattern.compile("question_link.+?href=\"(.+?)\">");
Matcher questionMatcher=urlPattern.matcher(content);
//是否掉到陷阱里面
Boolean isFind = questionMatcher.find();
while(isFind){
//存储刚才定义的知乎对象,数组里面都是Zhihu对象
Zhihu zhihuTmp=new Zhihu("https://www.zhihu.com"+questionMatcher.group(1));//参数是问题的地址的完整链接
res.add(zhihuTmp);
//继续查找下一个匹配对象
isFind=questionMatcher.find();
}
return res;
}

运行测试

Main类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/*
* 使用爬虫来获取知乎发现页面上的内容
*/

import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
//知乎的发现页面
String url="https://www.zhihu.com/explore";
// 访问链接并获取页面内容
String content = Spider.ReadPage(url);
// 获取该页面的所有的知乎对象
ArrayList<Zhihu> myZhihu = Spider.GetZhihu(content);
// 打印结果
System.out.println(content);
System.out.println(myZhihu);
}
}

可以明显地看到运行的速度变慢了,这是因为,比如知乎发现页面上有15个问题,那么小虫子就要爬到这15个网页摸数据,花的时间自然多了。
enter description here
好了,我要开始跑了,就要跑了。。。。
enter description here
还是差点什么,貌似问题的描述抓得不正确,明天继续!

热评文章