内容简介: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 的爬虫以及详细思路,有什么问题可以在评论里给我留言。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 一次解决所有代理问题
- 一文Get所有 Redis 性能问题分析手段
- 容器编排无法解决微服务的所有问题,你还需要服务网格
- Rust语言恰巧是一门解决了Go语言所有问题的语言
- HHVM 4.56.6 至 4.105.1 发布,修复所有版本的安全问题
- 你要偷偷学会排查线上 CPU 飙高的问题,然后惊艳所有人
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。