百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

利用 fidder + 微信pc端 全自动抓取微信公众号文章

bigegpt 2024-09-08 11:25 7 浏览

一、基本流程

1、采集已有的公众号文章:

首先用任意微信登陆微信pc端(要下载微信pc端并安装好)

把你做的采集入口(如:http://xxx.com/api/proxy/begincollect)发送到微信上

点击直接在微信PC端直接打开即可

2、如果公众号不存在:

做一个登记对应的公众号文章,一篇即可

做一个自动客户端,当有新文章时,自动导航访问,fidder监测到后,会自动推送到后台生成“公众号记录”

二、准备工作

1、fidder开启抓取 https 支持:Tools-》Options


2、自动解码


3、配置过滤


三、编写抓取脚本

1、在 Fidder 编写抓取脚本:Rules-》Customize Rules

static var tagUrl = "&abc=";
static var begincollectHost = "web.test.com";//替换成你的服务器
static var begincollectUrl = "/api/proxy/begincollect";	//替换成你的等待页面入口地址
static var host = "localhost:33386";//你的api服务主机地址
static var apiUrl = "/api/proxy/weixin";//你的api服务地址
static var debug = false;	

static function httpPost(url: String,host: String,contentStr: String): String{
		var content: byte[] = System.Text.Encoding.UTF8.GetBytes(contentStr);
		var oRQH: HTTPRequestHeaders = new HTTPRequestHeaders(url, ['Host: '+host,
     'Content-Length: '+content.length.ToString(), 'Content-Type: application/x-www-url-encoded']);
		oRQH.HTTPMethod = "POST";
		var oSD = new System.Collections.Specialized.StringDictionary();
		var newSession = FiddlerApplication.oProxy.SendRequestAndWait(oRQH, content, oSD, null);     	
		var jsonString = newSession.GetResponseBodyAsString();
		
		return jsonString;
}
		
	static function sendMsg(contentStr: String,type: String) : Object {
     	var jsonString = httpPost(apiUrl+"?type="+type,host,contentStr);
		
		  FiddlerApplication.Log.LogString("result:"+jsonString);
		  return Fiddler.WebFormats.JSON.JsonDecode(jsonString);
	}
		
	static function getFullUrl(url:String){
		if(debug){
			var end = "";

			if (url.IndexOf('#') > 0)
			{
				end = url.Substring(url.IndexOf('#'));
				url = url.Substring(0, url.IndexOf('#'));
			}
			url = url + (url.IndexOf('?') > 0 ? "" : "?a=") + tagUrl + end;
		}
		
		return "https://mp.weixin.qq.com/"+url;
	}
		
	static function getRndInternal(){
		return new System.Random().Next(3, 11) * 1000;
	}
		
	static function getReloadScript(url:String){
		return 	getReloadScript(url,0);
	}
		
	static function getReloadScript(url:String,time:int){
		if(time==0)
		  time = getRndInternal();
		var script = " <script>setTimeout(function(){window.location.href='"+url+"'},"+time+");</script>";
		
		FiddlerApplication.Log.LogString("reloadscript:"+script);
		return script;
	}
		
	static function getMPHisUrl(biz:String){ //获取公众号历史记录url
		return getFullUrl("mp/profile_ext?action=home&__biz="+biz+"&scene=124#wechat_redirect");
	}
		
	static function getMPhisReloadScript(biz:String){ 
		var url = getMPHisUrl(biz);
		
		return getReloadScript(url);
	}
		
	static function getMsgHisUrl(biz:String,pass_ticket:String,offset:String){ //获取公众号历史记录api url
		return getFullUrl("/mp/profile_ext?action=getmsg&__biz="+biz+"&f=json&offset="+
                      offset+"&count=10&is_ok=1&scene=124&pass_ticket="+pass_ticket
                      +"&x5=0&f=json");
	}

    static function OnBeforeResponse(oSession: Session) {
        if (m_Hide304s && oSession.responseCode == 304) {
            oSession["ui-hide"] = "true";
        }
		
		if(debug && !oSession.uriContains(tagUrl))
			return;
		
		if(oSession.HostnameIs(begincollectHost) && oSession.uriContains(begincollectUrl)){ //开始采集入口,地址要通过微信pc端浏览器打开
			var reloadScript="";
			var responses = oSession.GetResponseBodyAsString();
			var url="";
			var collect_url = "http://"+begincollectHost+begincollectUrl+"?key="+tagUrl;
			var time = 0;
			
			if(System.DateTime.Now.Hour<9 || System.DateTime.Now.Hour>=21)
			{	//21点之后,9点之前不采集
				url = collect_url;
				time = 3600 * 13 * 1000;
			}
			else
			{
				//获取公众号biz
				var jsonObj = sendMsg("","4");
				var biz = jsonObj.JSONObject["biz"];
        
				if(biz!=undefined){		
					 //跳转到公众号历史文章地址
					 url = getMPHisUrl(biz);
				}
				else{ //没有可采集的公众号,继续空页面轮询
					time = 3600 * 1000 + getRndInternal();
					url = collect_url;
				}	
			}
			reloadScript = getReloadScript(url, time);
      //我的入口页面返回是json,如果是html,则不用下面这句
			oSession.oResponse["Content-Type"]="text/html; charset=UTF-8";
			oSession.utilSetResponseBody(responses+reloadScript);
			
			return;
		}
		
		if(oSession.HostnameIs("mp.weixin.qq.com")){
			var reloadScript="";
			var responses = oSession.GetResponseBodyAsString();
			
			oSession.utilDecodeResponse(); //解码
			
			if(oSession.uriContains("profile_ext?action=home")){ //公众号历史消息页
				sendMsg(responses,"1");//记录公众号信息
				//获取公众号历史第1页记录
				var url = oSession.fullUrl.Replace("action=home","action=getmsg")+"&x5=0&f=json&f=json&offset=0&count=10&is_ok=1";
				
				reloadScript = getReloadScript(url);
			}
			else if(oSession.uriContains("profile_ext?action=getmsg")){ //获取历史消息
				var reload = oSession.uriContains("&offset=0");
				var content = (reload?"":oSession.url)+responses;

				//保存文章记录
				sendMsg(content,"2");
				
				if(reload){ //再次获取文章记录,总共获取20条
          //获取第2页10条记录
					var url = oSession.fullUrl.Replace("&offset=0&","&offset=10&");
					reloadScript = getReloadScript(url);
				}
				else
				{  //返回轮询等待页面
					var url = "http://"+begincollectHost+begincollectUrl+"?key="+tagUrl;
					
					reloadScript = getReloadScript(url);
				}
				oSession.oResponse["Content-Type"]="text/html; charset=UTF-8";					
			}
			else if(oSession.uriContains("/s/") || oSession.uriContains("s?__biz=")){//文章
        //保存文章内容
				var jsonObj = sendMsg(responses,"3");
				
				return;
			}
			
			oSession.utilSetResponseBody(responses+reloadScript);
		}
    }

2、服务端脚本(这里以c#.net为例)

public class ProxyController : ApiController
    {
        [System.Web.Http.HttpGet]
        [System.Web.Http.HttpPost]
        public JsonResult<object> BeginCollect()
        {
            return Json<object>("Collect,现在时间:" + DateTime.Now.ToString());
        }

        // GET: Proxy
        public async Task<JsonResult<object>> weixin(int type)
        {
            //获取从Fidder推送过来的内容
            string content = await Request.Content.ReadAsStringAsync();
            object obj = string.Empty;
            string biz = "";

            if (type == 1)
            { //从公众号历史页面获取并保存公众号信息
                Func<string, string> getValue = (pattern) => {
                    return GetValue(content, pattern);
                };

                AddMsg(() => {
                    //获取biz
                    biz = getValue("var\\s*__biz\\s*=\\s*\".+\"");
                    //获取昵称
                    string nickName = getValue("var\\s*nickname\\s*=\\s*\".+\"");
                    //获取headimage
                    string headImg = getValue("var\\s*headimg\\s*=\\s*\".+\"");
                    //appid
                    string appid = getValue("appid\\s*:\\s*\".+\"");
                    string errMsg;

                    if (nickName.Length > 0 && biz.Length > 0)
                    {	  //todo:保存数据到数据库
                        
                    }
                });
            }
            else if (type == 2)
            {
                if (!content.StartsWith("{"))
                {   //url和response组合
                    int index = content.IndexOf('{');
                    string url = content.Substring(0, index);
                    string[] paramList = url.Split('&');
                    Func<string, string> getValue = (name) => paramList.First(item => item.StartsWith(name + "=")).Replace(name + "=", "");

                    biz = getValue("__biz");
                    

                    content = content.Substring(index);
                    //obj = new { biz, uin, pass_ticket, key };
                    DataService.SetData("princess_updateflag", new { biz }, out string errMsg);
                }

                AddMsg(() => RecorData(content, biz));
            }
            else if (type==4)
            {
                string errMsg;
                dynamic data = DataService.GetData<ExpandoObject>("princess_getbiz", out errMsg);

                if (data != null)
                {
                    biz = data.biz;
                    if (biz?.Length > 0)
                        obj = new { biz };
                }
            }
            else if(type==3)
            {
                AddMsg(() => {
                    BuildPrincess(content);
                });
            }
            return Json<object>(obj);
        }

        private void AddMsg(Action action)
        {
            MessageQueue.Add(new MessageQueueItem(() => {
                try
                {
                    action();
                }
                catch (Exception ex)
                {
                    
                }
            }));
        }

        private void BuildPrincess(string content)
        {   //从文章信息里获取公众号信息
            //string url = content.Substring(0, 3000);
            string biz = GetValue(content, "var\\s*msg_link\\s*=\\s*\".+\"");

            if (biz.Length == 0) return;

            biz = biz.Substring(0, biz.IndexOf('&')).Substring(6);
            biz = biz.Substring(biz.IndexOf("__biz=") + 6);
            //content = content.Substring(1000);
            //公众号名称
            string source_name = GetValue(content, "var\\s*nickname\\s*=\\s*\".+\"");
            string source_img_url = GetValue(content, "var\\s*ori_head_img_url\\s*=\\s*\".+\"");
            string wechat_num = GetValue(content,
                                        "\\<span\\s+class=\"profile_meta_value\"\\>(?<name>.+)\\</span\\>");

            DataService.SetData("Princess_insert", new
            {
                org_id = biz,
                source_name = source_name,
                source_url = "",
                source_img_url = source_img_url,
                img_url = source_img_url,
                biz = biz
            }, out string errMsg);
        }

        private string GetValue(string value, string pattern)
        {
            if (Regex.IsMatch(value, pattern))
            {
                Match match = Regex.Match(value, pattern);

                if (match.Groups.Count > 1)
                    return match.Groups[1].Value;

                string result = match.Value;

                if (result.IndexOf('\"') > 0)
                {
                    result = result.Substring(result.IndexOf('\"') + 1);
                    result = result.Substring(0, result.IndexOf('\"'));
                }
                return result;
            }

            return "";
        }

        private void RecorData(string jsonData,string biz)
        {
            dynamic result = jsonData.ToObjectFromJson<ExpandoObject>();

            if (result.ret == 0)
            {
                string general_msg_list = result.general_msg_list;
                string errMsg;
                dynamic data = general_msg_list.ToObjectFromJson<ExpandoObject>();
                IEnumerable<dynamic> docs = (data.list as List<dynamic>).Where(item => {
                    if (!(item as IDictionary<string, object>).ContainsKey("app_msg_ext_info"))
                        return false;

                   return DataService.GetDataValue<int>("doc_exists",
                        out errMsg, new { articleid =
                                            #34;{item.comm_msg_info.id}-{item.app_msg_ext_info.fileid}" }) == 0;
                }).Select(item =>
                        {
                            item.app_msg_ext_info.create_date =
                              DateTimeHelper.GetDateTimeFromXml(item.comm_msg_info.datetime);
                            item.app_msg_ext_info.pid = item.comm_msg_info.id.ToString();
                            return item.app_msg_ext_info;
                        });

                if (docs.Count() == 0) return;

                string org_id = docs.First().content_url;
                org_id = org_id.Substring(org_id.IndexOf("__biz=") + 6).Split('&')[0];

                var paras = GetDatas(org_id, docs);
                var subDocs = docs.Where(item => item.is_multi == 1)
                                              .Select(item =>
                                              {
                                                  IEnumerable<dynamic> multiDocs =
                                                    item.multi_app_msg_item_list as IEnumerable<dynamic>;
                                                  return GetDatas(org_id, multiDocs, item.create_date, #34;{item.pid}");
                                              }
                                              );
                if (subDocs.Count() > 0)
                {
                    List<object> list = paras.ToList();
                    foreach (var item in subDocs)
                    {
                        list.AddRange(item);
                    }
                    paras = list;
                }

                if (!DataService.SetData("doc_insert", paras, out errMsg))
                {
                    
                }
            }
        }

        /// <summary>
        /// 上传图片到文件服务器
        /// </summary>
        /// <param name="picUrl"></param>
        /// <returns></returns>
        private string UploadFile(string picUrl)
        {
            dynamic picResult = DataService.Execute<ExpandoObject>("fileservice", new
            {
                keyword = "file",
                content = new
                {
                    ext = "jpg",
                    data = picUrl
                }
            });

            return picResult.picurl;
        }
        /// <summary>
        /// 获取要存储的数据对象
        /// </summary>
        /// <param name="org_id"></param>
        /// <param name="docs"></param>
        /// <param name="create_date"></param>
        /// <param name="pid"></param>
        /// <returns></returns>
        private IEnumerable<object> GetDatas(string org_id, IEnumerable<dynamic> docs
                                             , DateTime? create_date = null, string pid = null)
        {
            var paras = docs.Select(item => {
                string imageUrl = item.cover;

                imageUrl = UploadFile(imageUrl);

                return new
                {
                    articleid = #34;{pid ?? item.pid}-{item.fileid}",
                    title = item.title,
                    digest = item.digest,
                    ori_url = item.content_url,
                    url = item.content_url,
                    image_url = imageUrl,
                    ori_image_url = imageUrl,
                    doc_type = "图文",
                    create_date = create_date ?? item.create_date,
                    org_id = org_id
                };
            });

            return paras;
        }
    }

免责声明及注意事项:

目前理论上可以无限循环自动抓取所有公众号文章,但由于微信限制,当同一天采集达到100个以上公众号时,该微信会被暂时屏蔽。如果需要采集大量公众号的文章,可以借助第三方代理,让对外“IP”按规则变化。

【此文章仅供学习使用,不能用于任何商业目的。任何个人或组织用于非法目的使用,赚取利益的,与作者无关。造成的一切后果,由使用者自己承担】

相关推荐

Redis集群对比:主从复制、哨兵模式、Cluster一文看懂所有优缺点

在分布式系统中,Redis作为高性能的内存数据库,其集群方案的选择直接影响到系统的稳定性、可用性和扩展性。本文将全面对比Redis的三种主流集群方案:主从复制、哨兵模式和Cluster模式,帮助开发者...

redis的主从复制,读写分离,主从切换

当数据量变得庞大的时候,读写分离还是很有必要的。同时避免一个redis服务宕机,导致应用宕机的情况,我们启用sentinel(哨兵)服务,实现主从切换的功能。redis提供了一个master,多个sl...

# Redis 入门到精通(九)-- 主从复制(3)

#Redis入门到精通(九)--主从复制(3)##一、redis主从复制-常见问题(1)###1、伴随着redis系统的运行,master的数据量会越来越大,一旦master重启...

redis - 主从复制(Redis主从复制时序图)

1引言在上一篇文章中,我们了解了Redis两种不同的持久化方式,Redis服务器通过持久化,把Redis内存中持久化到硬盘当中,当Redis宕机时,我们重启Redis服务器时,可以由RDB文件或AO...

# Redis 入门到精通(九)-- 主从复制(2)

#Redis入门到精通(九)--主从复制(2)##一、redis主从复制--数据同步阶段注意事项###1、数据同步阶段master说明1)如果master数据量巨大,数据同步阶段应...

Redis主从复制(redis主从复制主节点挂了)

介绍Redis有两种不同的持久化方式,Redis服务器通过持久化,把Redis内存中持久化到硬盘当中,当Redis宕机时,我们重启Redis服务器时,可以由RDB文件或AOF文件恢复内存中的数据。不过...

深入解析 Redis 集群的主从复制实现方式

在互联网大厂的后端开发领域,Redis作为一款高性能的内存数据库,被广泛应用于缓存、消息队列等场景。而Redis集群中的主从复制机制,更是保障数据安全、实现读写分离以及提升系统性能的关键所在。今...

Redis主从架构详解(redis主从架构高可用如何实现)

Redis主从架构搭建Redis主节点配置创建主节点目录(/opt/redis-master),复制redis.conf到该目录下,redis.conf配置项修改#后台启动daemonizeyes...

抖音“四大包塘战神”:承包了全网的快乐

在抖音钓鱼垂类领域,"包塘战神"军团正掀起一场黑色幽默风暴。空军华、大表坑、李赔光、透心良四位创作者,以承包鱼塘为舞台,用连续翻车的钓鱼直播构筑起流量奇观。当钓鱼佬在抖音集体转型喜剧人...

ORACLE 11G RAC 安装-通过VM配置共享磁盘

简介:在自己的电脑上通过VM软件搭建Oracle11GRAC,通过修改VM的参数文件来实现磁盘共享!目标:搭建RAC环境实现:使用VMwareWorkstation8.0.0+ORACLE...

Linux操作系统安全配置(linux系统安全配置包括)

一、服务相关命令systemctlenable服务名#开机自启动systemctldisable服务名#禁用开机自启动systemctlstop服务名#停止服务systemctls...

关于Linux性能调优中网络I/O的一些笔记

写在前面和小伙伴分享一些Linux网络优化的笔记,内容很浅,可以用作入门博文内容结合《Linux性能优化》读书笔记整理涉及内容包括常用的优化工具(mii-tool,ethtool,ifconfig,i...

从 Sonatype Nexus Repository Manager 迁移到 Artifactory

1.Nexus1.1下载下载链接:https://help.sonatype.com/repomanager3/product-information/download/download-archiv...

Ubuntu20安装zabbix5.0企业监控系统亲测教程

前言示例主机:zabbix10.0.100.10,将安装在UbuntuServer上教程说明:因使用官方教程无法安装成功,所以本教程与官方教程有所不同安装前提:已安装UbuntuServer2...

Linux内核设计与实现—进程管理(linux内核程序设计)

进程进程就是处于执行期的程序(目标码存放在某种存储介质上)。进并不仅仅局限于一段可执行程序代码(Unix称其为代码段,textsection)。通常进程还要包含其他资源,像打开的文件,挂起的信号,...