文章目錄
- 前言
- 用Charles 抓包 iOS 微信小程序
- 在Mac端和iOS端安裝Charles 自簽名證書
- Mac端
- iOS端
- 能抓到Safari瀏覽器的包但是抓不到微信小程序的包
- 直接在iOS 上抓包的App
- 如何抓取Android 7.0 以上/Harmony OS微信小程序包
- Python 項目工程化
- pip 切換為國內鏡像源
- 工程化參考
- 腳手架
- Python 虛擬環境
- 實現爬蟲
- 動態IP
- 確保代理服務器的延遲夠低
- 設置User-Agent
- 發起爬蟲請求
- 設置請求的證書
- 關閉其他科學上網工具
- 使用多線程提高并發
- 推送爬取結果到iOS
- 未解決的問題
- 打碼平臺破解驗證碼
- 備注
前言
公司準備用Python替換傳統的Shell來做自動化運維,最近正好在做這方面的code review,試著用Python寫一個小爬蟲,順便入門一下Python。
該爬蟲的功能是:
- 對目標小程序定時發起下訂單請求
- 將下訂單請求結果推送到iOS以提醒成功或失敗
用Charles 抓包 iOS 微信小程序
原理是:電腦和手機處于同一網絡中,在電腦上安裝Charles,電腦和手機都安裝Charles 自簽名證書,然后更改手機網絡設置,將手機上的網絡請求轉發至電腦上的Charles以實現抓包
在Mac端和iOS端安裝Charles 自簽名證書
Mac端
設置步驟見Charles 文檔 > SSL Certificates > MacOS部分。
- 安裝完畢 在 鑰匙串訪問 中設置為始終信任
- 在 Proxy > SSL Proxy Settings > Include 中添加任意域名 ,然后在瀏覽器中訪問該網站來測試 Charles是否可以解析HTTPS數據包。如果想對所有流量進行抓包,域名設置為*, 端口設置為443,不過不建議這樣做,這樣做會有大量的我們不關心的包,會對我們造成干擾。這種做法建議只用于測試配置是否正確
iOS端
設置步驟見Charles 文檔 > SSL Certificates > iOS 部分 。
- 在iOS 瀏覽器中下載且安裝完證書之后,在設置 > 通用 > 關于本機 > 證書信任設置 中啟用該證書
- 修改網絡設置 > 配置代理 > 手動 > IP (可以在Charles > Help > Local IP Address 中找到),Port 一般為8888
- 打開瀏覽器 訪問任意網站,注意:這里要和Charles 中的 SSL Proxy Settings > Include 對應。查看Charles是否可以解析HTTPS數據
能抓到Safari瀏覽器的包但是抓不到微信小程序的包
經過上面的測試之后,開始正式抓小程序的包。 Mac端和iOS端 的瀏覽器都可以抓到包,不過卻發現無法抓小程序的包。 Google了一下發現,需要打開 設置 > App > 微信 > 本地網絡
感謝這位博主的分享
直接在iOS 上抓包的App
為什么會有這個需求呢?是因為 我覺得在iOS上抓包這么做太麻煩了,想著有沒有直接可以安裝在iOS上的App,還真有
- Charles iOS版,58 人民幣,還未購買使用
- Stream,免費。但是經過實際測試,該App已經很久沒更新了,無法抓HTTPS包
- 蜻蜓抓包,免費。未經過測試使用
如果還有其他好用的iOS抓包 App,歡迎評論區留言推薦
如何抓取Android 7.0 以上/Harmony OS微信小程序包
在另一臺華為手機 微信中抓包發現抓不到,系統為harmony os 4.2。Google了一下,發現很多人都有這個問題。簡單來說,在 Android7.0 及以上的系統中,App只信任系統預裝證書而不信任用戶安裝的證書。由于主力機是iPhone,我就沒有深入研究如何解決這個問題,想解決這個問題可參考 知乎回答 和 另外一位博主分享
Python 項目工程化
經過上面的抓包,拿到了目標小程序的請求格式和數據格式。開始寫Python腳本,既然是個項目,不如從一開始就規范起來,使用企業級的Python項目工程化結構,包括:使用流行的包管理工具,代碼風格,代碼風格檢測,單元測試,打包等等
pip 切換為國內鏡像源
最好切換為國內鏡像源,這樣下載包更快更穩定。
我使用清華大學的鏡像源
pip config set global.index-url https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple
pip config set global.trusted-host mirrors.tuna.tsinghua.edu.cn
工程化參考
關于Python項目的工程化我使用的python項目工程化指南里提到的一些組件,建議閱讀該指南并學習其中 快速上手 章節的小例子
腳手架
- 上述指南中使用的腳手架是cookiecutter,如果你也使用,需要在初始化好之后升級一些依賴的版本,腳手架生成的一些依賴的版本現在比較低了,如果直接使用會報錯
- cookiecutter 的提交信息提示已經是5年前了,另外一個比較活躍的腳手架項目是pyscaffold。我還沒研究,等后面研究透了再在其他博文中詳細介紹。
本文使用的cookiecutter
Python 虛擬環境
Python虛擬環境推薦使用poetry。激活Python項目的虛擬環境命令見poetry > Managing environments文檔
實現爬蟲
根據上述指南生成項目腳手架且升級了相關依賴之后,開始正式寫爬蟲代碼。核心爬蟲邏輯就幾行,構造下訂單數據,然后使用requests發起請求
動態IP
在測試過程中,發現目標小程序添加了同一個IP 1秒內不能訪問2次的限制。找了一家國內付費的TLS代理服務的公司,買了個IP池。國內付費HTTP/TLS代理的公司對于個人用戶推出的套餐基本差不多,有按照數量計費的,有按照小時/天計費的。我選的是按數量計費并且所選IP失效為5分鐘左右。
在選擇具體產品時要關注
- IP得是 高度匿名代理,這樣才不會被目標服務器追蹤到原始客戶端IP
- IP質量,代理IP要夠穩定且延遲低
確保代理服務器的延遲夠低
要讓代理請求到目標小程序的延遲夠低,首先選擇的IP最好和目標小程序所在區域一樣。經過上面的抓包,得知小程序的域名,使用dig命令通過域名查到該域名的IP
dig www.xxxx.com
再通過IP歸屬地查詢得知,該小程序部署在阿里云 > 華北區域 > 青島
在接入HTTP代理服務時,商家一般提供可選城市,選擇離青島最近的城市即可。
當然了還要綜合考量,不一定離目標網站所在地越近的代理IP速度越快,應該還和商家自身的硬件部署有關,所以如果發現離目標網站所在地的IP反而更慢,那就果斷切換至其他節點
即使完成了上述步驟,商家給你的IP池不一定每一個延遲都很低,所以在拿到IP之后,要測試一下該IP到目標小程序的速度,如果速度不滿足則丟棄重新獲取新的IP,重復上述步驟直至獲得滿足延遲的IP
"""get low latency proxy servers"""
import json
import logging
import timeimport requestslogger = logging.getLogger(__name__)PROXY_SERVICE_PROVIDER_URL = ("這里是HTTP代理服務商家的接入API")
TARGET_SERVER_URL = "這里是目標小程序的API"def get_proxy_ips_latency_less_than_one_second(need_ip_nums: int) -> dict:"""return a set of ip latency less than 1 second"""good_proxy_ip = {}request_start_time = time.time()proxy_index = 1while len(good_proxy_ip) < need_ip_nums :reponse = requests.get(PROXY_SERVICE_PROVIDER_URL, timeout=5)logger.debug(json.dumps(reponse.json(), indent=4, ensure_ascii=False))ip = reponse.json()["data"][0]["ip"]port = reponse.json()["data"][0]["port"]proxy_ip = f'http://{ip}:{port}'proxies = {"http": proxy_ip,"https": proxy_ip}if test_proxy_delay(proxies, TARGET_SERVER_URL, 1):good_proxy_ip[proxy_index] = proxiesproxy_index += 1request_end_time = time.time()logger.info("successfully get a batch of proxy IPs with a delay""of less than or equals 1 second, cost %d seconds", request_end_time - request_start_time)logger.info("IP proxies:\n%s", good_proxy_ip)return good_proxy_ip
def test_proxy_delay(request_proxies, target_url: str, request_timeout: int) -> bool:"test proxy ip's latency whether less than timeout seconds"request_start_time = time.time()logger.debug("start time: %s", time.strftime("%H:%M:%S", time.localtime(request_start_time)))# add try-except block to avoid program crashestry:response = requests.get(target_url, timeout=5, proxies=request_proxies)if response.status_code == 200 and response.json().get("code") == 200 and response.json().get("msg") == "操作成功":request_end_time = time.time()logger.debug("end time: %s", time.strftime("%H:%M:%S", time.localtime(request_end_time)))delay = request_end_time - request_start_timelogger.debug("Proxy %s response time: %.2f seconds", request_proxies, delay)return delay <= request_timeoutreturn Falseexcept requests.exceptions.RequestException as e:logger.error("Error testing proxy %s: %s", request_proxies, str(e))return False
設置User-Agent
偽造 User-Agent,使用fake-useragent。使用該庫時注意,多次請求/多線程 應只使用同一個對象,避免多次初始化對象浪費時間
ua = UserAgent(platforms='mobile')
ua.random
發起爬蟲請求
最終在發起請求時,設置reqeusts的proxies即可
response = requests.post(request_url,request_json_data, headers=request_header,timeout=5, verify=CERT_PATH, proxies=request_proxies)
設置請求的證書
分兩種情況
- 如果你想在請求過程中開著Charles,這時的證書來自Charles。把Charles的證書保存下來,這時CERT_PATH是Charles自己證書的路徑。Charles 官方文檔 > Python 提到了這一點
- 如果你已經抓到了所需要的目標小程序的數據格式,那么請求時無需再開Charles代理。這時的證書來自 瀏覽器打開 目標小程序 域名 > 查看證書 > 下載 。下載前點擊證書詳情,并確保這個證書的名字不帶有Charles,如果有,則關閉Charles并在瀏覽器中 刪除目標小程序的cookie,重新加載,即可獲得小程序后臺服務器的證書
關閉其他科學上網工具
有的科學上網工具,如果你沒設置好,無論國內國外的流量都會先經過它的節點,這樣請求反而是慢了很多
使用多線程提高并發
我這里還沒有使用scheduler,只是使用 threading.Thread方法。線程的執行邏輯是,線程啟動之后,即準備數據,即
- 獲得一個延遲足夠低的代理IP
- 構造請求數據
- 在requests.post前一行計算當前時間距離目標時間的毫秒數,然后time.sleep 休眠。等到目標時間一到,所有線程立刻同時發起請求而無需等待其他步驟
推送爬取結果到iOS
由于該爬蟲是定時執行,有的時候不一定在家。所以需要一個將爬取結果推送到iOS上。
鑒于APNs即Apple Push Notification service 有點復雜且不想花時間在上面,于是使用Bark來幫助快速開發。接入步驟非常簡單,建議閱讀文檔
未解決的問題
經過上述步驟,mini版的爬蟲基本滿足了自己的需求,即定時下單并推送消息到iOS提醒我付款。但是有以下幾個問題未解決
- token 失效和刷新機制尚不清楚且獲取token的請求我也沒找到。導致每次真正爬取之前都要驗證下token是否失效,如果失效了,還得重新抓取請求獲得有效token
- 該小程序防止爬蟲請求只在用戶ID層面制訂了防護策略,即只允許同一用戶下兩單,并沒有接入驗證碼之類的防護
打碼平臺破解驗證碼
如果未來該小程序接入了驗證碼,那么我決定使用付費的打碼平臺來進行破解。
有關打碼平臺的介紹,可參考打碼平臺是如何高效的破解市面上各家驗證碼平臺的各種形式驗證碼的? 這篇文章
備注
由于該爬蟲足夠簡單,就不再提供示例源碼。有關Python的其他最佳實踐日后會在其他博文中介紹,本篇只是讓各位同學對Python工程化和基本的爬蟲技術有個整體的了解