零基础写Java知乎爬虫之抓取知乎答案
前期我们抓取标题是在该链接下:
http://www.zhihu.com/explore/recommendations
但是显然这个页面是无法获取答案的。
一个完整问题的页面应该是这样的链接:
http://www.zhihu.com/question/22355264
仔细一看,啊哈我们的封装类还需要进一步包装下,至少需要个questiondescription来存储问题描述:
import java.util.arraylist;
public class zhihu {
public string question;// 问题
public string questiondescription;// 问题描述
public string zhihuurl;// 网页链接
public arraylist<string> answers;// 存储所有回答的数组
// 构造方法初始化数据
public zhihu() {
question = "";
questiondescription = "";
zhihuurl = "";
answers = new arraylist<string>();
}
@override
public string tostring() {
return "问题:" + question + "\n" + "描述:" + questiondescription + "\n"
+ "链接:" + zhihuurl + "\n回答:" + answers + "\n";
}
}
我们给知乎的构造函数加上一个参数,用来设定url值,因为url确定了,这个问题的描述和答案也就都能抓到了。
我们将spider的获取知乎对象的方法改一下,只获取url即可:
static arraylist<zhihu> getzhihu(string content) {
// 预定义一个arraylist来存储结果
arraylist<zhihu> results = new arraylist<zhihu>();
// 用来匹配url,也就是问题的链接
pattern urlpattern = pattern.compile("<h2>.+?question_link.+?href=\"(.+?)\".+?</h2>");
matcher urlmatcher = urlpattern.matcher(content);
// 是否存在匹配成功的对象
boolean isfind = urlmatcher.find();
while (isfind) {
// 定义一个知乎对象来存储抓取到的信息
zhihu zhihutemp = new zhihu(urlmatcher.group(1));
// 添加成功匹配的结果
results.add(zhihutemp);
// 继续查找下一个匹配对象
isfind = urlmatcher.find();
}
return results;
}
接下来,就是在zhihu的构造方法里面,通过url获取所有的详细数据。
我们先要对url进行一个处理,因为有的针对回答的,它的url是:
http://www.zhihu.com/question/22355264/answer/21102139
有的针对问题的,它的url是:
http://www.zhihu.com/question/22355264
那么我们显然需要的是第二种,所以需要用正则把第一种链接裁切成第二种,这个在zhihu中写个函数即可。
// 处理url
boolean getrealurl(string url) {
// 将http://www.zhihu.com/question/22355264/answer/21102139
// 转化成http://www.zhihu.com/question/22355264
// 否则不变
pattern pattern = pattern.compile("question/(.*?)/");
matcher matcher = pattern.matcher(url);
if (matcher.find()) {
zhihuurl = "http://www.zhihu.com/question/" + matcher.group(1);
} else {
return false;
}
return true;
}
接下来就是各个部分的获取工作了。
先看下标题:
正则把握住那个class即可,正则语句可以写成:zm-editable-content\">(.+?)<
运行下看看结果:
哎哟不错哦。
接下来抓取问题描述:
啊哈一样的原理,抓住class,因为它应该是这个的唯一标识。
验证方法:右击查看页面源代码,ctrl+f看看页面中有没有其他的这个字符串。
后来经过验证,还真出了问题:
标题和描述内容前面的class是一样的。
那只能通过修改正则的方式来重新抓取:
// 匹配标题
pattern = pattern.compile("zh-question-title.+?<h2.+?>(.+?)</h2>");
matcher = pattern.matcher(content);
if (matcher.find()) {
question = matcher.group(1);
}
// 匹配描述
pattern = pattern
.compile("zh-question-detail.+?<div.+?>(.*?)</div>");
matcher = pattern.matcher(content);
if (matcher.find()) {
questiondescription = matcher.group(1);
}
最后就是循环抓取答案了:
初步暂定正则语句:/answer/content.+?<div.+?>(.*?)</div>
改下代码之后我们会发现软件运行的速度明显变慢了,因为他需要访问每个网页并且把上面的内容抓下来。
比如说编辑推荐有20个问题,那么就需要访问网页20次,速度也就慢下来了。
试验一下,看上去效果不错:
ok,那就先这样好了~下次继续进行一些细节的调整,比如多线程,io流写入本地等等。
附项目源码:
zhihu.java
import java.util.arraylist;
import java.util.regex.matcher;
import java.util.regex.pattern;
public class zhihu {
public string question;// 问题
public string questiondescription;// 问题描述
public string zhihuurl;// 网页链接
public arraylist<string> answers;// 存储所有回答的数组
// 构造方法初始化数据
public zhihu(string url) {
// 初始化属性
question = "";
questiondescription = "";
zhihuurl = "";
answers = new arraylist<string>();
// 判断url是否合法
if (getrealurl(url)) {
system.out.println("正在抓取" + zhihuurl);
// 根据url获取该问答的细节
string content = spider.sendget(zhihuurl);
pattern pattern;
matcher matcher;
// 匹配标题
pattern = pattern.compile("zh-question-title.+?<h2.+?>(.+?)</h2>");
matcher = pattern.matcher(content);
if (matcher.find()) {
question = matcher.group(1);
}
// 匹配描述
pattern = pattern
.compile("zh-question-detail.+?<div.+?>(.*?)</div>");
matcher = pattern.matcher(content);
if (matcher.find()) {
questiondescription = matcher.group(1);
}
// 匹配答案
pattern = pattern.compile("/answer/content.+?<div.+?>(.*?)</div>");
matcher = pattern.matcher(content);
boolean isfind = matcher.find();
while (isfind) {
answers.add(matcher.group(1));
isfind = matcher.find();
}
}
}
// 根据自己的url抓取自己的问题和描述和答案
public boolean getall() {
return true;
}
// 处理url
boolean getrealurl(string url) {
// 将http://www.zhihu.com/question/22355264/answer/21102139
// 转化成http://www.zhihu.com/question/22355264
// 否则不变
pattern pattern = pattern.compile("question/(.*?)/");
matcher matcher = pattern.matcher(url);
if (matcher.find()) {
zhihuurl = "http://www.zhihu.com/question/" + matcher.group(1);
} else {
return false;
}
return true;
}
@override
public string tostring() {
return "问题:" + question + "\n" + "描述:" + questiondescription + "\n"
+ "链接:" + zhihuurl + "\n回答:" + answers.size() + "\n";
}
}
spider.java
import java.io.bufferedreader;
import java.io.inputstreamreader;
import java.net.url;
import java.net.urlconnection;
import java.util.arraylist;
import java.util.regex.matcher;
import java.util.regex.pattern;
public class spider {
static string sendget(string url) {
// 定义一个字符串用来存储网页内容
string result = "";
// 定义一个缓冲字符输入流
bufferedreader in = null;
try {
// 将string转成url对象
url realurl = new url(url);
// 初始化一个链接到那个url的连接
urlconnection connection = realurl.openconnection();
// 开始实际的连接
connection.connect();
// 初始化 bufferedreader输入流来读取url的响应
in = new bufferedreader(new inputstreamreader(
connection.getinputstream(), "utf-8"));
// 用来临时存储抓取到的每一行的数据
string line;
while ((line = in.readline()) != null) {
// 遍历抓取到的每一行并将其存储到result里面
result += line;
}
} catch (exception e) {
system.out.println("发送get请求出现异常!" + e);
e.printstacktrace();
}
// 使用finally来关闭输入流
finally {
try {
if (in != null) {
in.close();
}
} catch (exception e2) {
e2.printstacktrace();
}
}
return result;
}
// 获取所有的编辑推荐的知乎内容
static arraylist<zhihu> getrecommendations(string content) {
// 预定义一个arraylist来存储结果
arraylist<zhihu> results = new arraylist<zhihu>();
// 用来匹配url,也就是问题的链接
pattern pattern = pattern
.compile("<h2>.+?question_link.+?href=\"(.+?)\".+?</h2>");
matcher matcher = pattern.matcher(content);
// 是否存在匹配成功的对象
boolean isfind = matcher.find();
while (isfind) {
// 定义一个知乎对象来存储抓取到的信息
zhihu zhihutemp = new zhihu(matcher.group(1));
// 添加成功匹配的结果
results.add(zhihutemp);
// 继续查找下一个匹配对象
isfind = matcher.find();
}
return results;
}
}
main.java
import java.util.arraylist;
public class main {
public static void main(string[] args) {
// 定义即将访问的链接
string url = "http://www.zhihu.com/explore/recommendations";
// 访问链接并获取页面内容
string content = spider.sendget(url);
// 获取编辑推荐
arraylist<zhihu> myzhihu = spider.getrecommendations(content);
// 打印结果
system.out.println(myzhihu);
}
}
以上就是抓取知乎答案的全部记录,非常的详尽,有需要的朋友可以参考下