本文章为lonter首创,只发布在csdn平台,严禁转载
这几天接到任务,需要开发一个微信榜单的功能,因此需要采集微信公众号文章的阅读数,点赞数和评论数,榜单内的微信公众号有一百多个,每个月出一次榜单。
接到这个任务,我开始研究如何抓取微信阅读数,点赞数和评论数,通过大量参考网上的技术文章,最终确定了我所使用的方案:使用Fiddler进行采集
本文章为lonter首创,只发布在csdn平台,严禁转载
第一步:设置Fiddler
如图设置,此处为设置Fiddler支持https
本文章为lonter首创,只发布在csdn平台,严禁转载
第二步:设置Fiddler脚本
Fiddler工具打开后,选择Rules ->Customize Rules打开Fiddler ScriptEditor编辑器,编辑器如下:
这里我们只需要了解OnBeforeResponse方法,本方法为在http请求返回给客户端之前执行的方法,我们主要在本方法内进行脚本的编写
本文章为lonter首创,只发布在csdn平台,严禁转载
第三步:选择性截取responsebody存储到文本中
研究各个请求,找到返回点赞数与评论的请求,具体请求如图:
然后开始在Fiddler ScriptEditor的方法中编写具体的存储脚本:
// 首先判断请求域名是否是自己感兴趣的,以及URL中是否含有自己感兴趣的特征字符串。如果是,则将该请求的URL和QueryString记录到日志文件 "c:/fiddler-token.log"中。
if (oSession.HostnameIs("mp.weixin.qq.com") && oSession.uriContains("https://mp.weixin.qq.com/mp/getappmsgext")){
var filename = "C:/fiddler-token.log";
var curDate = new Date();
var logContent = "[" + curDate.toLocaleString() + "] " + oSession.PathAndQuery + "\r\n"+oSession.GetResponseBodyAsString()+"\r\n";
var sw : System.IO.StreamWriter;
if (System.IO.File.Exists(filename)){
sw = System.IO.File.AppendText(filename);
sw.Write(logContent);
}
else{
sw = System.IO.File.CreateText(filename);
sw.Write(logContent);
}
sw.Close();
sw.Dispose();
}
本代码功能为将涉及阅读数点赞数的数据存储到文本中,结果如图:
本文章为lonter首创,只发布在csdn平台,严禁转载
第四步:篡改公众号文章页面的js代码,让页面自动按照你的意思跳转
由于本功能可能涉及灰色地带,so,声明一下,不要用来干坏事!!!
我们先来看看公众号文章的主页面:
很明显,每段js脚本都是以 script nonce=”1007993124” 开头,而nonce字段的作用是防止xxs的,如果js的nonce与原来的不符,js是不会被执行的,因此,需要在脚本编写上费点功夫,具体的逻辑代码如图:
这段js加载后,保存一下Fiddler ScriptEditor,然后你点击微信公众号文章,你回在Fiddler中看到如下:
然后,你回发现页面会自动的跳转
本文章为lonter首创,只发布在csdn平台,严禁转载
第五步:开发任务获取页面
我们需要开发一个微信的中转页面,这个页面会从后台获取一篇微信公众号文章,然后让微信浏览器打开
具体html如下:
<html>
<head>
<meta charset="utf-8">
</head>
<body>
<script src="./js/hm.js"></script>
<script src="./js/jquery-3.2.1.min.js"></script>
<script language="javascript" >
window.onload=function(){
nextdoor();
}
function nextdoor(){
var taskid=GetQueryString("taskid")
var ob={task:taskid};
$.ajax({
type: "POST",
url: "rest/wxCrawler/wxTask",
contentType: "application/json; charset=utf-8",
data: JSON.stringify(ob),
dataType: "json",
success: function (message) {
var url=message.url;
var taskid=message.task;//每个微信客户端的id,这个id应该在后端自动生成
if(url==("http://127.0.0.1:8080/Externalservice/test.html?taskid="+taskid))
{
setTimeout(function(){window.location="http://127.0.0.1:8080/Externalservice/test.html?taskid="+taskid;},10000);
}else
{
//alert(url+"&taskid="+taskid);
window.location=url+"&taskid="+taskid+"#rd";
}
},
error: function (message) {
alert("提交数据失败");
}
});
}
function GetQueryString(name)
{
var reg = new RegExp("(^|&)"+ name +"=([^&]*)(&|$)");
var r = window.location.search.substr(1).match(reg);
if(r!=null)return unescape(r[2]); return null;
}
</script>
<div align="center">
<p>阅读刷新中转页面,页面正在跳转中...</p>
<p>如一直刷新本页面,则一直等待后台分配任务</p>
</div>
</body>
</html>
至于后端接口,想必很多人都能写吧,我就只搞一部分出来:
package test.springmvc;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import com.mangofactory.swagger.plugin.EnableSwagger;
import com.wordnik.swagger.annotations.ApiOperation;
import net.sf.json.JSONObject;
import test.springmvc.Artmodel.WxTask;
import test.springmvc.redis.JedisUtil;
/**
*
* @author Administrator
*
*/
@Controller
@EnableSwagger
@RequestMapping("/wxCrawler")
public class TopController {
private final static Logger logger = LoggerFactory.getLogger(TopController.class);
JedisUtil ju=new JedisUtil();
@ApiOperation(value = "微信任务调度接口", notes = "notes", httpMethod = "POST", produces = MediaType.APPLICATION_JSON_VALUE)
@RequestMapping(value = "wxTask", method = RequestMethod.POST)
@ResponseBody
// 使用了@RequestBody,不能在拦截器中,获得流中的数据,再json转换,拦截器中,也不清楚数据的类型,无法转换成java对象
// 只能手动调用方法
public String WeixinTask(@RequestBody WxTask wt) {
String task=wt.getTask();
byte[] redisKey= task.getBytes();//队列名称
byte[] bys=ju.rpop(redisKey);
if(bys==null)
{
JSONObject json=new JSONObject();
json.put("url", "http://127.0.0.1:8080/Externalservice/test.html?taskid="+task);
json.put("task", task);
return json.toString();
}else
{
String info=new String(bys);
JSONObject json=JSONObject.fromObject(info);
String url=json.getString("url");
url=url.replace("#rd", "");
json.put("url", url);
json.put("task", task);
return json.toString();
}
}
}
这段java和js的主要特点是可以多任务的进行分布式抓取,到这里,已经完成了所有的开发
你只需要写入几百几千的公众号文章链接,然后用微信打开http://localhost:8080/Externalservice/test.html?taskid=xxxxxl类似这样的中转页,你就会发现微信浏览器不停的跳转
本文章为lonter首创,只发布在csdn平台,严禁转载
第六步:解析Fiddler存储下来的文本
package com.crawler.top;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import com.mysql.jdbc.UpdatableResultSet;
import com.util.DBUtil;
import net.sf.json.JSONObject;
/**
* 读取Fiddler写入的内容,并将结果写入数据库
* @author Administrator
*
*/
public class ReaderTxt {
DBUtil dbu=new DBUtil();
public static void main(String[] args)
{
ReaderTxt rt=new ReaderTxt();
ArrayList<String> list=rt.InitTxt();
for(int i=0;i<list.size();i+=2)
{
String urlmsg=list.get(i);
int start=urlmsg.indexOf("&sn=")+4;
urlmsg=urlmsg.substring(start);
int end=urlmsg.indexOf("&");
urlmsg=urlmsg.substring(0, end);
String info=list.get(i+1);
JSONObject json=JSONObject.fromObject(info);
if(json.has("appmsgstat"))
{
JSONObject appmsgstat=json.getJSONObject("appmsgstat");
int read_num=appmsgstat.getInt("read_num");
int like_num=appmsgstat.getInt("like_num");
rt.updata(urlmsg, read_num, like_num);
}
}
rt.dbu.destroy();
}
/**
* 更新数据库中的文章数据
* @param urlmsg
* @param read_num
* @param like_num
*/
public void updata(String urlmsg,int read_num,int like_num)
{
String sql="update task_info set readers="+read_num+", attitudes="+like_num+" where url like '%"+urlmsg+"%'";
try {
dbu.Update(sql);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public ArrayList<String> InitTxt()
{
ArrayList<String> list=new ArrayList<String>();
try { // 防止文件建立或读取失败,用catch捕捉错误并打印,也可以throw
/* 读入TXT文件 */
String pathname = "C:\\fiddler-token.log"; // 绝对路径或相对路径都可以,这里是绝对路径,写入文件时演示相对路径
File filename = new File(pathname);
InputStreamReader reader = new InputStreamReader(
new FileInputStream(filename),"utf-8"); // 建立一个输入流对象reader
BufferedReader br = new BufferedReader(reader); // 建立一个对象,它把文件内容转成计算机能读懂的语言
String line = "";
while (line != null) {
line = br.readLine(); // 一次读入一行数据
if(line==null)
{
break;
}
list.add(line);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
}
这个挺简单的,嘿嘿
好啦,搞定啦
我的爬虫交流群:142351055