เมื่อเร็ว ๆ นี้ฉันต้องรวบรวมข้อมูลบทความของบัญชี WeChat อย่างเป็นทางการ ฉันค้นหาออนไลน์และพบว่าความยากลำบากในการรวบรวมข้อมูลบัญชีสาธารณะ WeChat คือลิงก์ไปยังบทความบัญชีอย่างเป็นทางการไม่สามารถเปิดได้ทางด้านพีซี เราต้องใช้เบราว์เซอร์ของ WeChat (คุณสามารถเปิดได้บนแพลตฟอร์มอื่น ๆ หลังจากที่คุณได้รับพารามิเตอร์เสริมโดยไคลเอนต์ WeChat) สิ่งนี้ทำให้เกิดปัญหาอย่างมากต่อโปรแกรม Crawler ต่อมาฉันเห็นโปรแกรมการรวบรวมข้อมูลบัญชีอย่างเป็นทางการของ WeChat ที่เขียนโดยชายร่างใหญ่ใน Zhihu และติดตามความคิดของเจ้านายโดยตรงและเปลี่ยนเป็น Java ฉันพบปัญหารายละเอียดมากมายระหว่างการเปลี่ยนแปลงดังนั้นฉันจะแบ่งปัน
แนวคิดพื้นฐานของระบบคือการเรียกใช้ WeChat บน Android Emulator ตั้งค่าพร็อกซีสำหรับตัวจำลองการสกัดกั้นข้อมูล WeChat ผ่านพร็อกซีเซิร์ฟเวอร์และส่งข้อมูลที่ได้รับไปยังโปรแกรมของคุณเองสำหรับการประมวลผล
สภาพแวดล้อมที่ต้องเตรียม: NodeJs, Proxy Anyproxy และ Android Emulator
Nodejs ดาวน์โหลดที่อยู่: http://nodejs.cn/download/ ฉันดาวน์โหลดเวอร์ชัน Windows เพียงติดตั้งโดยตรง หลังจากการติดตั้งให้เรียกใช้ C:/โปรแกรม program/nodejs/npm.cmd จะกำหนดค่าสภาพแวดล้อมโดยอัตโนมัติ
การติดตั้ง AnyProxy: หลังจากติดตั้ง nodejs ตามขั้นตอนก่อนหน้าเรียกใช้ NPM ติดตั้ง -g AnyProxy โดยตรงใน CMD และติดตั้ง
เพียงไปที่ Android Emulator ออนไลน์พวกเขาส่วนใหญ่
ขั้นแรกให้ติดตั้งใบรับรองสำหรับพร็อกซีเซิร์ฟเวอร์ AnyProxy ไม่ได้แก้ไขลิงก์ HTTPS โดยค่าเริ่มต้น หลังจากติดตั้งใบรับรองแล้วจะสามารถแก้ไขได้ ดำเนินการ AnyProxy -Root ใน CMD เพื่อติดตั้งใบรับรอง หลังจากนั้นคุณต้องดาวน์โหลดใบรับรองนี้ในตัวจำลอง
จากนั้นป้อนคำสั่ง Anyproxy -i เพื่อเปิดบริการพร็อกซี (อย่าลืมเพิ่มพารามิเตอร์!)
โปรดจำไว้ว่า IP และพอร์ตนี้จากนั้นตัวแทนของ Android Emulator จะใช้สิ่งนี้ ตอนนี้ใช้เบราว์เซอร์ของคุณเพื่อเปิดหน้าเว็บ: http: // localhost: 8002/นี่คือเว็บอินเตอร์เฟสของ Anyproxy ซึ่งใช้เพื่อแสดงข้อมูลการส่ง HTTP
คลิกที่เมนูในกล่องสีแดงด้านบนและรหัส QR จะถูกปล่อยออกมา ใช้เครื่องจำลอง Android เพื่อสแกนรหัสเพื่อระบุ Emulator (โทรศัพท์มือถือ) จะดาวน์โหลดใบรับรองและติดตั้ง
ตอนนี้คุณพร้อมที่จะตั้งค่าพร็อกซีสำหรับอีมูเลเตอร์ วิธีพร็อกซีถูกตั้งค่าเป็นคู่มือ พร็อกซี IP คือ IP ที่เรียกใช้เครื่อง AnyProxy และพอร์ตคือ 8001
งานเตรียมการเสร็จสมบูรณ์โดยทั่วไปที่นี่ เปิด WeChat บน Emulator และเปิดบทความในบัญชีสาธารณะและคุณสามารถดูข้อมูลที่บันทึกโดย AnyProxy จากเว็บอินเตอร์เฟสที่คุณเพิ่งเปิด:
ลิงค์ไปยังบทความ WeChat อยู่ในกล่องสีแดงด้านบน คลิกเพื่อดูข้อมูลเฉพาะ หากไม่มีสิ่งใดในร่างกายตอบสนองมีปัญหากับการติดตั้งใบรับรอง
หากทั้งหมดข้างต้นเสร็จสิ้นคุณสามารถเดินต่อไปได้
ที่นี่เราพึ่งพาบริการพร็อกซีเพื่อรวบรวมข้อมูล WeChat แต่เราไม่สามารถคว้าข้อมูลชิ้นหนึ่งและใช้งาน WeChat ด้วยตัวเองได้ ดีกว่าที่จะคัดลอกด้วยตนเอง ดังนั้นเราจึงต้องการไคลเอนต์ WeChat เพื่อข้ามไปที่หน้าด้วยตัวเอง ในเวลานี้คุณสามารถใช้ AnyProxy เพื่อสกัดกั้นข้อมูลที่ส่งคืนโดย WeChat Server ฉีดรหัสกระโดดหน้าลงไปแล้วส่งคืนข้อมูลที่ประมวลผลไปยังตัวจำลองเพื่อให้ได้การกระโดดอัตโนมัติของไคลเอนต์ WeChat
เปิดไฟล์ js ที่เรียกว่า Rule_default.js ใน Anyproxy ไฟล์ภายใต้ Windows คือ: C:/ผู้ใช้/ผู้ดูแลระบบ/AppData/Roaming/NPM/NODE_MODULES/AnyProxy/lib
มีวิธีการที่เรียกว่า replaceserverresdataasync: function (req, res, serverresdata, callback) ในไฟล์ วิธีนี้มีหน้าที่รับผิดชอบในการดำเนินการต่าง ๆ เกี่ยวกับข้อมูลที่ได้รับจาก AnyProxy ในตอนแรกควรมีเพียงการโทรกลับ (ServerResData); คำสั่งนี้หมายถึงการส่งคืนข้อมูลการตอบกลับเซิร์ฟเวอร์โดยตรงไปยังไคลเอนต์ ลบคำสั่งนี้โดยตรงและแทนที่ด้วยรหัสต่อไปนี้ที่เขียนโดย Daniu ฉันไม่ได้ทำการเปลี่ยนแปลงใด ๆ กับรหัสที่นี่และความคิดเห็นในนั้นมีการอธิบายอย่างชัดเจน เพียงแค่อ่านโดยตรงตามตรรกะและไม่มีปัญหาใหญ่
replaceserverResDataAsync: function (req, res, serverresdata, callback) {ถ้า (/mp//getmassendmsg/i.test (req.url)) {// เมื่อที่อยู่ลิงก์เป็นหน้าข้อความประวัติบัญชีอย่างเป็นทางการ (แบบฟอร์มหน้าแรก) //console.log if (serverresdata.toString ()! == "") {6 ลอง {// ป้องกันข้อผิดพลาดจากการออกจากโปรแกรม var reg =/msglist = (.*?); // // กำหนดกฎการจับคู่ข้อความปกติ var ret = reg.exec (เซิร์ฟเวอร์ httppost (ret [1], req.url, "/internetspider/getData/showbiz"); // ฟังก์ชั่นนี้ถูกกำหนดในภายหลังส่งข้อความประวัติศาสตร์ที่ตรงกัน JSON ไปยังเซิร์ฟเวอร์ของตัวเอง var http = ต้องการ ('http'); http.get ('http: // xxx/getwxhis', ฟังก์ชั่น (res) {// ที่อยู่นี้เป็นโปรแกรมบนเซิร์ฟเวอร์ของตัวเองวัตถุประสงค์คือเพื่อให้ได้ที่อยู่ลิงก์ถัดไปวางที่อยู่ในสคริปต์ JS และข้ามไปยังหน้าถัดไป การโทรกลับ (chunk+serverresdata); // แทรกรหัสที่ส่งคืนลงในหน้าข้อความประวัติศาสตร์และกลับไปแสดง})}); } catch (e) {// ถ้าปกติข้างต้นไม่ตรงกันดังนั้นเนื้อหาของหน้านี้อาจเป็นหน้าสองของหน้าข้อความประวัติศาสตร์ของบัญชีอย่างเป็นทางการที่ลดลงเนื่องจากหน้าแรกของข้อความประวัติศาสตร์อยู่ในรูปแบบ HTML และหน้าสองอยู่ในรูปแบบ JSON //console.log(" start หน้าแรกคลานลงแบบฟอร์มลง "); ลอง {var json = json.parse (serverresdata.toString ()); if (json.general_msg_list! = []) {httppost (json.general_msg_list, req.url, "/xxx/showbiz"); // ฟังก์ชั่นนี้ถูกกำหนดในภายหลัง การโทรกลับ (ServerResData); // กลับไปที่หน้าสองเนื้อหา JSON}} //console.log("Start การรวบรวมข้อมูลหน้าแรก "); } อื่นถ้า (/mp//profile_ext/?action=home/i.test (req.url)) {// เมื่อที่อยู่ลิงก์เป็นหน้าข้อความบัญชีอย่างเป็นทางการ (แบบฟอร์มหน้าสอง) ลอง {var reg =/var msglist ='( reg.exec (serverresdata.toString ()); // แปลงตัวแปรเป็นสตริง httppost (ret [1], req.url, "/xxx/showbiz"); // ฟังก์ชั่นนี้ถูกกำหนดในภายหลัง http.get ('xxx/getwxhis', ฟังก์ชั่น (res) {// ที่อยู่นี้เป็นโปรแกรมบนเซิร์ฟเวอร์ของคุณวัตถุประสงค์คือการได้รับที่อยู่ลิงก์ถัดไปวางที่อยู่ในสคริปต์ JS และข้ามไปยังหน้าถัดไปโดยอัตโนมัติ รหัสลงในหน้าข้อความประวัติและกลับไปแสดง})}); } catch (e) {//console.log(e); การโทรกลับ (ServerResData); }} อื่นถ้า (/mp//profile_ext/?action=getmsg/i.test (req.url)) {// นิพจน์หน้าที่สอง json ลอง {var json = json.parse (ServerResdata.toString ()); if (json.general_msg_list! = []) {httppost (json.general_msg_list, req.url, "/xxx/showbiz"); // ฟังก์ชั่นนี้ถูกกำหนดในภายหลัง } การโทรกลับ (ServerResData); } อื่นถ้า (/mp//getappmsgext/i.test (req.url)) {// เมื่อที่อยู่ลิงก์คือจำนวนมุมมองและชอบสำหรับบทความบัญชีอย่างเป็นทางการลอง {httppost (serverresdata, req.url, "/xxx/getmsgext" เซิร์ฟเวอร์} catch (e) {} callback (ServerResData); } อื่นถ้า (/s/? __ biz/i.test (req.url) || /mp//rumor/i.test(req.url))1-2)) {///when ที่อยู่ลิงก์เป็นบทความบัญชีอย่างเป็นทางการ (ที่อยู่ข่าวลือคือบทความบัญชีอย่างเป็นทางการ http.get ('http: // xxx/getwxpost', ฟังก์ชั่น (res) {// ที่อยู่นี้เป็นโปรแกรมอื่นบนเซิร์ฟเวอร์ของคุณวัตถุประสงค์คือเพื่อรับที่อยู่ลิงก์ถัดไปวางที่อยู่ในสคริปต์ JS และข้ามไปยังหน้า (chunk) - } catch (e) {callback (ServerResData); }} else {callback (ServerResData); } // callback (ServerResData); -ให้ฉันอธิบายสั้น ๆ ที่นี่ว่ามีลิงก์สองรูปแบบไปยังหน้าข้อความประวัติศาสตร์ของบัญชีอย่างเป็นทางการของ WeChat: หนึ่งเริ่มต้นด้วย mp.weixin.qq.com/mp/getmassendmsg และอื่น ๆ เริ่มต้นด้วย mp.weixin.qq.com/mp/profile_ext หน้าประวัติสามารถพลิกลงได้ หากพลิกลงมันจะทำให้เกิดเหตุการณ์ JS เพื่อส่งคำขอเพื่อรับข้อมูล JSON (เนื้อหาของหน้าถัดไป) นอกจากนี้ยังมีลิงก์บทความบัญชีอย่างเป็นทางการรวมถึงลิงก์ไปยังจำนวนบทความที่อ่านและชอบ (ส่งคืนข้อมูล JSON) รูปแบบของลิงก์เหล่านี้ได้รับการแก้ไขและสามารถแยกแยะได้ด้วยการตัดสินเชิงตรรกะ มีคำถามที่นี่: จะทำอย่างไรหากหน้าประวัติศาสตร์ทั้งหมดจำเป็นต้องคลาน ความคิดของฉันคือการจำลองเมาส์ที่เลื่อนลงผ่าน JS ดังนั้นจึงเรียกร้องให้ส่งคำขอเพื่อโหลดส่วนถัดไปของรายการ หรือใช้ AnyProxy โดยตรงเพื่อวิเคราะห์คำขอสำหรับการเลื่อนการโหลดและสร้างคำขอนี้โดยตรงไปยังเซิร์ฟเวอร์ WeChat แต่มีปัญหาเกี่ยวกับวิธีการตัดสินว่าไม่มีข้อมูลที่เหลืออยู่ ฉันกำลังรวบรวมข้อมูลล่าสุดและฉันไม่มีข้อกำหนดนี้ในขณะนี้และฉันอาจต้องการมันในอนาคต หากคุณต้องการคุณสามารถลองได้
รูปต่อไปนี้คือเนื้อหาของวิธี HTTPPOST ด้านบน
ฟังก์ชั่น httppost (str, url, path) {// ส่ง json ไปยังเซิร์ฟเวอร์, str คือเนื้อหา JSON, url เป็นที่อยู่หน้าข้อความประวัติศาสตร์, เส้นทางคือเส้นทางและชื่อไฟล์ของโปรแกรมรับ
console.log ("เริ่มส่งต่อ");
พยายาม{
var http = ต้องการ ('http');
var data = {
Str: encodeuricomponent (str)
URL: encodeuricomponent (URL)
-
data = require ('querystring'). stringify (data);
ตัวเลือก var = {
วิธี: "โพสต์",
โฮสต์: "xxx", // โปรดทราบว่าไม่มี http: // นี่คือชื่อโดเมนของเซิร์ฟเวอร์
พอร์ต: xxx,
PATH: PATH, // เส้นทางและชื่อไฟล์ของโปรแกรมรับ
ส่วนหัว: {
'ประเภทเนื้อหา': 'แอปพลิเคชัน/x-www-form-urlencoded; charset = utf-8 '
"ความยาวเนื้อหา": data.length
-
-
var req = http.request (ตัวเลือก, ฟังก์ชัน (res) {
Res.setEncoding ('UTF8');
res.on ('data', ฟังก์ชั่น (ก้อน) {
console.log ('ร่างกาย:' + ก้อน);
-
-
req.on ('ข้อผิดพลาด', ฟังก์ชัน (e) {
console.log ('ปัญหากับคำขอ:' + e.message);
-
req.write (ข้อมูล);
req.end ();
} catch (e) {
console.log ("ข้อความแสดงข้อผิดพลาด:"+e);
-
console.log ("การดำเนินการส่งต่อสิ้นสุด");
-หลังจากทำงานด้านบนขั้นตอนต่อไปคือการกรอกรหัสเซิร์ฟเวอร์ตามธุรกิจของคุณเอง บริการของเราถูกใช้เพื่อรับข้อมูลที่ส่งโดยพร็อกซีเซิร์ฟเวอร์สำหรับการประมวลผลดำเนินการอย่างต่อเนื่องและในเวลาเดียวกันส่งรหัส JS ที่ต้องฉีดเข้าไปใน WeChat ไปยังพร็อกซีเซิร์ฟเวอร์ สำหรับข้อมูลที่ส่งจากลิงก์ที่แตกต่างกันหลายรายการที่สกัดกั้นโดยพร็อกซีเซิร์ฟเวอร์เราจำเป็นต้องออกแบบวิธีการที่สอดคล้องกันเพื่อประมวลผลข้อมูลเหล่านี้ จากวิธีการ JS ของ JS ในการประมวลผลข้อมูล WeChat แทนที่ WeChat Data ReplaceERRESDATAASYNC: ฟังก์ชั่น (REQ, RES, ServerResData, การโทรกลับ) เราสามารถรู้ได้ว่าต้องใช้วิธีการอย่างน้อยสามวิธีในการออกแบบหน้าประวัติบัญชีอย่างเป็นทางการ ในเวลาเดียวกันเรายังต้องออกแบบวิธีการสร้างงานคลานและทำการคลานไปกลับของบัญชีอย่างเป็นทางการ หากคุณต้องการรวบรวมข้อมูลมากขึ้นคุณสามารถวิเคราะห์ข้อมูลที่จำเป็นมากขึ้นจากลิงก์ที่บันทึกโดย AnyProxy จากนั้นเพิ่มการตัดสินเพื่อแทนที่ REPLACESERVERRESDATAASYNC: ฟังก์ชั่น (req, res, serverResData, การโทรกลับ) สกัดข้อมูลที่ต้องการและส่งไปยังเซิร์ฟเวอร์ของคุณเอง
ฉันกำลังเขียนรหัสเซิร์ฟเวอร์ใน Java
วิธีการประมวลผลประวัติบัญชีอย่างเป็นทางการหน้าข้อมูล:
โมฆะสาธารณะ getMSGJSON (String str, url string) พ่น unsupportencodingexception {// toDo วิธีการที่สร้างขึ้นอัตโนมัติ stub string biz = ""; แผนที่ <สตริงสตริง> querystrs = httpurlparser.parseurl (url); if (querystrs! = null) {biz = querystrs.get ("__ biz"); biz = biz + "=="; } /*** แบบสอบถามจากฐานข้อมูลไม่ว่าจะมี Biz อยู่แล้วและแทรกถ้าไม่มีอยู่ * ซึ่งหมายความว่าเราได้เพิ่มบัญชีอย่างเป็นทางการใหม่สำหรับเป้าหมายการรวบรวม */ list <weixin> results = weixinmapper.selectbybiz (biz); if (ผลลัพธ์ == null || results.size () == 0) {weixin weixin = new weixin (); Weixin.setBiz (biz); Weixin.setCollect (System.CurrentTimeMillis ()); Weixinmapper.insert (Weixin); } //system.out.println(str); // Parse Str Variable List <jobch> list = jsonPath.read (str, "['list']"); สำหรับ (รายการวัตถุ: รายการ) {object json = list; int type = jsonpath.read (json, "['comm_msg_info'] ['type']"); if (type == 49) {// type = 49 หมายความว่ามันเป็นสตริงข้อความ content_url = jsonpath.read (json, "$ .app_msg_ext_info.content_url"); content_url = content_url.replace ("//", "") .replaceall ("amp;", ""); // รับที่อยู่ลิงก์ของข้อความข้อความ int is_multi = jsonpath.read (json, "$ .app_msg_ext_info.is_multi" "$ .COMM_MSG_INFO.DATETIME"); // ส่งเวลาของภาพและข้อความ/** * ที่อยู่ภาพและข้อความเชื่อมโยงข้อความจะถูกแทรกลงในห้องสมุดคิวการซื้อกิจการ tmplist * (ห้องสมุดคิวจะถูกนำมาใช้ในภายหลัง if (content_url! = null &&! "". equals (content_url)) {tmplist tmplist = tmplist ใหม่ (); tmplist.setContentUrl (content_url); tmplistmapper.insertselective (tmplist); }} catch (exception e) {system.out.println ("คิวอยู่แล้วไม่มีการแทรก!"); } / *** ที่นี่เราตัดสินว่ามีการทำซ้ำจากโพสต์ฐานข้อมูลตาม $ content_url* / list <post> postlist = postmapper.selectbyContentUrl (content_url); บูลีน contenturlexist = false; if (postlist! = null && postlist.size ()! = 0) {contenturlexist = true; } if (! contenturlexist) {// 'มี $ content_url เดียวกันอยู่ในโพสต์ฐานข้อมูล' integer fileid = jsonpath.read (json, "$ .app_msg_ext_info.fileid"); urlencoder.encode (ชื่อเรื่อง "UTF-8"); String Digest = jsonpath.read (json, "$ .App_MSG_EXT_INFO.Digest"); // บทความสรุปสตริง source_url = jsonpath.read (json, "$ .app_msg_ext_info.source_url"); // อ่านข้อความต้นฉบับ สตริงปก = jsonpath.read (json, "$ .App_MSG_EXT_INFO.Cover"); // ปกภาพปก = cover.replace ("//", ""); /*** บันทึกลงในฐานข้อมูล* /// System.out.println ("title:"+title); // system.out.println ("weChat id:"+fileid); // system.out.println ("บทความสรุป:"+digest); // system.out.println (" ที่อยู่: "+ปก); โพสต์โพสต์ = โพสต์ใหม่ (); post.setBiz (biz); post.settitle (ชื่อ); post.settitleencode (title_encode); post.setfieldId (fileid); post.setDigest (Digest); post.setSourceurl (source_url); post.setCover (ปก); post.setistop (1); // แท็กเป็นเนื้อหาพาดหัวโพสต์ SetIsmulti (is_multi); post.setDateTime (DateTime); post.setContentUrl (content_url); postmapper.insert (โพสต์); } if (is_multi == 1) {// ถ้าเป็นรายการข้อความหลายกราฟิก <Ojuct> multilists = jsonPath.read (json, "['app_msg_ext_info'] ['multi_app_msg_item_list']"); สำหรับ (Object Multilist: Multilists) {Object MultiJson = Multilist; content_url = jsonpath.read (multijson, "['content_url']"). toString (). แทนที่ ("//", "") .replaceall ("amp;", ""); รายการ <post> โพสต์ = postmapper.selectbyContentUrl (content_url); if (โพสต์! = null && posts.size ()! = 0) {contenturlexist = true; } if (! contenturlexist) {// '$ content_url เดียวกันไม่ปรากฏในฐานข้อมูล'/** * ที่นี่แทรกที่อยู่กราฟิกและข้อความลิงก์ข้อความลงในห้องสมุดคิวการซื้อกิจการ * (ห้องสมุดคิวจะถูกนำมาใช้ในภายหลัง if (content_url! = null &&! "". เท่ากับ (content_url)) {tmplist tmplistt = tmplist ใหม่ (); tmplistt.setContentUrl (content_url); tmplistmapper.insertselective (tmplistt); } string title = jsonpath.read (MultiJson, "$ .title"); String title_encode = urlencoder.encode (ชื่อเรื่อง "UTF-8"); จำนวนเต็ม fileid = jsonpath.read (MultiJson, "$ .fileid"); String Digest = jsonPath.read (MultiJson, "$ .Digest"); String Source_url = jsonpath.read (MultiJson, "$ .source_url"); source_url = source_url.replace ("//", ""); สตริงปก = jsonpath.read (MultiJson, "$ .cover"); cover = cover.replace ("//", ""); // System.out.println ("Title:"+title); // system.out.println ("weChat id:"+fileid); // system.out.println ("บทความสรุป:"+digest); // system.out.println ("ลิงก์ดั้งเดิม:" โพสต์โพสต์ = โพสต์ใหม่ (); post.setBiz (biz); post.settitle (ชื่อ); post.settitleencode (title_encode); post.setfieldId (fileid); post.setDigest (Digest); post.setSourceurl (source_url); post.setCover (ปก); post.setistop (0); // แท็กไม่ใช่เนื้อหาพาดหัวโพสต์ SetIsmulti (is_multi); post.setDateTime (DateTime); post.setContentUrl (content_url); postmapper.insert (โพสต์); -วิธีจัดการกับหน้าบทความบัญชีอย่างเป็นทางการ:
Public String getWxPost () {// todo วิธีการที่สร้างอัตโนมัติ stub / *** เมื่อหน้าปัจจุบันเป็นหน้าบทความบัญชีอย่างเป็นทางการอ่านโปรแกรมนี้* ก่อนการลบบรรทัดโหลด = 1 ในรายการคิวคอลเลกชัน* จากนั้นเลือกหลายบรรทัดตาม "คำสั่งซื้อ asc" จากรายการคิว รายการ <tmplist> queues = tmplistmapper.selectmany (5); สตริง url = ""; if (queues! = null && queues.size ()! = 0 && queues.size ()> 1) {tmplist queue = queues.get (0); url = queue.getContentUrl (); queue.setisload (1); int result = tmplistmapper.updateByPrimaryKey (คิว); System.out.println ("Update Rets:"+ผลลัพธ์); } else {system.out.println ("คิว getpost เป็น null?"+queues == null? null: queues.size ()); weixin weixin = weixinmapper.selectone (); สตริง biz = weixin.getBiz (); if ((math.random ()> 0.5? 1: 0) == 1) {url = "http://mp.weixin.qq.com/mp/getmasssendmsg?__biz=" + biz + "#wechat_webview_type = 1 & wechat_redirect"; = "https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=" + biz + "#wechat_redirect"; // แยกที่อยู่ URL ของบัญชีประวัติอย่างเป็นทางการ + biz + "#wechat_redirect"; // แยกที่อยู่ URL ของข้อความประวัติบัญชีอย่างเป็นทางการ (แบบฟอร์มหน้าสอง) // อัปเดตฟิลด์เวลาการรวบรวมเวลาในตารางบัญชีอย่างเป็นทางการที่กล่าวถึงตอนนี้ถึงการประทับเวลาปัจจุบัน Weixin.setCollect (System.CurrentTimeMillis ()); int result = weixinmapper.updateByPrimaryKey (Weixin); System.out.println ("GetPost Weixin Updateresult:"+ผลลัพธ์); } int randomtime = new random (). nextint (3) + 3; string jscode = "<script> settimeout (function () {window.location.href = '"+url+"';},"+randomtime*1000+"); </script>"; ส่งคืน JScode; -วิธีจัดการกับจำนวนไลค์และการอ่านบัญชีอย่างเป็นทางการ:
โมฆะสาธารณะ getMsGext (String str, url string) {// todo วิธีการที่สร้างอัตโนมัติ stub string biz = ""; สตริง sn = ""; แผนที่ <สตริงสตริง> querystrs = httpurlparser.parseurl (url); if (querystrs! = null) {biz = querystrs.get ("__ biz"); biz = biz + "=="; sn = querystrs.get ("sn"); sn = "%" + sn + "%"; } /** * $ sql = "เลือก * จาก` Table Table 'โดยที่ `biz` ='". $ biz. "' * และ` content_url` เช่น'%"$ sn."%'"จำกัด 0,1; * ค้นหาบทความที่เกี่ยวข้องตาม biz และ sn*/ post post = postmapper.selectbybizandsn (biz, sn); if (post == null) {system.out.println ("biz:"+biz); System.out.println ("SN:"+SN); tmplistmapper.deletebyload (1); กลับ; } // system.out.println ("ข้อมูล JSON:"+str); จำนวนเต็ม read_num; จำนวนเต็ม like_num; ลอง {read_num = jsonpath.read (str, "['appmsgstat'] ['read_num']"); // อ่านระดับเสียง like_num = jsonpath.read (str, "['appmsgstat'] ['like_num']"); System.out.println ("read_num:"+read_num); System.out.println ("like_num:"+like_num); System.out.println (e.getMessage ()); } /*** ที่นี่บทความที่เกี่ยวข้องจะถูกลบในรายการคิวคิวการรวบรวมตาม SN ซึ่งหมายความว่าบทความนี้สามารถลบออกจากคิวการรวบรวม * $ sql = "ลบจาก` รายชื่อทีม 'โดยที่ `content_url` เช่น'%". $ sn. "%'" */ tmplistmapper.deletebysn (SN); // จากนั้นอัปเดตจำนวนมุมมองและชอบในตารางบทความ post.setReadNum (read_num); post.setLikenum (like_num); postmapper.updateByPrimaryKey (โพสต์); -วิธีจัดการกระโดดไปที่ WeChat Injection JS:
สตริงสาธารณะ getWxHis () {string url = ""; // วิธีการที่สร้างขึ้นอัตโนมัติ todo stub /*** เมื่อหน้าปัจจุบันเป็นข้อความประวัติบัญชีสาธารณะอ่านโปรแกรมนี้* มีฟิลด์โหลดในรายการคิวคิวคอลเลกชัน เมื่อค่าเท่ากับ 1 หมายความว่ากำลังอ่าน* ก่อนลบโหลดบรรทัด = 1 ในรายการคิวการรวบรวม* จากนั้นเลือกบรรทัดใด ๆ จากรายการทีม*/ tmplistmapper.deletebybyload (1); tmplist queue = tmplistmapper.selectrandomone (); System.out.println ("คิวเป็นโมฆะ?"+คิว); if (queue == null) {// รายการคิวว่างเปล่า/*** หากรายการคิวว่างเปล่าให้รับ biz จากตารางที่จัดเก็บบัญชีอย่างเป็นทางการ * ที่นี่ฉันตั้งค่าฟิลด์เวลาของเวลาการรวบรวมในตารางบัญชีอย่างเป็นทางการ หลังจากเรียงลำดับตามลำดับบวก * รับบันทึกบัญชีอย่างเป็นทางการด้วยการประทับเวลาที่เล็กที่สุดและรับ biz */ weixin weixin = weixinmapper.selectone (); สตริง biz = weixin.getBiz (); url = "https://mp.weixin.qq.com/mp/profile_ext?action=home&__biz=" + biz + "#wechat_redirect"; // แยกที่อยู่ URL ข้อความประวัติอย่างเป็นทางการ Weixin.setCollect (System.CurrentTimeMillis ()); int result = weixinmapper.updateByPrimaryKey (Weixin); System.out.println ("Gethis Weixin Updateresult:"+ผลลัพธ์); } else {// รับฟิลด์ content_url ของ url บรรทัดปัจจุบัน = queue.getContentUrl (); // อัปเดตฟิลด์โหลดเป็น 1 tmplistmapper.updateByContentUrl (URL); } // เปลี่ยน $ url ถัดไปที่จะเปลี่ยนเส้นทางไปยังสคริปต์ JS และฉีดเข้าไปในหน้า WeChat โดย Anyproxy // echo "<script> settimeout (function () {window.location.href = '". $ url. "';}, 2000); </script>"; int randomtime = new random (). nextint (3) + 3; string jscode = "<script> settimeout (function () {window.location.href = '"+url+"';},"+randomtime*1000+"); </script>"; ส่งคืน JScode; -ด้านบนเป็นโปรแกรมที่ประมวลผลข้อมูลที่สกัดกั้นโดยพร็อกซีเซิร์ฟเวอร์ มีปัญหาที่ต้องให้ความสนใจที่นี่ โปรแกรมจะดำเนินการเข้าถึง Round-Robin ไปยังแต่ละบัญชีอย่างเป็นทางการที่รวมอยู่ในฐานข้อมูลและแม้แต่บทความที่เก็บไว้จะถูกเข้าถึงอีกครั้ง วัตถุประสงค์คือเพื่ออัปเดตจำนวนมุมมองและความชอบของบทความ หากคุณต้องการรวบรวมข้อมูลบัญชีสาธารณะจำนวนมากขอแนะนำให้แก้ไขรหัสเพื่อเพิ่มคิวงานและเพิ่มข้อ จำกัด ตามเงื่อนไข มิฉะนั้นบัญชีอย่างเป็นทางการจะรวบรวมข้อมูลที่ซ้ำกันในหลายรอบและรอบจะส่งผลกระทบอย่างมากต่อประสิทธิภาพ
ณ จุดนี้ลิงก์บทความทั้งหมดของบัญชีอย่างเป็นทางการของ WeChat ถูกรวบรวมข้อมูลและลิงค์นี้ใช้ได้อย่างถาวรและสามารถเปิดได้ในเบราว์เซอร์ ถัดไปเขียนโปรแกรมตัวรวบรวมข้อมูลเพื่อรวบรวมข้อมูลเนื้อหาบทความและข้อมูลอื่น ๆ จากฐานข้อมูล
ฉันเป็นตัวรวบรวมข้อมูลที่เขียนด้วยเว็บมานจ์มันมีน้ำหนักเบาและใช้งานง่าย
ชั้นเรียนสาธารณะ Spidermodel ใช้ pageprocessor {postmapper postmapper ส่วนตัว รายการคงที่ส่วนตัว <Post> โพสต์; // การกำหนดค่าที่เกี่ยวข้องของเว็บไซต์รวบรวมข้อมูลรวมถึงการเข้ารหัสช่วงเวลาการรวบรวมข้อมูลเวลาลอง retry ฯลฯ ไซต์ส่วนตัว = site.me (). setRetryTimes (3) .SetSleeptime (100); เว็บไซต์สาธารณะ getSite () {// todo วิธีการที่สร้างขึ้นอัตโนมัติ stub return this.site; } กระบวนการโมฆะสาธารณะ (หน้าหน้า) {// todo วิธีการที่สร้างอัตโนมัติ stub โพสต์โพสต์ = posts.remove (0); สตริงเนื้อหา = page.gethtml (). xpath ("// div [@id = 'js_content']"). get (); // บทความแฮร์รี่ถูกกำหนดที่นี่ หากมีบันทึกการลบโดยตรงหรือตั้งค่าบิตการเป็นตัวแทนเพื่อระบุว่าบทความมีความกลมกลืนถ้า (เนื้อหา == null) {system.out.println ("บทความมีความกลมกลืน!"); //postmapper.deleteByPrimaryKey (post.getId ()); กลับ; } สตริง contentsNap = content.replaceall ("data-src", "src"). replaceall ("preview.html", "player.html"); // snapshot string contenttxt = htmltoword.striphtml (เนื้อหา); page.getHtml (). xpath ("// div [@id = 'meta_content']"); String PubTime = NULL; สตริง wxName = null; String Author = NULL; if (metacontent! = null) {pubtime = metacontent.xpath ("// em [@id = 'โพสต์วันที่']") รับ (); if (pubtime! = null) {pubtime = htmltoword.striphtml (pubtime); // เวลาการเผยแพร่บทความ} wxname = metacontent.xpath ("// a [@id = 'post-user']") if (wxName! = null) {wxName = htmlToword.striphtml (wxName); // ชื่อบัญชีสาธารณะ} ผู้แต่ง = metacontent.xpath ("// em [ @class = 'rich_media_meta rich_media_meta_text' if (ผู้เขียน! = null) {ผู้แต่ง = htmltoword.striphtml (ผู้เขียน); // ผู้เขียนบทความ}} // system.out.println ("เผยแพร่เวลา:"+pubtime); // system.out.println ("ชื่อบัญชีสาธารณะ:"+wxname); String title = post.getTitle (). replaceAll ("", ""); // บทความชื่อเรื่องสตริง digest = post.getDigest (); // บทความสรุป int likenum = post.getLikenum (); // บทความชอบ intreadnum = post.getReadNum (); // การอ่านบทความ ใหม่ wechatinfobean (); wechatbean.settitle (ชื่อ); wechatbean.setContent (contentTxt); // เนื้อหาข้อความธรรมดา weChatBean.setSourceCode (contentsNap); // snapshot weChatBean.setLikecount (Likenum); WeChatBean.SetViewCount (readnum); wechatbean.setabstracttext (Digest); // บทคัดย่อ wechatbean.seturl (contenturl); WeChatBean.SetPublishTime (PubTime); wechatbean.setsitename (wxName); // ชื่อเว็บไซต์ชื่อบัญชีสาธารณะ WeChatBean.setauthor (ผู้เขียน); wechatbean.setmediatype ("บัญชีอย่างเป็นทางการของ WeChat"); // ประเภทสื่อประเภท Wechatstorage.savewechatinfo (WeChatBean); // แท็กบทความได้รับการรวบรวมข้อมูล post.setisspider (1); postmapper.updateByPrimaryKey (โพสต์); } โมฆะคงที่สาธารณะ startSpider (รายการ <post> inposts, postmapper mypostmapper, สตริง ... urls) {starttime ยาว, endtime; startTime = system.currentTimeMillis (); postmapper = myPostMapper; โพสต์ = inposts; httpClientDownloader httpClientDownloader = ใหม่ httpClientDownloader (); Spidermodel Spidermodel = new Spidermodel (); Spider myspider = Spider.create (spidermodel) .addurl (URL); MySpider.SetDownLoader (httpClientDownloader); ลอง {SpiderMonitor.Instance (). ลงทะเบียน (MySpider); myspider.thread (1) .run (); } catch (jmexception e) {e.printstacktrace (); } endtime = system.currentTimeMillis (); System.out.println ("เวลารวบรวมข้อมูล" + ((endtime-starttime) / 1000) + "วินาที-"); -ฉันจะไม่โพสต์รหัสหน่วยเก็บข้อมูลตรรกะที่ไม่เกี่ยวข้องอื่น ๆ ที่นี่ฉันใส่ข้อมูลที่บันทึกโดยพร็อกซีเซิร์ฟเวอร์ใน MySQL และเก็บข้อมูลที่รวบรวมไว้โดยโปรแกรมตัวรวบรวมข้อมูลของฉันใน MongoDB
ด้านล่างนี้เป็นข้อมูลเกี่ยวกับหมายเลขบัญชีอย่างเป็นทางการที่คุณคลาน: