前言

背景: 某一天,拿着自己的手机看着技术文章,然而手机看技术文章,有时候确实蛋疼,因为一旦代码多起来,小屏幕看的还是眼花;又或者某一天觉得这一篇文章,觉得写的很棒棒哦,于是先收藏,打算过几天看,然后等我几天再次打开收藏的文章,卧X,居然被作者删了···;或者想对某个博主的文章进行分类···

于是就萌生了能不能爬下“微信公众号”文章,保存到电脑的想法

如今普天盖地的安利 Python ,虽然有着“人生苦短,我用 Python”一说,但我还是想在「爬虫」这方面支持一下我大 Java(好吧,其实自己折腾一番,还是写着 Java 舒服,平时写 python 还是少)

一、抓包

关于手机抓包(这里指 Android 手机),推荐使用 Fiddler 工具来抓包,Fiddler 自行去下载。

划重点:请确保电脑和手机连接在同一局域网的同一个 WiFi,别又说怎么抓不到包

1.查询电脑当前 IP

Win + R (快捷键),打开【运行】窗口,然后输入 cmd 回车,弹窗命令窗口,紧接着输入:ipconfig

记着 ip,一会配置手机 WiFi,不会配置的可以看 Fiddler 官网这篇文章:https://docs.telerik.com/fiddler/Configure-Fiddler/Tasks/ConfigureForAndroid

打开手机 WiFi 管理,显示 WiFi 的高级选项,设置代理服务器为手工,代理主机名为刚刚电脑 IPv4 地址:192.168.0.XXX ,代理服务器端口默认设置为:8888

2.手机安装 Fiddler 证书

因为微信的网络请求为 HTTPS ,安全性高,所以 Fiddler 需要在手机端安装它的信任证书,才能抓到微信的请求(比喻:Fiddler 充当代理人、中间商,在建立 https 的过程搞事情,瞒天过海,以获取信任)。

操作如下:
  1. 手机浏览器打开:http://ipv4.fiddler:8888/
  2. 下载证书 FiddlerRoot Certificate
  3. 手机安装这个证书,安装过程可能需要设置屏幕密码
  4. 打开 【Fiddler】-【Tools】-【HTTPS】,勾选 Capture HTTPS traffic

3.HttpCanary

除了 Fiddler 之外,这边推荐安卓另一个抓包工具:HttpCanary,安装 apk 到手机,也可实现实时抓包。

传送门:手机抓包+注入黑科技HttpCanary——最强大的Android抓包注入工具

二、爬虫

配置好抓包工具之后,打开某公众号,切换历史文章消息,然后点击更多消息,此时观察 Fiddler 抓包情况。

每次抓包前,建议先清空历史抓包数据,然后在执行操作,这样方便定位链接。

于是我们可以很轻易的拿到微信公众号获取文章接口地址:

https://mp.weixin.qq.com/mp/profile_ext

切换到 WebForms 选项卡,可以看到 Get 请求下的参数信息,后面我们模拟请求,照着写就 ok 了(Get请求参数,可以写在链接里面)

在上面这个图,我框了几个重要参数的,这几个参数涉及到微信服务端的校验相关操作,所以在复制的时候,记得不要搞错了,否则会提示 session 错误。

我个人试错发现,每次爬一个新的公众号,只需对应修改这 4 个参数即可:__biz、appmsg_token、pass_ticket、wap_sid2

如何爬取所有文章呢? 做过手机客户端的童鞋,应该知道我们用 RecyclerviewListView下拉刷新或者上拉加载更多的时候,接口一般需要配置 nextpage 的参数吧,对应微信文章接口就是:offset 参数(理解为偏移量),count参数(理解为每次加载的数目)。

举例:我设置 offset 为0,count 为 10,那么第一页数据就加载10条,那第二页的起始点就应该是 offset = 10count 不作修改依旧为 10。希望大家能理解我的例子,相信这个不难

1.构建请求,递归调用

请求这东西,当然是 okhttp 来啦,引用相关 jar 包或者 gradle 依赖。说明一下:User-Agent 使用 Fiddler 抓取的值,以模拟手机客户端的请求。核心代码如下:

       String url = "https://mp.weixin.qq.com/mp/profile_ext?action=getmsg&__biz=%s&f=json&offset=%d&count=10&is_ok=1&scene=126&uin=777&key=777&pass_ticket=%s&wxtoken=&appmsg_token=%s&f=json ";url = String.format(url, MyClass.__biz, startIndex, MyClass.pass_ticket, MyClass.appmsg_token);//        System.out.println(url);String cookie = "rewardsn=; wxtokenkey=777; wxuin=777750088; devicetype=android-26; version=2700033c; lang=zh_CN; pass_ticket=%s; wap_sid2=%s";cookie = String.format(cookie, MyClass.pass_ticket, MyClass.wap_sid2);Request request = new Request.Builder().url(url).get().addHeader("Host", "mp.weixin.qq.com").addHeader("Connection", "keep-alive").addHeader("User-Agent", "Mozilla/5.0 (Linux; Android 8.0.0; SM-G9500 Build/R16NW; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/044704 Mobile Safari/537.36 MMWEBID/8994 MicroMessenger/7.0.3.1400(0x2700033C) Process/toolsmp NetType/WIFI Language/zh_CN").addHeader("Accept-Language", "zh-CN,zh-CN;q=0.9,en-US;q=0.8").addHeader("X-Requested-With", "XMLHttpRequest").addHeader("Cookie", cookie).addHeader("Accept", "*/*").build();Response response = okHttpClient.newCall(request).execute();if (response.isSuccessful()) {String body = response.body().string();JSONObject jo = new JSONObject(body);if (jo.getInt("ret") == 0) {currentTimes++;System.out.println("当前是第" + currentTimes + "次");String general_msg_list = jo.getString("general_msg_list");general_msg_list = general_msg_list.replace("\\/", "/");// json 解析JSONObject jo2 = new JSONObject(general_msg_list);JSONArray msgList = jo2.getJSONArray("list");for (int i = 0; i < msgList.length(); i++) {JSONObject j = msgList.getJSONObject(i);JSONObject msgInfo = j.getJSONObject("comm_msg_info");long datetime = msgInfo.getLong("datetime");SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");String date = sdf.format(new Date(datetime * 1000));if (j.has("app_msg_ext_info")) {JSONObject app_msg_ext_info = j.getJSONObject("app_msg_ext_info");JSONArray multi_app_msg_item_list = app_msg_ext_info.getJSONArray("multi_app_msg_item_list");if (multi_app_msg_item_list.length() > 0) {//多图文 do nothing} else {String content_url = app_msg_ext_info.getString("content_url");String title = app_msg_ext_info.getString("title");int copyright_stat = app_msg_ext_info.getInt("copyright_stat");String record = date + "-@@-" + title + "-@@-" + content_url;System.out.println(record);datas.add(record);}} else {System.out.println("非图文推送");}}// can_msg_continue 来判断是否还有下一页数据if (jo.getInt("can_msg_continue") == 1) {Thread.sleep(1000);startIndex = jo.getInt("next_offset");execute();} else {System.out.println("爬取完成!");// 完成之后,保存结果saveToFile();}} else {System.out.println("无法获取文章,参数错误");}}

2.保存文章信息

好像代码也不是特别多,哈哈,然后把爬取的数据保存到一个 txt 文本文件里面,我这边用的格式是: 时间-@@-标题-@@-链接后面方便使用“-@@-”分割字符串),当然你也可以连接 Mysql,来存储信息,我就偷懒了,没搞了。

三、Html 转 Pdf

既然拿到每一篇文章的 Url 了,那保存成 Html 不是很 easy 的事情吗,但是如何将 html convert Pdf 呢?

1.wkhtmltopdf 工具

1.1下载 wkhtmltopdf 并安装

传送门: https://wkhtmltopdf.org/,注意:系统版本的选择,我这边是 Windows 系统

1.2配置环境变量

如果你没有配置系统环境变量的话,就需要到 wkhtmltopdf 的安装目录下的 bin 文件夹下面,去执行命令

1.3如何使用

例如:你想把 Google 网页转成 pdf

 wkhtmltopdf http://google.com google.pdf

2.解决 wkhtmltopdf 保存图片丢失问题

通过 wkhtmltopdf 保存 pdf 的时候,存在网络图片丢失的问题,也就是不显示图片,那如何解决这个问题呢?通过替换 html 中,img 标签的 data-src 和 src 的属性值,由 http 链接改为本地路径即可

思路:请求文章 url,获取 html 信息,通过 jsoup 解析 html,然后通过选择器选择 img 标签,接着获取 imgdata-src 的属性值(图片地址),然后遍历下载图片到本地,下载图片成功之后,通过 jsoup 提供的方法,修改该 imgdata-src 的属性值,替换原先的 html 信息。核心代码如下:

Jsoup介绍:html解析神器


Request request = new Request.Builder().url(url).get().build();
Response response = okHttpClient.newCall(request).execute();if (response.isSuccessful()) {String html = response.body().string();//                System.out.println(html);Document doc = Jsoup.parse(html);//找到图片标签Elements img = doc.select("img");for (int i = 0; i < img.size(); i++) {// 图片地址String imgUrl = img.get(i).attr("data-src");if (imgUrl != null && !imgUrl.equals("")) {Request request2 = new Request.Builder().url(imgUrl).get().build();Response execute = okHttpClient.newCall(request2).execute();if (execute.isSuccessful()) {String imgPath = imgDir + MD5Utils.MD5Encode(imgUrl, "") + ".png";File imgFile = new File(imgPath);if (!imgFile.exists()) {// 下载图片InputStream in = execute.body().byteStream();FileOutputStream ot = new FileOutputStream(new File(imgPath));BufferedOutputStream bos = new BufferedOutputStream(ot);byte[] buf = new byte[8 * 1024];int b;while ((b = in.read(buf, 0, buf.length)) != -1) {bos.write(buf, 0, b);bos.flush();}bos.close();ot.close();in.close();}//重新赋值为本地路径img.get(i).attr("data-src", imgPath);img.get(i).attr("src", imgPath);//导出 htmlhtml = doc.outerHtml();}execute.close();}}String htmlPath = dirPath + fileName + ".html";final File f = new File(htmlPath);if (!f.exists()) {Writer writer = new FileWriter(f);BufferedWriter bw = new BufferedWriter(writer);bw.write(html);bw.close();writer.close();}// 转换HtmlToPdf.convert(htmlPath, destPath);// 删除html文件if (f.exists()) {f.delete();}response.close();
}

3.转换成 PDF

/*** html转pdf*/
public static boolean convert(String srcPath, String destPath) {StringBuilder cmd = new StringBuilder();cmd.append("wkhtmltopdf");cmd.append(" ");cmd.append("--enable-plugins");cmd.append(" ");cmd.append("--enable-forms");cmd.append(" ");cmd.append(" \"");cmd.append(srcPath);cmd.append("\" ");cmd.append(" ");cmd.append(destPath);System.out.println(cmd.toString());boolean result = true;try {Process proc = Runtime.getRuntime().exec(cmd.toString());HtmlToPdfInterceptor error = new HtmlToPdfInterceptor(proc.getErrorStream());HtmlToPdfInterceptor output = new HtmlToPdfInterceptor(proc.getInputStream());error.start();output.start();proc.waitFor();} catch (Exception e) {result = false;e.printStackTrace();}return result;
}

获取终端输入输出信息,上面代码的 HtmlToPdfInterceptor

public class HtmlToPdfInterceptor extends Thread {private InputStream is;public HtmlToPdfInterceptor(InputStream is) {this.is = is;
}@Override
public void run() {try {InputStreamReader isr = new InputStreamReader(is, "utf-8");BufferedReader br = new BufferedReader(isr);String line = null;while ((line = br.readLine()) != null) {System.out.println(line.toString()); //输出内容}} catch (IOException e) {e.printStackTrace();}}

wkhtmltopdf 的转换过程速度比较慢,建议开多个线程搞,我是 5 个线程去转换,最后看一下成果图(python 党别喷代码量哈,求放过~)

小结

感谢您的阅读,如有不对的地方,还请指出修正!文中不理解的地方,可加 qq 交流:1029226002。

(Java篇)爬取微信公众号文章并保存为 PDF 格式相关推荐

  1. python爬取正确但不出文件_使用Python爬取微信公众号文章并保存为PDF文件(解决图片不显示的问题)...

    前言 第一次写博客,主要内容是爬取微信公众号的文章,将文章以PDF格式保存在本地. 爬取微信公众号文章(使用wechatsogou) 1.安装 pip install wechatsogou --up ...

  2. python批量保存网页为pdf_爬取微信公众号文章并保存为PDF文件(Python方法)

    {title} {content_info['content_html']}

  3. python爬取微信公众号文章

    爬取微信公众号文章 获取微信公众号的url 获取每一篇文章的url ​ 选择一个公众号进入,选择一个目录进入后点复制链接,然后去浏览器打开.按F12打开检查的模式,在Console中输入$x('标签路 ...

  4. python爬取微信公众号文章(包含文章内容和图片)

    之前虽然做过网页爬取,但微信爬取一直没做过,因为我一直不知道网页可以进微信公众平台,只用过微信客户端进微信公众号.既然可以通过网页进微信公众平台,那么爬取微信公众号文章就流程上就没太多难度了. 自己在 ...

  5. python爬虫能爬取微信密码吗_爬虫如何爬取微信公众号文章

    下篇文章:python爬虫如何爬取微信公众号文章(二) 下下篇连接python爬虫如何实现每天爬取微信公众号的推送文章 因为最近在法院实习,需要一些公众号的数据,然后做成网页展示出来便于查看,之前我倒 ...

  6. Python爬取微信公众号文章、点赞数

    代码还是热乎的,只要你细心一步步的慢慢调试,绝壁没问题 前期准备 订阅号: Python: Fiddler: 微信账号: 流程 使用用微信公众号生成cookie 使用Fiddler抓取微信公众号数据, ...

  7. python 爬取微信公众号文章(selenium+webdriver)

    """通过搜狗搜索中的微信搜索入口爬取微信公众号文章(selenium) """ import re import os import js ...

  8. 记一次企业级爬虫系统升级改造(四):爬取微信公众号文章(通过搜狗与新榜等第三方平台)

    首先表示抱歉,年底大家都懂的,又涉及SupportYun系统V1.0上线.故而第四篇文章来的有点晚了些~~~对关注的朋友说声sorry! SupportYun系统当前一览: 首先说一下,文章的进度一直 ...

  9. 记一次企业级爬虫系统升级改造(四):爬取微信公众号文章(通过搜狗与新榜等第三方平台)...

    首先表示抱歉,年底大家都懂的,又涉及SupportYun系统V1.0上线.故而第四篇文章来的有点晚了些~~~对关注的朋友说声sorry! SupportYun系统当前一览: 首先说一下,文章的进度一直 ...

最新文章

  1. 从fig文件中读取数据
  2. jQuery on()方法绑定动态元素的点击事件无效
  3. 操作系统外壳(shell)
  4. OpenGL 混合Blending
  5. [蓝桥杯][2015年第六届真题]生命之树(树形dp)
  6. Java web(2012/2/22)
  7. Linux中mongodb定时远程备份
  8. vfp语言属于第代计算机语言,计算机等级考试VFP教程:第二章VFP语言基础
  9. 图解物联网---物联网基础知识
  10. 我又拖后腿了, 2月全国程序员平均工资13716元!
  11. 东北大学《材料现代研究方法》复习题及答案
  12. 计算机五笔是什么时候学的吗,现在还有人学五笔么
  13. windows vcpkg下载慢
  14. Spring Cloud Netflix框架最基础的五大组件介绍
  15. win10桌面右键一直转圈是什么原因
  16. Linux bpf 1.1、BPF内核实现
  17. RTC月度小报5月 |教育aPaaS灵动课堂升级、抢先体验VUE版 Agora Web SDK、声网Agora与HTC达成合作
  18. 自学php多久可以工作_php自学要多久,学php难吗,多久能学会?
  19. 期许2016 迎新春技术分享沙龙总结
  20. ipad iphone开发_如何在iPhone或iPad上使用Adobe Flash

热门文章

  1. QQ群数据的切割与归并(数据分析案例四)
  2. scrcpy—Android投屏神器
  3. 未来的计算机 教案,精选计算机教案四篇
  4. 机器学习:【1】tensorflow2.0GPU版本在windows10环境下的安装
  5. 树莓派学习笔记003-超声波距离传感器
  6. ssm电影院购票系统、
  7. java程序实现wifi连接打印_Android下通过wifi调用打印机打印,Java代码片段分享,
  8. php上传word文档添加水印
  9. 单调队列 → 常用于动态规划问题的优化
  10. 一些简单的MATLAB数学实验