前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 爬知乎某个问题下的所有图片

Java 爬知乎某个问题下的所有图片

作者头像
一份执着✘
发布2018-06-04 17:16:36
1.6K0
发布2018-06-04 17:16:36
举报

前言

网上有许多关于知乎的爬虫,但都是用 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 属性的图片地址。

得到图片地址后,下载到本地就可以了,直接看代码吧!

上代码!

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116

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); } // 根据正则匹配图片地址并去除特殊字符。 // 例: 正则得到的图片链接 : \&quot;https://pic2.zhimg.com/v2-94ae015b8e1bd2e0dc9bdd7d7da6d7ed_r.jpg\&quot; 需要去除特殊字符 &quot; List<String> imgList = html.regex("data-original=\"(.*?)\"").replace("\\\\&quot;", "").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 的爬虫以及详细思路,有什么问题可以在评论里给我留言。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2017-12-042,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 思路
  • 上代码!
  • 爬取结果展示
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档