抓取微信公众号全部文章,采用AnyProxy+Javascript+Java实现
git
https://gitee.com/saly/WechatSpider/blob/master/src/main/resources/spider.js#
/**
* 微信公众号爬虫, 抓取流程参考`README.MD`文档
*
* @author 最爱吃小鱼
*/
// 规则配置
var config = {
host: 'http://127.0.0.1:8080', // 服务器地址配置
crawlHistory: true, // 是否采集列表历史数据
crawlArticle: true, // 是否采集文章数据
crawlComment: true, // 是否采集评论数据
crawlLikeReadNum: true, // 是否采集文章的阅读量及点赞量
autoNextScroll: true, // 是否自动下拉采取数据
autoNextPage: true, // 是否自动文章翻页
autoPostData: true, // 是否提交数据到服务器
m: 3000, // 自动下拉的时间间隔 m ~ n 秒之间
n: 5000,
jumpInterval: 10, // 文章页跳转的时间间隔
saveContentType: 'html',// 微信文章保存内容的形式: html/text
localImg: true // 公众号的图片返回本地图片
}
var url = require('url');
var http = require('http');
var querystring = require('querystring');
var cheerio = require('cheerio');
var rp = require('request-promise');
var fs = require("fs");
var img = fs.readFileSync(__dirname + "/black.png")
module.exports = {
// 模块介绍
summary: '微信公众号爬虫',
// 发送请求拦截
*beforeSendRequest(requestDetail) {
if (!config.localImg) return null;
// 将请求图片变成本地图片,加快文章显示
if(/mmbiz\.qpic\.cn/i.test(requestDetail.url)){
const localResponse = {
statusCode: 200,
header: { 'Content-Type': 'image/png' },
body: img
};
return {
response: localResponse
};
}
},
// 发送响应前处理
*beforeSendResponse(requestDetail, responseDetail) {
try {
// 解析连接中的参数信息
var link = requestDetail.url;
// 历史页面第一页数据
if(/mp\/profile_ext\?action=home/i.test(link)){
if (!config.crawlHistory) {
return null;
}
// 取得响应内容
var serverResData = responseDetail.response.body.toString();
// 取得公众号唯一标识biz
var biz = getBizByLink(link);
// 取得微信公众号历史数据的第一页数据,包含公众号详情及最新的文章信息
var account = getAccount(biz, serverResData);
// 数据上传到服务器
serverPost(biz, account, '/spider/firstpage')
// 判断是否自动下拉请求数据
if (!config.autoNextScroll) {
return null;
}
// 根据返回的数据状态组装相应的自动滚动加载JS
var autoNextScrollJS = getAutoNextScrollJS();
// 修改返回的body内容,插入JS
var newResponse = Object.assign({}, responseDetail.response);
newResponse.body += autoNextScrollJS;
return {
response: newResponse
};
}
// 向下翻页的数据的AJAX请求处理
if(/mp\/profile_ext\?action=getmsg/i.test(link)){
if (!config.crawlHistory) {
return null;
}
var biz = getBizByLink(link);
var content = JSON.parse(responseDetail.response.body.toString());
content = JSON.parse(content.general_msg_list);
var articles = getArticles(biz, content.list);
serverPost(biz, articles, '/spider/nextpage');
return null;
}
// 文章页跳转
if (/\/s\?__biz/.test(link) || /mp\/appmsg\/show/.test(link)) {
if (!config.crawlArticle) {
return null;
}
var content = responseDetail.response.body.toString();
var article = getArticle(link, content);
let bb = async function() {
return await serverPost(article.biz, article, "/spider/updateArticleContent").then((nextLink) => {
if (nextLink == undefined || nextLink == null || nextLink == '') {
return null;
}
var autoNextPageMeta = getAutoNextPageMeta(nextLink);
console.log(nextLink);
// 修改返回的body内容,插入meta
var newResponse = Object.assign({}, responseDetail.response);
newResponse.body = content.replace('</title>', '</title>' + autoNextPageMeta);
return {
response: newResponse
};
});
}
return bb();
}
// 微信公众号的跟贴评论
if (/\/mp\/appmsg_comment/.test(link)){
if (!config.crawlComment) {
return null;
}
var comments = getComments(link, responseDetail);
if (comments.length > 0) {
serverPost(comments[0].biz, comments, '/spider/comment');
}
return null;
}
// 获取点赞量和阅读量
if (link.indexOf('getappmsgext') > -1) {
if (!config.crawlLikeReadNum) {
return null;
}
var article = getArticleOfReadNumAndLikeNum(link, responseDetail);
serverPost(article.biz, article, '/spider/updateArticleNum');
return null;
}
return null;
} catch (e) {
console.log("程序运行异常");
console.log(e);
throw e;
}
}
};
// 转义符换成普通字符
function escape2Html(str){
const arrEntities={'lt':'<','gt':'>','nbsp':' ','amp':'&','quot':'"'};
return str.replace(/&(lt|gt|nbsp|amp|quot);/ig,function(all,t){return arrEntities[t];});
}
/**
* 取得文章的详细信息, 通过biz,mid聚合主键找些此文章,更新文章内容
*
* @param link
* @param responseDetail
* @returns {{biz: *, mid: *, idx: *, content: (*|jQuery|string)}}
*/
function getArticle(link, content) {
var $ = cheerio.load(content, { decodeEntities: false });
var identifier = querystring.parse(url.parse(link).query);
var articleContent = config.saveContentType=='html' ? $('#js_content').html() : $('#js_content').text();
if (articleContent == '') {
return null;
}
return {
biz: identifier.__biz,
mid: identifier.mid,
idx: identifier.idx,
content: articleContent
}
}
/**
* 取得文章的阅读量、点赞量等相关文章信息
*
* @param link
* @param responseDetail
* @returns {{biz: *, mid: *, idx: *, readNum: *, likeNum: *}}
*/
function getArticleOfReadNumAndLikeNum(link, responseDetail) {
var identifier = querystring.parse(url.parse(link).query);
var content = JSON.parse(responseDetail.response.body.toString());
return {
biz: identifier.__biz,
mid: identifier.mid,
idx: identifier.idx,
readNum: content.appmsgstat.read_num,
likeNum: content.appmsgstat.like_num
}
}
/**
* 从URL中解析出biz
* @param link
* @returns {biz}
*/
function getBizByLink(link) {
var identifier = querystring.parse(url.parse(link).query);
return identifier.__biz;
}
/**
* 取得微信公众号及最新的文章信息
* @param biz
* @param serverResData
* @returns {{}}
*/
function getAccount(biz, serverResData) {
var account = {};
// 解析公众号的数据
account.nickname = /var nickname = "(.+?)"/.exec(serverResData)[1];
account.headimg = /var headimg = "(.+?)"/.exec(serverResData)[1];
account.biz = biz;
account.crawlTime = new Date().getTime();
// 解析文章列表数据
var msgList = /var msgList = '(.+)';\n/.exec(serverResData)[1];
msgList = JSON.parse(escape2Html(msgList).replace(/\\\//g,'/'));
msgList = msgList.list;
account.articles = getArticles(biz, msgList);
return account;
}
/**
* 解析封装取得自己想要文章信息
* @param biz
* @param content
* @returns {Array}
*/
function getArticles(biz, content) {
var articles = [];
for (var i=0, len=content.length ; i < len ; i++) {
var post = content[i];
var cmi = post.comm_msg_info;
// 只采取图文消息的数据,目前所知type=3就只有一张图片,其它类型未知
if (cmi.type != 49) continue;
var amei = post.app_msg_ext_info;
var obj = getMidAndIdx(amei.content_url);
articles.push({
biz: biz,
mid: obj.mid,
title: amei.title,
digest: amei.digest,
contentUrl: amei.content_url,
sourceUrl: amei.source_url,
author: amei.author,
cover: amei.cover,
copyrightStat: amei.copyright_stat,
datetime: cmi.datetime,
idx: obj.idx
});
}
return articles;
}
/**
* 解析封装取得文章的评论信息
* @param link
* @param responseDetail
* @returns {Array}
*/
function getComments(link, responseDetail){
var identifier = querystring.parse(url.parse(link).query);
var comments = [];
var content = JSON.parse(responseDetail.response.body.toString());
var electedComment = content.elected_comment;
if (electedComment && electedComment.length) {
for (var i=0, len=electedComment.length ; i < len ; i++) {
comments.push({
biz: identifier.__biz,
mid: identifier.appmsgid,
contentId: electedComment[i].content_id,
nickName: electedComment[i].nick_name,
logoUrl: electedComment[i].logo_url,
content: electedComment[i].content,
createTime: electedComment[i].create_time,
likeNum: electedComment[i].like_num
});
}
}
return comments;
}
/**
* 从连接取得mid及idx
* @param link
* @returns {{mid: *, idx: *}}
*/
function getMidAndIdx(link) {
var identifier = querystring.parse(url.parse(link.replace(/amp;/g, '')).query);
return {
mid: identifier.mid,
idx: identifier.idx
}
}
/**
* 向服务上传抓取到的数据
* @param data 数据
* @param path 请求路径
*/
function serverPost(biz, data, path) {
if (!config.autoPostData) {
return Promise.resolve().then(function(){
return null;
});
}
var options = {
method: 'POST',
uri: config.host + path,
form: {
biz: biz,
content: JSON.stringify(data)
},
json: true
};
return rp(options).then(function (parsedBody) {
if (parsedBody.code == 1) {
console.log('--' + path + '------------------------');
//console.log(data);
return parsedBody.data;
} else {
console.log('请求失败, 失败信息=' + parsedBody.message);
return null;
}
});
}
/**
* 组装自动向下滚动翻页的JS
*
* @returns {string}
*/
function getAutoNextScrollJS() {
var nextJS = '';
nextJS += '<script type="text/javascript">';
nextJS += ' var end = document.createElement("p");';
nextJS += ' document.body.appendChild(end);';
nextJS += ' (function scrollDown(){';
nextJS += ' end.scrollIntoView();';
nextJS += ' var loadMore = document.getElementsByClassName("loadmore with_line")[0];';
nextJS += ' if (loadMore.style.display) {';
nextJS += ' setTimeout(scrollDown,Math.floor(Math.random()*('+config.n+'-'+config.m+')+'+config.m+'));';
nextJS += ' } ';
nextJS += ' })();';
nextJS += '<\/script>';
return nextJS;
}
/**
* 自动跳转到下一文章页的meta
*
* @param nextLink
* @returns {string}
*/
function getAutoNextPageMeta(nextLink) {
return '<meta http-equiv="refresh" content="' + config.jumpInterval + ';url=' + nextLink + '" />';
}
抓取微信公众号全部文章,采用AnyProxy+Javascript+Java实现相关推荐
- python爬取公众号历史文章_pythons爬虫:抓取微信公众号 历史文章(selenium+phantomjs)...
原标题:pythons爬虫:抓取微信公众号 历史文章(selenium+phantomjs) 大数据挖掘DT数据分析 公众号: datadw 本文爬虫代码可以通过回复本公众号关键字"公众号& ...
- 如何抓取微信公众号历史文章?使用订阅号实现微信公众号历史文章爬虫
微信订阅号已经改版了,这篇文章已经过时了,不过可以提供还算有价值的参考. 微信公众号已经成为生活的一部分了,虽然里面有很多作者只是为了蹭热点,撩读者的 G 点,自己从中获得一些收益:但 ...
- python爱好者社区公众号历史文章合集_如何优雅的抓取微信公众号历史文章
这是几天前在公众号上发的文章,主要讨论现在微信公众号文章抓取的一般思路以及优缺点,我不会讲技术细节,但我会分享别人已经开源的项目,你可以参考代码开了解其中的细节. 背景 微信公众号历史记录只可以通过客 ...
- python抓取微信公众号新闻文章图片
在看公众号新闻的时候,总会有一些有用的PPT图片啥的想保存下来. 那么用python如何抓取来,简单介绍一下. 比如这个网址,https://mp.weixin.qq.com/s/-rj91sCpea ...
- 记一次批量定时抓取微信公众号文章的实现
记一次批量定时抓取微信公众号文章的实现 抓取前的说明和准备 数据的抓取 批量抓取 定时抓取 对爬虫防抓取机制的一些解决办法 最后 抓取前的说明和准备 本次抓取的选择的语言是java,本文章不会将整个工 ...
- python公众号文章_Python 抓取微信公众号文章
起因是刷微信的时候看到一篇文章,Python 抓取微信公众号文章保存成pdf,很容易搜到,就不贴出来了 先用chrome登陆微信公众号后台,先获取一下自己的cookie,复制下来就行,解析一下转换成 ...
- java 微信文章评论点赞_使用fiddler抓取微信公众号文章的阅读数、点赞数、评论数...
1 设置fiddler支持https 打开fiddler,在菜单栏中依次选择 [Tools]->[Options]->[HTTPS],勾上如下图的选项: 单击Actions,选择Expor ...
- Python实现抓取微信公众号文章
本文首发于微信公众号:"算法与编程之美",欢迎关注,及时了解更多此系列文章. 前言 对于抓取微信公众号文章主要通过代理ip抓包进行的操作,总会出现一些问题,以下问题导致无法抓包. ...
- 利用搜狗抓取微信公众号文章
微信一直是一个自己玩的小圈子,前段时间搜狗推出的微信搜索带来了一丝曙光.搜狗搜索推出了内容搜索和公众号搜索两种,利用后者可以抓取微信公众号的最新内容,看了下还是比较及时的. 每个公众号都有一个open ...
最新文章
- 实现php实现价格的排序,php 数组动态添加实现代码(最土团购系统的价格排序)_PHP教程...
- warning LNK4098: 默认库“MSVCRT”与其他库的使用冲突;使用 /NODEFAULTLIB:library问题解决方法
- dubbo 部分 配置的关系-dubbo github 官方案例
- hive数据仓库摘录和总结
- python 操作 postgresql 数据库
- 用matlab画三维凸起,求助大牛MATLAB画三维等势面
- 就有趣,Python生成字符视频
- 这回真要涨工资了!国务院教育督导办:2020年把义务教育教师平均工资收入水平不低于当地公务员作为督导检查重点...
- java 数据库连接 释放_java - 数据库连接池耗尽 - Java - 堆栈内存溢出
- Spring Security学习
- Linux下5种动态库运行时搜索路径的方法
- js 图片浏览插件原生
- android开发笔记之xml矢量图片
- debian9 使用cups远程管理打印服务
- VUE小需求——旋转小图标
- 楼天城楼教主的acm心路历程
- typora 快捷键使用(mac)
- 转:Yahoo!网站性能最佳体验的34条黄金守则——图片、Coockie与移动应用
- C#语言基础学习笔记
- vm虚拟机网络标志_虚拟机安装win7系统后网络图标黄色标志不能上网如何解决