文章目錄
- 前言
- 專案背景
- 專案實作
- 1. 登陸
- 登陸頁面分析
- 登陸實作
- 登陸流程
- ① 獲取登陸二維碼并保存二維碼至本地
- ② 本地彈出二維碼
- ③ app掃碼確認——登陸成功
- 2. 抓取分析
- 介面引數分析
- 相關js逆向
- 資料抓取-資料保存
- 總結
前言
最近啃了一個新專案,是關于某群官網的某群成員獲取,本專案僅用于交流學習,若侵犯到貴公司權益請聯系郵箱229456906@qq.com第一時間洗掉,讀者請切忌用于一切非法途徑,否則后果自行承擔!
專案背景
前段時間在某外包群看到一個需求:獲取QQ上的QQ群成員號碼…等,剛好現在有時間就打開了某群官網,進行了一波操作,肝了一個晚上終于是整得明明白白了,于是專案[QQqunSpider]問世了hhh,其中在登陸方面得到了一位大佬的技術指導,很感謝!!!下面是專案目錄結構圖:

專案實作
本專案使用requests的方式進行全程獲取,登陸采用二維碼掃碼的方式代碼實作,因為目前沒找到賬密登錄的入口,網站好像并沒有,所以采用掃碼(如果有朋友知道賬密登陸的入口也可告知我,我會盡量去更新賬密登錄方式), 登錄獲取cookie,為之后的資料采集提供身份驗證,本專案實作并不難,從登陸到資料獲取入庫等流程較為簡單,下面正片開始~
1. 登陸
想要獲取資料,必須進行登錄處理,獲取cookie后才能實作完整的獲取流程,本次地址:aHR0cHM6Ly9xdW4ucXEuY29tLw==
登陸頁面分析
打開首頁并點擊登錄出現如圖:

可以看到沒有其他的登陸方式,只能使用app進行掃碼登陸,
接下來抓包,直接F12打開開發者工具點擊All然后重繪網頁點擊登陸即可:

首先看到這一條介面地址,他是一個圖片介面(也就是二維碼的介面):

看看他的Headers:

經過多次重復抓包知道他是一個GET請求,提交的引數沒有加密,并且所有引數均可寫死,
登陸實作
獲取二維碼并在本地呼叫彈出二維碼進行掃描登陸
登陸流程
① 獲取登陸二維碼并保存二維碼至本地
現在只需要撰寫python代碼請求二維碼介面即可,定義LoginFuncRequests類并初始化引數:
def __init__(self):
self.session = requests.session()
self.img_path = 'cookieserverfunction/image/code.png'
self.headers = {
'Host': 'ssl.ptlogin2.qq.com',
'Referer': 'https://xui.ptlogin2.qq.com/',
'sec-ch-ua': '"Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36',
}
撰寫code_show方法獲取二維碼并保存:
def code_show(self):
url = 'https://ssl.ptlogin2.qq.com/ptqrshow?'
data = {
'appid': '715030901',
'e': '2',
'l': 'M',
's': '3',
'd': '72',
'v': '4',
't': '0.9292435301685353',
'daid': '73',
'pt_3rd_aid': '0',
}
response = self.session.get(url, params=data, headers=self.headers)
with open('cookieserverfunction/image/code.png', 'wb') as f:
f.write(response.content)
f.close()
② 本地彈出二維碼
這里需要注意的是當二維碼獲取到并彈出后服務器會定時驗證二維碼的有效性(包括是否過期,是否掃描,是否確認):

介面如圖:

回應的內容:

所以這一步我們需要先處理這條介面請求,驗證二維碼是否被掃描是否過期或者確認,
先看看他的Headers,以及各個引數情況:

其中ptqrtoken、login_sig、action是變動的,首先看看ptqrtoken,搜索:

只有一條記錄, 打上斷點:

先看這段代碼:
“ptqrlogin” == t && (o.ptqrtoken = k[“default”].str.hash33(k[“default”].cookie.get(“qrsig”)))
大致意思就是ptqrlogin=未知函式(從cookie中獲取qrsig)
接下來單步執行進入下一步:

繼續執行到斷點處, 并在控制臺列印回傳值:

所以只需要知道cookie中的qrsig引數即可, 這里嘗試一下搜索t的值進行搜索(因為qrsig=t=hNHL52PPCVBd6O45-6RCIEvRXjzt6ag36EUJKxX-VkkmRTdTJdDMYPTHDR*cUg):
需要注意的是全域搜索的字串不能含有特殊字符,否則會影響搜索結果,所以這里截取t的部分字符進行搜索:

很清楚明了的看到:t是二維碼介面回傳的cookie值;
所以我們只需要在獲取二維碼的時候將cookie回傳獲取下來即可:
def code_show(self):
url = 'https://ssl.ptlogin2.qq.com/ptqrshow?'
data = {
'appid': '715030901',
'e': '2',
'l': 'M',
's': '3',
'd': '72',
'v': '4',
't': '0.9292435301685353',
'daid': '73',
'pt_3rd_aid': '0',
}
response = self.session.get(url, params=data, headers=self.headers)
with open('cookieserverfunction/image/code.png', 'wb') as f:
f.write(response.content)
f.close()
print(response.cookies)
return response.cookies
接下來將js扣出改寫,并撰寫get_ptqrtoken函式獲取ptqrtoken引數:
js代碼:
function ptqrtoken(t) {
for (var e = 0, i = 0, n = t.length; i < n; ++i) e += (e << 5) + t.charCodeAt(i);
return 2147483647 & e
}
get_ptqrtoken函式:
def get_ptqrtoken(self, cookies):
with open('lib/bkn.js', 'r') as f:
js = f.read()
f.close()
com = execjs.compile(js)
ptqrtoken = com.call('ptqrtoken', re.search(r'qrsig=(.*?) ', str(cookies)).group(1))
print('ptqrtoken:', ptqrtoken)
return ptqrtoken
現在看看action引數:action引數(0-0-1614435984861)類似于x-x-13位時間戳(先假設是這樣等下請求驗證便知)
接下來看看login_sig引數:
這里清楚login_sig=S.ptui.login_sig, 我們搜索S.ptui.login_sig:

打下斷點并重繪網頁再次點擊登錄:

通過控制臺輸出,以及代碼段:S.ptui.login_sig = S.ptui.login_sig || k[“default”].cookie.get(“pt_login_sig”)可以清楚的知道S.ptui.login_sig等于cookie中的pt_login_sig值, 接下來進行pt_login_sig值得搜索:

經過多次觀察Headers,提交的引數為固定值
撰寫pt_login_sig函式代碼獲取cookie即可:
def pt_login_sig(self):
url = 'https://xui.ptlogin2.qq.com/cgi-bin/xlogin?'
data = {
'pt_disable_pwd': '1',
'appid': '715030901',
'daid': '73',
'pt_no_auth': '1',
's_url': 'https://qun.qq.com/',
}
headers = {
'referer': 'https://qun.qq.com/',
'sec-ch-ua': '"Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36',
}
response = self.session.get(url, params=data, headers=headers)
print(response.cookies)
login_sig = re.search(r'pt_login_sig=(.*?) ', str(response.cookies)).group(1)
print('login_sig:', login_sig)
return login_sig, response.cookies
所有引數獲取完成,下面撰寫check_Codeshow函式向服務器發送請求:
def check_Codeshow(self, ptqrtoken, login_sig, ck):
url = 'https://ssl.ptlogin2.qq.com/ptqrlogin?'
action = int(time.time() * 1000)
data = {
'u1': 'https://qun.qq.com/',
'ptqrtoken': ptqrtoken,
'ptredirect': '1',
'h': '1',
't': '1',
'g': '1',
'from_ui': '1',
'ptlang': '2052',
'action': '0-0-' + str(action),
'js_ver': '21020514',
'js_type': '1',
'login_sig': login_sig,
'pt_uistyle': '40',
'aid': '715030901',
'daid': '73',
}
headers = {
'referer': 'https://xui.ptlogin2.qq.com/',
'sec-ch-ua': '"Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36',
}
response = self.session.get(url, params=data, headers=headers, cookies=ck)
print(response.text)
return response.text
將圖片顯示(showc函式)結合check_Codeshow函式:
def showc(self, ptqrtoken, login_sig, ck):
# 顯示二維碼
img = Image.open(self.img_path) # 打開圖片,回傳PIL image物件
plt.figure("請使用app掃描驗證碼", figsize=(5, 4))
plt.ion() # 打開互動模式
plt.axis('off') # 不需要坐標軸
plt.imshow(img)
global pausing
pausing = True
while pausing:
plt.pause(0.05)
while True:
bg = self.check_Codeshow(ptqrtoken, login_sig, ck)
if '登錄成功' in str(bg):
pausing = False
ebg = 1
break
if '二維碼認證中' in str(bg):
pass
if '二維碼已失效' in str(bg):
pausing = False
ebg = 0
break
if '本次登錄已被拒絕' in str(bg):
pausing = False
ebg = 0
break
time.sleep(1)
plt.ioff() # 顯示完后一定要配合使用plt.ioff()關閉互動模式,否則可能出奇怪的問題
plt.clf() # 清空圖片
plt.close() # 清空視窗
return ebg, bg
③ app掃碼確認——登陸成功
然后我們使用app掃碼確認,再次抓包分析:

當我們點擊確認之后緊接著服務器請求了一條介面:

并且設定了一堆cookie:

所以現在需要解決這條介面的引數,并進行模擬請求,先看看請求表單:

我設想,會不會是上面的check_Codeshow函式想服務器發送請求的時候,在我們確認之后請求回應回傳的?帶著設想查看了response:

并沒有…然后我通過代碼進行debug除錯:

發現了有回傳值,并且其中的鏈接就是上面的介面地址,所以現在只需要將
check_Codeshow函式的回傳值稍作處理提取鏈接,然后再進行求情獲取登陸cookie即可,
get_cookie函式:
def get_cookie(self, url):
headers = {
'referer': 'https://xui.ptlogin2.qq.com/',
'sec-ch-ua': '"Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36',
}
response = self.session.get(url, headers=headers, allow_redirects=False)
print(response.cookies)
return response.cookies
整合代碼:
# -- coding: utf-8 --
# @Time : 2021/2/25 20:25
# @Author : Los Angeles Clippers
# @Email: 229456906@qq.com
# @sinaemail: angelesclippers@sina.com
import pickle
import re
import time
import execjs
import requests
from PIL import Image
import matplotlib.pyplot as plt
class LoginFuncRequests(object):
def __init__(self):
self.session = requests.session()
self.img_path = 'cookieserverfunction/image/code.png'
self.headers = {
'Host': 'ssl.ptlogin2.qq.com',
'Referer': 'https://xui.ptlogin2.qq.com/',
'sec-ch-ua': '"Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36',
}
def code_show(self):
url = 'https://ssl.ptlogin2.qq.com/ptqrshow?'
data = {
'appid': '715030901',
'e': '2',
'l': 'M',
's': '3',
'd': '72',
'v': '4',
't': '0.9292435301685353',
'daid': '73',
'pt_3rd_aid': '0',
}
response = self.session.get(url, params=data, headers=self.headers)
print(response.status_code)
with open('cookieserverfunction/image/code.png', 'wb') as f:
f.write(response.content)
f.close()
print(response.cookies)
return response.cookies
def get_ptqrtoken(self, cookies):
with open('lib/bkn.js', 'r') as f:
js = f.read()
f.close()
com = execjs.compile(js)
ptqrtoken = com.call('ptqrtoken', re.search(r'qrsig=(.*?) ', str(cookies)).group(1))
print('ptqrtoken:', ptqrtoken)
return ptqrtoken
def pt_login_sig(self):
url = 'https://xui.ptlogin2.qq.com/cgi-bin/xlogin?'
data = {
'pt_disable_pwd': '1',
'appid': '715030901',
'daid': '73',
'pt_no_auth': '1',
's_url': 'https://qun.qq.com/',
}
headers = {
'referer': 'https://qun.qq.com/',
'sec-ch-ua': '"Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36',
}
response = self.session.get(url, params=data, headers=headers)
print(response.cookies)
login_sig = re.search(r'pt_login_sig=(.*?) ', str(response.cookies)).group(1)
print('login_sig:', login_sig)
return login_sig, response.cookies
def check_Codeshow(self, ptqrtoken, login_sig, ck):
url = 'https://ssl.ptlogin2.qq.com/ptqrlogin?'
action = int(time.time() * 1000)
data = {
'u1': 'https://qun.qq.com/',
'ptqrtoken': ptqrtoken,
'ptredirect': '1',
'h': '1',
't': '1',
'g': '1',
'from_ui': '1',
'ptlang': '2052',
'action': '0-0-' + str(action),
'js_ver': '21020514',
'js_type': '1',
'login_sig': login_sig,
'pt_uistyle': '40',
'aid': '715030901',
'daid': '73',
}
headers = {
'referer': 'https://xui.ptlogin2.qq.com/',
'sec-ch-ua': '"Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36',
}
response = self.session.get(url, params=data, headers=headers, cookies=ck)
print(response.text)
# print(response.cookies)
return response.text
def showc(self, ptqrtoken, login_sig, ck):
# 顯示二維碼
img = Image.open(self.img_path) # 打開圖片,回傳PIL image物件
plt.figure("請使用app掃描驗證碼", figsize=(5, 4))
plt.ion() # 打開互動模式
plt.axis('off') # 不需要坐標軸
plt.imshow(img)
global pausing
pausing = True
while pausing:
plt.pause(0.05)
while True:
bg = self.check_Codeshow(ptqrtoken, login_sig, ck)
if '登錄成功' in str(bg):
pausing = False
ebg = 1
break
if '二維碼認證中' in str(bg):
pass
if '二維碼已失效' in str(bg):
pausing = False
ebg = 0
break
if '本次登錄已被拒絕' in str(bg):
pausing = False
ebg = 0
break
time.sleep(1)
plt.ioff() # 顯示完后一定要配合使用plt.ioff()關閉互動模式,否則可能出奇怪的問題
plt.clf() # 清空圖片
plt.close() # 清空視窗
return ebg, bg
def get_cookie(self, url):
headers = {
'referer': 'https://xui.ptlogin2.qq.com/',
'sec-ch-ua': '"Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36',
}
response = self.session.get(url, headers=headers, allow_redirects=False)
print(response.cookies)
return response.cookies
def run(self):
cookies = self.code_show()
ptqrtoken = self.get_ptqrtoken(cookies=cookies)
login_sig, ck = self.pt_login_sig()
ebg, bg = self.showc(ptqrtoken, login_sig, ck)
if ebg == 0:
self.run()
else:
url = re.search(r"ptuiCB\('0','0','(.*?)'", bg).group(1)
print(url)
lck = self.get_cookie(url=url)
cookie_name = re.search(r'uin=(.*?)&', bg).group(1)
with open("cookied/{}_cookie.txt".format(cookie_name), 'w') as f:
f.write(str(lck))
return lck, cookie_name
獲取cookie結果:

到這里登陸成功,并且成功獲取cookie,
2. 抓取分析
點擊群管理 →成員管理 ,到以下界面:

介面引數分析
F12打開開發者工具,隨便點擊一個群進行抓包:

看看回應:

所以這里就是我們想要的資訊,看看表單引數:

相關js逆向
這里的gc是群號碼,bkn不知道,先搜索一波:

打下斷點,并重繪網頁:

通過控制臺列印,他就是bkn引數,進入函式看看:

打下斷點后我們再執行到斷點處:

通過控制臺列印,這段js代碼即是bkn的演算法:
o.getCSRFToken = function() {
var e = o.cookie("skey");
if (e) {
for (var t = 5381, n = 0, r = e.length; n < r; ++n)
t += (t << 5) + e.charAt(n).charCodeAt();
return this.CSRFToken = 2147483647 & t
}
}
通過var e = o.cookie(“skey”)可知, e等于cookie中的skey的值,所以在這里將cookie的skey值取出,傳入js函式即可得到bkn的值,撰寫getbkn函式:
def getbkn(self, skey):
with open('lib/bkn.js', 'r') as f:
js = f.read()
f.close()
com = execjs.compile(js)
bkn = com.call('getbkn', skey)
print(bkn)
return bkn
資料抓取-資料保存
可以看到我的qq上有很多個群,所以如果是一個一個qq群號碼去寫的話很麻煩,所以看看服務器有沒有回傳我qq上的所有qq群,隨意找個群號進行搜索:

有結果了,看看Headers:

只有一個bkn引數,接下來撰寫get_gc函式獲取所有的群號碼串列:
def get_gc(self, cookies, bkn):
data = {
'bkn': bkn
}
response = requests.post(self.url, headers=self.headers, data=data, cookies=cookies)
jsdata = json.loads(response.text)
print(jsdata)
gc = re.findall(r"'gc': (.*?),", str(jsdata))
gn = re.findall(r"'gn': '(.*?),", str(jsdata))
print(gc)
print(gn)
print(len(gc), len(gn))
return (gc, gn)
加下來定義SearchGroupMembers類進行資料的獲取:
首先定義初始化引數:
def __init__(self, gcgn, bkn, cookies):
self.url = 'https://qun.qq.com/cgi-bin/qun_mgr/search_group_members'
self.bkn = bkn
self.gc = gcgn[0]
self.gn = gcgn[1]
self.cookies = cookies
self.fp = open('datas/qq.csv', 'a', encoding='gb18030', newline='')
self.fw = csv.writer(self.fp)
self.fps = open('datas/qq_email.csv', 'a', encoding='gb18030', newline='')
self.fws = csv.writer(self.fps)
self.headers = {
'origin': 'https://qun.qq.com',
'pragma': 'no-cache',
'referer': 'https://qun.qq.com/member.html',
'sec-ch-ua': '"Chromium";v="88", "Google Chrome";v="88", ";Not A Brand";v="99"',
'sec-ch-ua-mobile': '?0',
'sec-fetch-dest': 'empty',
'sec-fetch-mode': 'cors',
'sec-fetch-site': 'same-origin',
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36',
}
下面撰寫獲取資料函式getMembers:
def getMembers(self):
o = 0
for gc, gn in zip(self.gc, self.gn):
print(gn)
i = 0
for pg in range(0, 1000000, 21):
data = {
'gc': gc,
'st': pg,
'end': 20 + pg,
'sort': '0',
'bkn': self.bkn,
}
response = requests.post(self.url, headers=self.headers, cookies=self.cookies, data=data)
jsdata = json.loads(response.text)
try:
mems = jsdata['mems']
except KeyError as e:
break
if len(mems) == 0:
break
for item in mems:
i += 1
o += 1
qq = item['uin']
qq_email = str(qq) + '@qq.com'
nick = item['nick']
print(i, o, qq, nick)
self.fw.writerow([qq, nick])
self.fws.writerow([str(qq), qq_email, nick])
self.fp.close()
self.fps.close()
啟動函式:
from cookieserverfunction.logincodeshow import LoginFuncRequests
from CrawlSpider.get_group_list import GetGroupList
from CrawlSpider.search_group_members import SearchGroupMembers
if __name__ == '__main__':
logins = LoginFuncRequests()
lck, cookie_name = logins.run()
ggl = GetGroupList()
gcgn, bkn, cookies = ggl.run(lck, cookie_name)
sgm = SearchGroupMembers(gcgn, bkn, cookies)
sgm.getMembers()
執行結果:

資料保存情況:

挺意外我群友居然有將近5w個的hhhhh~
總結
好好學習 天天向上 不掉頭發 事業有成~
碼字不易,如果不能篇文章對你有幫助請點個贊,謝謝~
合作及原始碼獲取vx:tiebanggg 【注明來意】
QQ交流群:735418202
需原始碼請關注微信公眾號回復【QQqunSpider】獲取 :
*注:本文為原創文章,轉載文章請附上本文鏈接!否則將追究相關責任,請自重!謝謝!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/264844.html
標籤:python
