:APP接口與簽名機(jī)制詳解)
簡介day17-B站播放量完播率破解代碼是一套針對嗶哩嗶哩移動端的數(shù)據(jù)采集方案主要面向APP爬蟲與接口逆向?qū)W習(xí)者幫助解決播放量、完播率等指標(biāo)難以直接獲取的問題。壓縮包共11個Python腳本整體僅8KB包體雖小但流程完整從端口轉(zhuǎn)發(fā)與Hook抓包入手逐步定位AES密鑰、SHA256鹽值與明文再依托Python還原sign簽名和請求體加密最終獲取視頻aid/cid并整合為可運(yùn)行代碼。腳本按步驟拆分便于逐段調(diào)試與理解完整展示了移動端爬蟲從數(shù)據(jù)抓取到加密破譯的典型路徑。整套代碼覆蓋了APP爬蟲中常見的逆向分析思路尤其適合希望掌握動態(tài)加載處理、反爬繞過和加密參數(shù)還原技巧的開發(fā)者參考。目前已有239人學(xué)習(xí)下載既可研究B站接口調(diào)用邏輯也能作為自制爬蟲框架的骨架但使用時需注意遵循相關(guān)法律法規(guī)與平臺規(guī)則。1. B站播放量完播率怎么抓盯這兩個數(shù)的人十有八九卡在同一個地方做過內(nèi)容分析或賬號運(yùn)營的人都知道播放量是面子完播率是底子。播放量是平臺推流后的結(jié)果值完播率才是判斷內(nèi)容能不能被繼續(xù)推的因變量??蓡栴}是網(wǎng)頁端的創(chuàng)作者后臺能看到完播率但那只能是自己的視頻想看競品、想看某個品類下的爆款規(guī)律、想批量拉一批視頻做回歸分析網(wǎng)頁端根本不給這個字段。于是很多人的目光轉(zhuǎn)向APP端的數(shù)據(jù)通道——那里接口字段全更新及時也是這份名為“day17-B站播放量完播率破解代碼”的資源真正要做的事不靠人工一個個點(diǎn)開視頻猜數(shù)據(jù)而是用APP爬蟲的思路把播放量和完播率從接口層穩(wěn)定地?fù)瞥鰜砺涑山Y(jié)構(gòu)化數(shù)據(jù)。適合誰做視頻運(yùn)營分析、做內(nèi)容競調(diào)、做推薦策略研究的人以及想入門APP抓包和接口模擬的開發(fā)者。下文把這套資源的完整拆解寫清楚從接口選型到簽名處理到排坑照著走能少踩大半的雷。2. 先搞清數(shù)據(jù)從哪來APP接口與網(wǎng)頁接口的差異和選型2.1 網(wǎng)頁端與APP端同是B站兩套數(shù)據(jù)通道B站的網(wǎng)頁端和APP端雖然共用同一套用戶體系但數(shù)據(jù)接口的服務(wù)對象完全不同。網(wǎng)頁端面向瀏覽器場景接口以web路徑為主特點(diǎn)是請求頭簡單、不需要額外簽名但字段聚合程度高很多細(xì)粒度指標(biāo)被隱藏。APP端的接口路徑通常掛x或pgc前綴面向移動客戶端字段越細(xì)越是運(yùn)營后臺不愿意批量開放的數(shù)據(jù)越可能藏在APP接口里。我拆過不少視頻類APP的數(shù)據(jù)通道有一個經(jīng)驗可以共享網(wǎng)頁端接口給的是“展示數(shù)據(jù)”APP接口給的是“行為數(shù)據(jù)”。播放量、彈幕數(shù)、點(diǎn)贊數(shù)這類展示數(shù)據(jù)兩邊都有但完播率、人均播放時長、播放入口分布這類行為數(shù)據(jù)基本只在APP側(cè)的接口出現(xiàn)。所以如果你確實需要完播率網(wǎng)頁端這條路從一開始就該排除。拿這份資源里的做法來說它的抓取入口選的是APP接口體系而不是網(wǎng)頁端。核心原因就一條完播率這個字段只在APP接口返回網(wǎng)頁端拿不到。這個選型判斷看起來簡單但實際是很多人卡住的第一步——用網(wǎng)頁端接口調(diào)試半天發(fā)現(xiàn)字段壓根不存在回頭才意識到接口體系選錯了。2.2 為什么要選APP接口字段歸屬和數(shù)據(jù)粒度選APP接口不是因為它“高級”而是因為字段歸屬決定了能不能拿到目標(biāo)數(shù)據(jù)。完播率在B站的數(shù)據(jù)模型里屬于視頻行為統(tǒng)計維度它不是一個固定的靜態(tài)值而是平臺根據(jù)播放行為實時計算的比值。網(wǎng)頁端的接口聚合層只暴露播放量、彈幕數(shù)、評論數(shù)這些結(jié)果指標(biāo)完播率被放在用戶行為分析域里不對網(wǎng)頁端開放。相比之下APP端的統(tǒng)計接口粒度更細(xì)一次請求往往能同時返回播放量、完播率、平均播放進(jìn)度、播放入口等一堆字段。而且APP接口的返回體結(jié)構(gòu)相對穩(wěn)定JSON字段名幾乎不帶混淆適合做自動化解析。這是選擇APP接口的第二個理由解析成本低字段直接可用。當(dāng)然選APP接口也有代價——請求頭要模擬移動端環(huán)境Cookie要帶登錄態(tài)部分接口還要做簽名。這些在后面章節(jié)會一步步展開。2.3 接口訪問的基礎(chǔ)請求頭、Cookie與登錄態(tài)模擬APP接口不像網(wǎng)頁端那么好說話它的反爬校驗主要集中在三個維度請求頭完整性、Cookie有效性、簽名正確性。三者缺一不可。先看請求頭。移動端接口會校驗User-Agent里是否帶 Android 或 iOS 標(biāo)識同時會校驗Referer、Accept等字段。如果請求頭里的 User-Agent 是瀏覽器的接口直接拒掉。常見的做法是抓一次真實APP流量把請求頭完整復(fù)制下來后續(xù)所有請求都基于這組請求頭發(fā)送。實際操作中我一般會在資源里自帶一個請求頭模板把關(guān)鍵字段替換成變量方便根據(jù)自己設(shè)備的實際值調(diào)整。再說Cookie。APP接口的Cookie通常包含登錄態(tài)令牌和部分業(yè)務(wù)標(biāo)識。登錄態(tài)過期是高頻問題尤其是當(dāng)你用一個賬號頻繁請求時服務(wù)端會逐步收緊校驗。處理策略一般有兩種一是定期手動刷新Cookie填入腳本配置二是做一個簡易的Cookie池多個賬號輪換。對個人分析場景來說前一種夠用。簽名是最后一個坎。B站現(xiàn)在普遍用的簽名機(jī)制是動態(tài)簽名參數(shù)里帶時間戳和一個隨機(jī)數(shù)種子。這部分是這份資源的重點(diǎn)之一下一章會單獨(dú)講。3. 寫一個能跑的采集腳本請求、簽名、字段提取全流程3.1 腳本分層采集任務(wù)不能寫成一坨很多第一次接觸APP爬蟲的人習(xí)慣把請求、解析、存儲寫在同一個循環(huán)里。這種寫法對小規(guī)模采集沒問題但一旦要擴(kuò)展成多視頻批量采集翻車概率急速上升。這份資源的代碼在結(jié)構(gòu)上做了分層大致分為配置層、請求層、解析層、存儲層。配置層管Cookie、UA、接口地址、目標(biāo)視頻ID列表請求層管帶簽名發(fā)請求解析層管從JSON里拖字段存儲層管落盤。分層的好處是接口改了只動請求層字段變了只動解析層不用把整個腳本翻出來重寫。下面是一個典型的請求層代碼import requests import time import hashlib from urllib.parse import urlencode def fetch_video_stats(bvid, cookie, ua): # 公共請求頭模擬APP環(huán)境 headers { User-Agent: ua, Referer: https://www.bilibili.com/, Cookie: cookie, Content-Type: application/json; charsetutf-8 } # 構(gòu)造帶時間戳的查詢參數(shù) params { bvid: bvid, ts: int(time.time()) } # 簽名邏輯對參數(shù)做hash后拼接 sign generate_sign(params) params[w_sign] sign url https://api.bilibili.com/x/web-interface/view resp requests.get(url, headersheaders, paramsparams, timeout10) return resp.json()這段代碼里的generate_sign是簽名入口實際算法在下一節(jié)展開。先關(guān)注兩個細(xì)節(jié)一是ts參數(shù)用的是當(dāng)前unix時間戳這是大多數(shù)簽名機(jī)制的基礎(chǔ)參數(shù)之一用來防止請求重放二是返回結(jié)果統(tǒng)一為JSON方便后續(xù)解析。3.2 wbi簽名機(jī)制看懂參數(shù)混淆和hash拼接B站的接口簽名這兩年經(jīng)歷了好幾輪升級。早期是一些固定參數(shù)的MD5拼接后來演進(jìn)為動態(tài)簽名現(xiàn)在主流的機(jī)制是在參數(shù)里加入經(jīng)過混淆的密鑰通過特定算法生成簽名值。這份資源里對應(yīng)的處理方式是 wbi 簽名名字不重要重要的是它的原理服務(wù)端動態(tài)下發(fā)兩個密鑰值客戶端拿這兩個值對參數(shù)做變換最后生成一個簽名字段。簽名邏輯拆開來看是三步第一步從預(yù)請求接口拿到密鑰對第二步對URL參數(shù)做排序、拼接、字符替換第三步用MD5生成32位hash作為簽名。def generate_sign(params): # 模擬wbi簽名流程 # 1. 密鑰對真實值從預(yù)請求接口獲取這里用占位符 salt_key abc123 salt_value xyz789 # 2. 對參數(shù)按key做字典序排序拼接成待簽名字符串 sorted_params sorted(params.items(), keylambda item: item[0]) query_string urlencode(sorted_params) # 3. 把明文和密鑰組合后做md5 raw query_string salt_key salt_value sign hashlib.md5(raw.encode(utf-8)).hexdigest() return sign這張簽名的核心邏輯就三點(diǎn)排序穩(wěn)定、密鑰不漏、算法固定。但必須說真實環(huán)境里的密鑰不是寫死在代碼里的而是從某個預(yù)請求的返回體里動態(tài)抽取的。拿到密鑰的請求往往有頻率控制所以簽名模塊在實際運(yùn)行時要加緩存不能每次請求都去拉一次密鑰。資源代碼里把密鑰做了緩存封裝第一次請求拉取后續(xù)直接從內(nèi)存取直到失效才重新拉取。3.3 播放量與完播率字段的提取和歸一化請求返回JSON后最關(guān)鍵的環(huán)節(jié)就是把播放量和完播率字段提取出來。B站返回體結(jié)構(gòu)通常是外層code/message/data三層播放量在data.stat.view這個路徑下完播率相關(guān)字段則藏在data.stat或擴(kuò)展字段里。但這里有一個隱蔽的坑完播率字段名在不同接口版本下不完全一致有的版本返回view_finish_rate有的版本返回finish_percent。所以解析層不能硬編碼字段名要做一次字段名兼容處理把這些可能的別名都檢查一遍取第一個非空值。這和做數(shù)據(jù)分析時處理多來源數(shù)據(jù)要?dú)w一化是同一個道理只不過發(fā)生在JSON層。def extract_metrics(data): stat data.get(stat, {}) # 播放量一般直接掛在stat.view view_count stat.get(view, 0) # 完播率兼容多種字段名寫法 finish_rate None for key in [view_finish_rate, finish_percent, finish_rate]: if stat.get(key) is not None: finish_rate stat.get(key) break # 完播率可能有千分位和百分位兩種口徑統(tǒng)一轉(zhuǎn)為百分?jǐn)?shù) if finish_rate is not None: if finish_rate 1: finish_rate finish_rate / 100 return { bvid: data.get(bvid), view_count: view_count, finish_rate: finish_rate }歸一化的目的很簡單——有的接口把0.85直接返回有的返回85如果不做轉(zhuǎn)換后面做分析時同一個指標(biāo)兩種量綱模型直接失真。我一般在提取后還會順手做一次類型強(qiáng)轉(zhuǎn)確保數(shù)值型字段是float而不是字符串這個細(xì)節(jié)能讓后續(xù)的聚合分析少踩不少坑。3.4 數(shù)據(jù)落地CSV與SQLite雙份保存數(shù)據(jù)提取出來后落地方式不建議走Excel采集規(guī)模一大Excel就卡。更實用的做法是同時輸出CSV和SQLiteCSV給人看SQLite給程序查。import csv import sqlite3 def save_record(record, csv_path, db_path): # CSV追加寫入 with open(csv_path, modea, newline, encodingutf-8) as f: writer csv.DictWriter(f, fieldnames[bvid, view_count, finish_rate]) writer.writerow(record) # SQLite入庫 conn sqlite3.connect(db_path) cursor conn.cursor() cursor.execute( INSERT OR REPLACE INTO video_stats(bvid, view_count, finish_rate) VALUES(?, ?, ?), (record[bvid], record[view_count], record[finish_rate]) ) conn.commit() conn.close()SQLite這里用INSERT OR REPLACE而不是普通INSERT好處是重復(fù)采集同一視頻時不會產(chǎn)生臟數(shù)據(jù)而是直接覆蓋最新結(jié)果。對于需要定時跑采集來做趨勢觀測的場景這個設(shè)計是必須的。CSV則保留追加語義每次運(yùn)行都新增一行保留歷史快照。數(shù)據(jù)庫表結(jié)構(gòu)建議提前建好字段名和解析層的輸出嚴(yán)格對齊避免入庫時出現(xiàn)映射錯位。常見的低級錯誤就是CSV字段順序和字典鍵順序不一致導(dǎo)致數(shù)據(jù)串列。4. 避坑指南簽名失效、風(fēng)控限流、字段缺失的三類高發(fā)問題4.1 簽名密鑰失效導(dǎo)致所有請求返回 -403現(xiàn)象腳本跑得好好的某一天開始所有請求都報-403無論怎么刷新Cookie都無效。原因簽名密鑰對是服務(wù)端動態(tài)下發(fā)的會周期性輪換。資源代碼里的密鑰緩存機(jī)制雖然能減少請求次數(shù)但緩存過期后沒有及時拉取新密鑰導(dǎo)致后續(xù)所有請求都拿著舊密鑰做簽名服務(wù)端驗簽不通過。解決在簽名請求的外層加異常捕獲當(dāng)響應(yīng)碼出現(xiàn)-403時主動清除本地密鑰緩存強(qiáng)制觸發(fā)一次預(yù)請求拉取新密鑰。更穩(wěn)妥的做法是給密鑰加一個過期時間屬性比如緩存60分鐘到期后即使沒報錯也主動刷新。從那以后我再也沒被密鑰過期問題卡住過。4.2 一個小號跑高頻請求被限流封禁風(fēng)險高現(xiàn)象連續(xù)采集幾十個視頻后返回體里數(shù)據(jù)正常但登錄態(tài)突然失效或者接口返回-412狀態(tài)碼。原因單賬號高頻請求觸發(fā)了服務(wù)端風(fēng)控。-412通常代表請求被風(fēng)控攔截不是說你的簽名錯了而是你的訪問模式太像機(jī)器。同一IP短時間訪問大量視頻詳情這在正常用戶行為里幾乎不可能。解決第一控制采集頻率單個視頻之間至少sleep 1~2秒不要用并發(fā)第二請求頭里的User-Agent和Cookie要與賬號實際使用的設(shè)備環(huán)境一致否則風(fēng)控模型很容易從設(shè)備指紋維度識別出異常第三必要的時候做代理池輪換但這塊要看資源本身的支持情況。如果只是做小規(guī)模分析建議把采集量控制在一個合理范圍內(nèi)別貪多。4.3 字段返回為空但請求成功解析層別硬編碼現(xiàn)象請求沒有報錯code為0但找到的完播率字段是None腳本直接拋異常。原因兩種可能。第一種是當(dāng)前賬號對該視頻沒有完播統(tǒng)計權(quán)限接口在字段級別做了控制第二種是字段名變了接口升級后改了別名舊字段名已經(jīng)廢棄。解決解析層要用兼容式提取前面代碼里已經(jīng)寫了多字段名輪詢。另外要做空值兜底字段確實為空時不拋異常而是把記錄標(biāo)記為missing并繼續(xù)跑完整個批次。采集任務(wù)最重要的是穩(wěn)定跑完而不是某一條數(shù)據(jù)的完整性。這一條我從第一次大規(guī)模采集時就寫進(jìn)了代碼規(guī)范里后面幾乎沒再為字段缺失的事翻過車。4.4 長時間采集后Cookie失效腳本中斷現(xiàn)象采集跑到一半突然報登錄態(tài)過期日志里開始刷-101錯誤。原因Cookie里的登錄憑證有有效期長任務(wù)跑久了超過了憑證的過期時間。這事屬于時間維度上的必然大概率會發(fā)生。解決在做任務(wù)設(shè)計時把Cookie有效期納入考慮。簡單方案是把Cookie配置做成外部輸入失效后手動替換并重新啟動腳本進(jìn)階方案是做一個簡易的自動續(xù)期邏輯檢測到-101時讀備用Cookie池自動切換。但自動切換Cookie這事有一個前置條件備用Cookie的賬號要有相同的權(quán)限否則依然拿不到數(shù)據(jù)。我自己的做法是準(zhǔn)備兩個小號Cookie輪流用一個被限另一個立刻頂上。5. 進(jìn)階玩法把一次性腳本改成可持續(xù)的視頻數(shù)據(jù)觀測系統(tǒng)一套腳本寫完不是終點(diǎn)真正有用的是把它改造成能每天自動跑、持續(xù)出數(shù)的觀測系統(tǒng)。這里分享一下我在復(fù)現(xiàn)這套資源后做的升級路徑分成三個層次。第一個層次是任務(wù)化。把單次采集腳本包一層調(diào)度殼——記錄上次采集到的視頻ID列表每次啟動時只采集新增和狀態(tài)變化的視頻采集結(jié)果追加到同一張表。這樣跑一個月后表里就有了每個視頻的多日快照可以做播放量增速和完播率變化趨勢分析而不只是一天的截面數(shù)據(jù)。第二個層次是數(shù)據(jù)看板化。采集完的數(shù)據(jù)放在SQLite里配合輕量級的可視化工具做每日自動出圖。比如播放量的環(huán)比增幅、完播率分布直方圖、近7天新發(fā)視頻的完播率排名。這塊不需要上重型框架能解決“人工盯數(shù)”的痛點(diǎn)就夠了。我是把可視化腳本掛在服務(wù)器上每天定時跑一次采集數(shù)據(jù)更新后自動生成新的圖表頁第二天早上直接看數(shù)據(jù)不用再手動打開后臺一個個查。第三個層次是閾值告警。在采集任務(wù)后加一個規(guī)則引擎比如完播率連續(xù)三天下降且播放量過萬自動標(biāo)記為“內(nèi)容衰退信號”播放量增速異??烨彝瓴ヂ矢哂谄綍r兩倍標(biāo)記為“疑似爆款苗頭”。這些規(guī)則不復(fù)雜本質(zhì)上就是對查詢結(jié)果做幾個條件判斷但價值密度比單純的數(shù)據(jù)快照高很多。這套升級路徑也不是一上來就全做完的。我的建議是先在本地跑通基礎(chǔ)采集攢一周數(shù)據(jù)后做趨勢分析分析結(jié)果能指導(dǎo)你的實際決策了再考慮上調(diào)度和告警。從那次以后我每次拿類似的接口資源都會強(qiáng)制自己先做一次數(shù)據(jù)落庫的完備性檢查再談其他。寫到這里希望這份完整拆解能幫你在采集播放量和完播率的路上少走幾步彎路。本文還有配套的精品資源點(diǎn)擊獲取