標籤:AC 發送 網站 encoding org har 粘貼 載入 html_
注意聲明:本篇僅基於興趣以及技術研究而對B站曾經發生過的搶樓事件背後的技術原理進行解析。請不要將其作為私利而對B站以及B站使用者體驗造成影響!謝謝合作!若本文對B站帶來困擾,本人將自行刪除本文。
雖然說是技術研究,但實際上並沒有什麼太深的東西在裡面,你只需要懂一點http協議的請求格式、懂python、會使用python requests package就能完成這個簡單的任務了。
如果你不懂,並想要弄懂這幾點知識,可以試試:
1.http協議的請求格式我推薦看看這兩篇:14002273、78604937。
2.python2/3學習:網上學吧,如果你有其他語言編程基礎,應該很快就能上手使用python,否則,就認真從基礎開始學習python這門語言。
3.python requests package文檔:http://docs.python-requests.org/zh_CN/latest/
最後你肯定得有一個瀏覽器,我推薦Chrome —— 個人喜好。最好還有一個PyCharm或者其他python編輯器,如果你只喜歡用python內建的命令列也行。
然後說一點,現在B站為了防止搶樓,把番劇下所有視頻的評論區都合并了,以前每一個視頻下都會有對應的評論區,現在所有視頻的評論全部在一起的。。。所以就算要搶也只有對新番第一集搶樓可能才有‘意義’了。
工具都準備好了,讓我們開始吧:
這裡隨便選了一部老番《D.C.Ⅱ S.S.》又稱《初音島》作為測試。
來到番劇集頁面,先F12準備監控一會兒發送請求資料的網路狀態:
當我們要進行這個任務的時候,我們必須要Crowdsourced Security Testing道:我們該向什麼地方發送的請求?難道就直接對番劇頁面發送就可以了嗎?如果有做過網站的經驗就會知道,一個網站的前端展示頁面基本上都是通過 js + ajax 等通過背景商務邏輯調用資料庫中的資料載入到對應的jsp檔案中的html標籤中自動產生的。比如評論區,肯定有一個 post 的API介面來接受使用者發送的資料,並將資料存入到資料庫中,然後展示頁面 + js + 資料庫 + 後台商務邏輯等一套服務,最後我們使用者才能在前端中看到豐富的內容,才能看到即時更新的資料,說即時更新不太對,但總之你每次重新整理頁面,網站後台就會做這些事情。
這些有什麼用呢?至少我知道了當我在B站評論區編輯好要發送的訊息並點擊發送評論的時候,肯定是通過一個特地編寫好的介面來post data,而這個post介面的url會在我們點擊按鈕的動作後顯示在瀏覽器的網路監控中,所以,我們要找到這個介面的url就要先發送一個訊息試一試:
最好就是在瀏覽器載入完該頁面的資料後按F12開啟監控台,這樣比較乾淨,點擊發表評論後,很快就可以注意到我們的動作的回饋,點一下看看內容:
顯然,它是通過http post動詞來提交的,從中我找到了這個add介面的url:
到這裡已經可以宣告結束了。注意到Request Headers中就是我們需要自訂添加的要求標頭內容,然後,很關鍵的地方就是發送內容,它一般在Chrome監控台最下面:
oid對應當前視頻的av號,這樣才能確定是對哪一個視頻進行的評論,可以在視頻頁面這裡擷取它:
這裡type的含義不重要,message顯然是我們要發送的東西,注意message如何是中文,那麼將會進行url編碼,可以參考:http://www.w3school.com.cn/tags/html_ref_urlencode.html
後面幾個除csrf外的含義在這裡也並不重要,不知道是不是B站換了(之前的不是這個,還是因為不同番劇下的csrf不一樣,如果是不同番劇下的csrf不一樣的話,這可能說明,對於一部新番,使用這樣的方法我們第一集並不能強到樓。。。不過我也不知道使用同樣的csrf能不能有效,但同一部番劇下的所有視頻應該可以使用同樣的csrf,不過這也只是我的想法,我並沒有驗證過。。。
ps:剛才我對新番Comic Girls用同樣的csrf試了一下,成功了:
B站評論區效果:
但其實我更覺得比較僥倖的是,B站的發送評論並沒有做非常複雜的驗證,假如有一個伺服器端的系統時間的key-value段,那麼我就跪了...我們在python中使用它們:
data = { ‘oid‘: ‘2458871‘, ‘type‘: ‘1‘, ‘message‘: ‘test~‘, ‘plat‘: ‘1‘, ‘jsonp‘: ‘jsonp‘, ‘csrf‘: ‘da9c3263c011ee0969ce383e8d799f05‘}
然後利用瀏覽器中的Request Headers寫好我們的要求標頭:
headers = { ‘Accept‘: ‘application/json, text/javascript, */*; q=0.01‘, ‘Accept-Encoding‘: ‘gzip, deflate, br‘, ‘Accept-Language‘: ‘zh-CN,zh;q=0.9‘, ‘Connection‘: ‘keep-alive‘, ‘Content-Length‘: ‘xxx‘, # 這裡對應的就是我們的data資料的長度 ‘Cookie‘: ‘xxx‘, ‘Host‘: ‘api.bilibili.com‘, ‘Referer‘: ‘https://www.bilibili.com/bangumi/play/ep86125‘, ‘User-Agent‘: ‘xxx‘,}
這裡上面注釋中的長度可以這樣擷取,來到瀏覽器:
點擊藍色地區變為:
當然你可以肉眼一個個字元的數,但我還是選擇複製一下這段字串,然後到粘貼到PyCharm中,選中粘貼上去的那段字串,然後藍色框住的部分就是字串的長度,也就是這裡我們的Content-Length的長度:
但這是測試過後得到了add中的data資訊才能知道長度,如果對一部沒有add資訊的視頻,還是自己將data轉換為瀏覽器中的拼接參數格式的字串,然後用python len算一下字串長度:
content_length = len(str(data).replace(‘: ‘, ‘=‘).replace(‘,‘, ‘&‘).replace(‘\‘‘, ‘‘))
或者直接len(str(data)),因為Content-Length對於填寫並不嚴格,但雖然Content-Length對於填寫並不嚴格,但就算隨便填寫長度也必須要比實際長度大,因為這樣從推斷上請求內容就應該是損壞的。
刪除這段字串,開始使用requests post:
r = request.post(url, data=data, headers=headers, timeout=1, )print(r.status_code)print(r.json()) # r.text
值得注意的一點是,有些介面的請求內容是data參數,data參數僅僅就是轉換為字串,而還有一些介面的請求內容格式是json,這時就只需要將data改為使用json即可,詳細的可以對準post方法 Ctrl + 左鍵 看看源碼:
r = request.post(url, json=json)
到這裡,這樣就完成了發送評論,但我想再提一點:有興趣你可以去掉要求標頭試試效果,然後考慮一下哪些headers中的key-value可以去掉,哪些不能?
------------------- 提出上面思考的主要原因 ---------------------
我試過這樣寫:
from requests import Sessionrequest = Session()url = ‘https://api.bilibili.com/x/v2/reply/add‘data = { ‘oid‘: ‘2458871‘, ‘type‘: ‘1‘, ‘message‘: ‘test~‘, ‘plat‘: ‘1‘, ‘jsonp‘: ‘jsonp‘, ‘csrf‘: ‘da9c3263c011ee0969ce383e8d799f05‘}r = request.post(url, data=data, auth=(‘user‘, ‘pw‘), timeout=1, )print(r.status_code)print(r.json()) # r.text
顯然被殘酷的拒絕了:
這就是沒有做好Cookie的原因了。Cookie中包含了登入使用者的資訊,所以Cookie必不可少,我們光用auth類比登入是行不通的,因為B站還有登入驗證呢...
最後,看似很順利的過程,其中也包含了許多知識點,還有許多細節還得要自己去體會...要學的還有很多~ 我一直想要下載B站的視頻,但一直沒能完成。。。
python學習 —— B站搶樓原理