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

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

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

一、基本流程

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”按规则变化。

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

相关推荐

恢复软件6款汇总推荐,帮你减轻数据恢复压力!

在当今数字化生活中,数据丢失的风险如影随形。无论是误删文件、硬盘故障,还是遭遇病毒攻击,丢失的数据都可能给我们带来不小的麻烦。此时,一款优秀的数据恢复软件就成为了挽救数据的关键。今天,为大家汇总推荐...

中兴星星一号刷回官方原版recovery的教程

【搞科技教程】中兴星星一号的官方recovery也来说一下了,因为之前给大家分享过了第三方的recovery了,之前给大家分享的第三方recovery也是采用一键刷入的方式,如果细心的朋友会发现,之前...

新玩机工具箱,Uotan柚坛工具箱软件体验

以前的手机系统功能比较单调,各厂商的重视程度不一样,所以喜欢玩机的朋友会解锁手机系统的读写权限,来进行刷机或者ROOT之类的操作,让使用体验更好。随着现在的手机系统越来越保守,以及自身功能的增强,...

三星g906k刷recovery教程_三星g906k中文recovery下载

【搞科技教程】看到有一些机友在找三星g906k的第三方recovery,下面就来说一下详细的recovery的刷入方法了,因为手机只有有了第三方的recovery之后才可以刷第三方的root包和系统包...

中兴星星2号刷recovery教程_星星二号中文recovery下载

【搞科技教程】咱们的中兴星星2手机也就是中兴星星二号手机的第三方recovery已经出来了,并且是中文版的,有了这个recovery之后,咱们的手机就可以轻松的刷第三方的系统包了,如果没有第三方的re...

数据恢复软件有哪些值得推荐?这 6 款亲测好用的工具汇总请收好!

在数字生活中,数据丢失的阴霾常常突如其来。无论是误删工作文档、格式化重要磁盘,还是遭遇系统崩溃,都可能让我们陷入焦虑。关键时刻,一款得力的数据恢复软件便是那根“救命稻草”。今天,为大家精心汇总6...

中兴u956刷入recovery的教程(中兴e5900刷机)

【搞科技教程】这次主要来给大家说说中兴u956手机如何刷入第三方的recovery,因为第三方的recovery工具是咱们刷第三方rom包的基础,可是很我欠却不会刷,所以太这里来给大家整理了一下详细的...

联想A850+刷recovery教程 联想A850+第三方recovery下载

【搞科技教程】联想A850+的第三方recovery出来了,这个第三方的recovery是非常的重要的,比如咱们的手机要刷第三方的系统包的时候,都是需要用到这个第三方的recovery的,在网上也是有...

工具侠重大更新 智能机上刷机一条龙完成

工具侠是针对玩机的机油开发的一款工具,不管是发烧级别的粉丝,还是普通小白用户,都可以在工具侠上找到你喜欢的工具应用。这不,最新的工具侠2.0.16版本,更新了专门为小白准备的刷机助手工具,以及MTK超...

shift+delete删除的文件找回6种硬盘数据恢复工具

硬盘作为电脑的重要存储设备,如同一个巨大的数字仓库,承载着我们日常工作、学习和生活中的各种文件,从珍贵的照片、重要的工作文档到喜爱的视频、音乐等,都依赖硬盘来安全存放。但有时,我们可能会不小心用sh...

使用vscode+Deepseek 实现AI编程 基于Cline和continue

尊敬的诸位!我是一名专注于嵌入式开发的物联网工程师。关注我,持续分享最新物联网与AI资讯和开发实战。期望与您携手探寻物联网与AI的无尽可能。这两天deepseek3.0上线,据说编程能力比肩Cl...

详解如何使用VSCode搭建TypeScript环境(适合小白)

搭建Javascript环境因为TypeScript不能直接在浏览器上运行。它需要编译器来编译并生成JavaScript文件。所以需要首先安装好javascript环境,可以参考文章:https://...

使用VSCode来书写你的Jupyter Notebooks

现在你可以在VScode里面来书写你的notebook了,使用起来十分的方便。下面来给大家演示一下环境的搭建。首先需要安装一个jupyter的包,使用下面的命令安装:pip3install-ih...

使用VSCode模板提高Vue开发效率(vscode开发vue插件)

安装VSCode安装Vetur和VueHelper插件,安装完成后需要重启VScode。在扩展插件搜索框中找到如下Vetur和VueHelper两个插件,注意看图标。添加Vue模板打...

干货!VsCode接入DeepSeek实现AI编程的5种主流插件详解

AI大模型对编程的影响非常之大,可以说首当其冲,Cursor等对话式编程工具渐渐渗透到开发者的工作中,作为AI编程的明星产品,Cursor虽然好用,但是贵啊,所以咱们得找平替,最好免费那种。俗话说,不...