1. 概述
閑來無事澳化,做一個(gè)微信刷票工具持寄,只是驗(yàn)證了技術(shù)思路是否可行源梭。不涉及任何商業(yè)利益娱俺!
2. 技術(shù)預(yù)研
微信投票工具很多,有一些是微信官方的废麻,這個(gè)可能沒辦法破解荠卷;有一些是需要關(guān)注公眾號,與微信深度綁定烛愧,不容易破解油宜。
剩下一些,要么只獲取了微信openid怜姿,或者只通過ip做了限制慎冤,破解起來簡單一些,可以一試沧卢。
3. 破解思路
3.1. 確認(rèn)投票機(jī)制
打開手機(jī)微信蚁堤,進(jìn)入投票頁面,下拉可以看到投票網(wǎng)站的地址搏恤,如果不是wx的违寿,可以嘗試搞一下子
通過電腦chrome瀏覽器模擬微信瀏覽器湃交,打開投票頁面熟空,確認(rèn)投票api
確認(rèn)投票api比較麻煩,需要慢慢摸索搞莺,有時(shí)候需要看看上下文的請求息罗,有時(shí)候可能需要看看js源碼,分析投票機(jī)制才沧,也需要反復(fù)嘗試
# 在chrome增加一個(gè)device迈喉,user-agent設(shè)置為wechat瀏覽器即可
Mozilla/5.0 (Linux; Android 7.0; MI 5s Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/64.0.3282.137 Mobile Safari/537.36 wxwork/2.4.16 MicroMessenger/6.3.22 NetType/WIFI Language/zh
Chrome瀏覽器模擬微信瀏覽器
Chrome 修改 user agent 簡單模擬微信內(nèi)置瀏覽器
3.2. 編寫投票腳本
基于#2.1的探索,如果只是基于IP的投票限制温圆,反而簡單許多挨摸。
可以搞一個(gè)代理IP池,通過代理IP池發(fā)起投票request岁歉,基本可以成功得运。
代理IP池,依賴免費(fèi)代理源
锅移,定時(shí)抓取page解析得到可用的代理IP
https://github.com/jhao104/proxy_pool
免費(fèi)的可用率太低熔掺,大概1/10,網(wǎng)速也比較慢非剃;做爬蟲的話置逻,可以買一個(gè)收費(fèi)的,當(dāng)前場景夠用
- 代碼clone下來之后备绽,如何執(zhí)行
DB_CONN=redis://127.0.0.1:6379/0 sh start.sh
- 也可以通過docker啟動
docker run --network host --env DB_CONN=redis://127.0.0.1:6379/0 -p 5010:5010 jhao104/proxy_pool
投票腳本
import requests
from retry import retry
import random
import time
import string
import hashlib
import pickle
import pathlib
def get_user_agent():
android_user_agents = [
'Mozilla/5.0 (Linux; Android 7.0; MI 5s Build/NRD90M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/64.0.3282.137 Mobile Safari/537.36 wxwork/2.4.16 MicroMessenger/6.3.22 NetType/WIFI Language/zh',
'Mozilla/5.0 (Linux; Android 9; PCNM00 Build/PKQ1.190630.001; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/045129 Mobile Safari/537.36 MMWEBID/1926 MicroMessenger/7.0.12.1620(0x27000C37) Process/tools NetType/WIFI Language/zh_CN ABI/arm64',
'Mozilla/5.0 (Linux; Android 10; LYA-AL00 Build/HUAWEILYA-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/045130 Mobile Safari/537.36 MMWEBID/9034 MicroMessenger/7.0.12.1620(0x27000C36) Process/tools NetType/WIFI Language/zh_CN ABI/arm64',
'Mozilla/5.0 (Linux; Android 9; Redmi Note 8 Pro Build/PPR1.180610.011; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/67.0.3396.87 XWEB/1171 MMWEBSDK/200201 Mobile Safari/537.36 MMWEBID/229 MicroMessenger/7.0.12.1620(0x27000C37) Process/tools NetType/WIFI Language/zh_CN ABI/arm64',
'Mozilla/5.0 (Linux; Android 10; MI 8 Build/QKQ1.190716.003; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/67.0.3396.87 XWEB/1171 MMWEBSDK/191201 Mobile Safari/537.36 MMWEBID/4454 MicroMessenger/7.0.10.1580(0x27100AFF) Process/toolsmp NetType/WIFI Language/zh_CN ABI/arm32',
'Mozilla/5.0 (Linux; Android 9; BKL-AL20 Build/HUAWEIBKL-AL20; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/045130 Mobile Safari/537.36 MMWEBID/7838 MicroMessenger/7.0.11.1600(0x27000B34) Process/tools NetType/4G Language/zh_CN ABI/arm64',
'Mozilla/5.0 (Linux; Android 10; VOG-AL00 Build/HUAWEIVOG-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/66.0.3359.126 MQQBrowser/6.2 TBS/045130 Mobile Safari/537.36 MMWEBID/2687 MicroMessenger/7.0.10.1580(0x27000AFE) Process/tools NetType/WIFI Language/zh_CN ABI/arm64',
]
return random.choice(android_user_agents)
def get_req_key():
letters = random.sample('abcdefghijklmnopqrstuvwxyz', 4)
digits = random.sample(string.digits, 4)
return ''.join([f'{x}{y}' for x, y in zip(digits, letters)])
def get_hash(key):
hash_str = hashlib.md5()
hash_str.update(key.encode('utf-8'))
return hash_str.hexdigest()
def get_proxy():
return requests.get("http://127.0.0.1:5010/get/").json()
def delete_proxy(proxy):
requests.get("http://127.0.0.1:5010/delete/?proxy={}".format(proxy))
def pickle_dump(data, file):
with open(file, 'wb') as f:
pickle.dump(data, f)
def pickle_load(file):
with open(file, 'rb') as f:
return pickle.load(f)
@retry(tries=3, delay=1)
def fake_req(proxy):
req_key = get_req_key()
user_agent = get_user_agent()
url = f'http://{req_key}.gd-cj.com/index.php?g=Wap&m=Vote&a=ticket'
headers = {
'Content-Length': '114',
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
'User-Agent': user_agent,
'Origin': f'http://{req_key}.gd-cj.com',
'Host': f'{req_key}.gd-cj.com',
'Referer': f'http://{req_key}.gd-cj.com/index.php?g=Wap&m=Vote&a=detail&token=Fxea5C5sj3562xdE&id=556&zid=2230',
'X-Requested-With': 'XMLHttpRequest'
}
form_data = dict(
zid='2230', vid='556', token='Fxea5C5sj3562xdE',
__hash__=f'{get_hash(req_key)}_{get_hash(user_agent)}'
)
print(f'proxy: {proxy}')
resp = requests.post(url, headers=headers, data=form_data,
proxies={"http": "http://{}".format(proxy)},
timeout=10)
print(f'response: {resp.status_code} - {resp.content}')
try:
resp_json = resp.json()
return resp_json.get('status') == 108
except:
pass
return False
if __name__ == '__main__':
used_proxy_file = './proxy_list.pkl'
total, success = 0, 0
proxy_list = pickle_load(used_proxy_file) if pathlib.Path(used_proxy_file).exists() else []
proxy = None
while True:
try:
proxy = get_proxy().get("proxy")
delete_proxy(proxy)
if proxy is None:
time.sleep(10)
continue
if proxy in proxy_list:
time.sleep(0.1)
continue
proxy_list.append(proxy)
pickle_dump(proxy_list, used_proxy_file)
total += 1
if fake_req(proxy):
success += 1
print('=================== bingo')
except Exception as e:
# print(f'exception: {e}')
pass
print(f'success: {success}, total: {total}')
time.sleep(random.randint(1, 5))
4. 總結(jié)
時(shí)間倉促券坞,只是走通思路鬓催,代碼沒有仔細(xì)設(shè)計(jì)與調(diào)整,進(jìn)而更加仿真报慕,減少被發(fā)現(xiàn)的可能深浮,比如
- request里面
__hash__
字段的生成機(jī)制(隨意生成的,貌似不要也行眠冈。飞苇。) - req_key的作用是啥(只是仿照原來的規(guī)則,生成了一個(gè))
IP代理工具
jhao104/proxy_pool
很贊蜗顽,省了不少事情布卡。做了幾個(gè)小優(yōu)化:
- 有一個(gè)bugfix
- 解析免費(fèi)代理源,只看了第一頁雇盖,修改為前10頁
- 內(nèi)部還有一些邏輯可以優(yōu)化
道高一尺忿等,魔高一丈,技術(shù)就是在不斷的battle情況下崔挖,才日益精進(jìn)