内容简介:Java 爬知乎某个问题下的所有图片
网上有许多关于知乎的爬虫,但都是用 Python 来实现的,由于我的主语言是 Java 所以想用 Java 来实现下。
本次用到了一个国人开发的优秀的爬虫框架: WebMagic 。
思路
首先打开知乎的一个问题 https://www.zhihu.com/question/43551423 ,然后打开 FireFox 的 F12 控制台,然后发现知乎的问题需要翻页,且是无刷新的请求,那必然是 AJAX 请求了。
筛选一下,发现一个可疑的东西,以 answers 开头的,应该就是回答内容的请求了。
观察一下这个请求: https://www.zhihu.com/api/v4/questions/43551423/answers?include=data[*].is_normal,admin_closed_comment,reward_info,is_collapsed,annotation_action,annotation_detail,collapse_reason,is_sticky,collapsed_by,suggest_edit,comment_count,can_comment,content,editable_content,voteup_count,reshipment_settings,comment_permission,created_time,updated_time,review_info,question,excerpt,relationship.is_authorized,is_author,voting,is_thanked,is_nothelp,upvoted_followees;data[*].mark_infos[*].url;data[*].author.follower_count,badge[?(type=best_answerer)].topics&offset=0&limit=20&sort_by=default
可以发现其中的 43551423 代表的就是该问题的 ID 号,请求参数中的参数 include 为请求的内容, offset 为偏移量(表示从多少页开始请求), limit 为本页的答案数量(最大为 20), sort_by 为 排序 方式。其实我们只需要关注 offset 即可,其他的默认就好。
有了思路以后,我们先用 Postman 来测试一下:
但是得到了这样的结果,他告诉我 AuthenticationInvalidRequest 认证无效,看来是需要添加认证,那我们再去浏览器看下,刚才的请求头中还有什么信息传递了过去,然后发现了这个东东:
我们把这个添加到请求头中,发现可以正常得到数据了:
观察一下这个数据发现,这是 20 条用户的回答,因为我们请求的参数 limit 为 20 ,所以这里为 20 条,那么我们可以根据这个来判断是否翻页结束,每次 limit 都自增 20,直到得到的数据不满 20 条,则代表翻页结束,停止爬取。
然后得到了数据,就开始解析图片下载地址吧:
img 元素就是我们要爬取的图片,可以看到 data-original 属性的内容与 src 属性的内容都是图片的地址,但验证后发现,src 可能是缩略图,所以我们还是选择 data-original 属性的图片地址。
得到图片地址后,下载到本地就可以了,直接看代码吧!
上代码!
package com.yfzz.zhihu3 ;
import us.codecraft.webmagic.Page;
import us.codecraft.webmagic.Site;
import us.codecraft.webmagic.Spider;
import us.codecraft.webmagic.processor.PageProcessor;
import us.codecraft.webmagic.selector.Html;
import us.codecraft.webmagic.selector.JsonPathSelector;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashSet;
import java.util.List;
import java.util.UUID;
public class ZhihuQuestion implements PageProcessor {
private Site site = Site.me().setSleepTime(2000)
.setCycleRetryTimes(5)
.setRetryTimes(5)
.setUserAgent("Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36")
.addHeader("authorization", "你的认证信息")
.setCharset("UTF-8");
private static String questionID = "37787176"; // 要爬取的问题 ID 号
private static String filePath = "e://zhihu"; // 文件存放路径,程序会自动在此路径后添加一级目录为问题标题
private static int offset = 0; // 偏移量,表示从第 n 个答案开始获取,limit 表示获取多少个(上限为20)
private static int count = 0; // 下载到的图片总数
@Override
public void process(Page page) {
Html html = page.getHtml();
// 得到当前页的所有答案的 ID 号,主要用处是为了判断是否到页尾。
List<String> idList = new JsonPathSelector("$.data[*].id").selectList(page.getRawText());
String title = new JsonPathSelector("$.data[*].question.title").selectList(page.getRawText()).get(0);
int getSize = idList.size(); // 当前页获取到的回答个数
if (getSize == 20) {
// 将下一页添加到队列中
offset += 20;
String url = "https://www.zhihu.com/api/v4/questions/" + questionID + "/answers?include=data%5B%2A%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cupvoted_followees%3Bdata%5B%2A%5D.mark_infos%5B%2A%5D.url%3Bdata%5B%2A%5D.author.follower_count%2Cbadge%5B%3F%28type%3Dbest_answerer%29%5D.topics&offset=" + offset + "&limit=20&sort_by=default";
page.addTargetRequest(url);
}
// 根据正则匹配图片地址并去除特殊字符。
// 例: 正则得到的图片链接 : \"https://pic2.zhimg.com/v2-94ae015b8e1bd2e0dc9bdd7d7da6d7ed_r.jpg\" 需要去除特殊字符 "
List<String> imgList = html.regex("data-original=\"(.*?)\"").replace("\\\\"", "").all();
// 图片链接去重复
HashSet<String> set = new HashSet<String>(imgList);
count += set.size();
System.out.println("正在下载第" + offset + "-" + (offset + getSize) + "个回答的图片,当前页图片数量为:" + set.size() + ",目前总图片数量:" + count);
for (String url : set) {
String fileName = url.substring(url.lastIndexOf('/') + 1, url.length());
try {
String savePath = filePath + "/" + title + "_" + questionID; // 下载路径:指定路径/标题/问题ID
downloadPicture(url, savePath, fileName);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static void downloadPicture(String urlString, String savePath, String filename) throws Exception {
File file = new File(savePath + File.separator + filename);
if (!new File(savePath).exists()) {
System.out.println("下载目录不存在,已创建:" + savePath);
new File(savePath).mkdirs();
}
if (file.exists()) {
System.out.println("文件已存在,跳过该文件:" + file.getName());
return;
}
OutputStream os = new FileOutputStream(file);
// 构造URL
URL url = new URL(urlString);
// 打开连接
URLConnection con = url.openConnection();
//设置请求超时为5s
con.setConnectTimeout(5 * 1000);
// 输入流
InputStream is = con.getInputStream();
// 1K的数据缓冲
byte[] bs = new byte[1024];
// 读取到的数据长度
int len;
// 开始读取
while ((len = is.read(bs)) != -1) {
os.write(bs, 0, len);
}
// 完毕,关闭所有链接
os.close();
is.close();
}
@Override
public Site getSite() {
return site;
}
public static void main(String[] args) throws Exception {
Spider.create(new ZhihuQuestion()).
thread(1).
addUrl("https://www.zhihu.com/api/v4/questions/" + questionID + "/answers?include=data%5B%2A%5D.is_normal%2Cadmin_closed_comment%2Creward_info%2Cis_collapsed%2Cannotation_action%2Cannotation_detail%2Ccollapse_reason%2Cis_sticky%2Ccollapsed_by%2Csuggest_edit%2Ccomment_count%2Ccan_comment%2Ccontent%2Ceditable_content%2Cvoteup_count%2Creshipment_settings%2Ccomment_permission%2Ccreated_time%2Cupdated_time%2Creview_info%2Cquestion%2Cexcerpt%2Crelationship.is_authorized%2Cis_author%2Cvoting%2Cis_thanked%2Cis_nothelp%2Cupvoted_followees%3Bdata%5B%2A%5D.mark_infos%5B%2A%5D.url%3Bdata%5B%2A%5D.author.follower_count%2Cbadge%5B%3F%28type%3Dbest_answerer%29%5D.topics&offset=0&limit=20&sort_by=default")
.run();
}
}
爬取结果展示
前 20 个回答就有 1050 张图片!!!
然后我用浏览器打开了这个网页,发现……
竟然有 9559 个回答,我的天,我还是停了吧,估计下载完这些,我这小硬盘都要满了,然后看了下已经下载完成的。
嗯,按照这个情况,下载完这些,估计上 10G 了,那么知乎这么多钓鱼贴,咳咳,自己理解吧。
总结
这只是一个简单的例子,为了防止给知乎的服务器带了太大的压力,这里我的代码是写成了单线程的方式。后续我会再更新一些关于 Java 的爬虫以及详细思路,有什么问题可以在评论里给我留言。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Trading and Exchanges
Larry Harris / Oxford University Press, USA / 2002-10-24 / USD 95.00
This book is about trading, the people who trade securities and contracts, the marketplaces where they trade, and the rules that govern it. Readers will learn about investors, brokers, dealers, arbit......一起来看看 《Trading and Exchanges》 这本书的介绍吧!