2020-03-23 13:48:52 更新:
最近看到部分文章閱讀量竟然變高了,有點惶恐,希望自己不會誤人子弟。這篇文章是非常淺顯的 http 協議的一小部分,如果要全面了解 http / 1.1 協議,請移步:RFC 7231
它是http 1.1 標准規范檔案,里面講述十分詳細。(我記得火狐開發者社區也有)
比如 http1.1 Accept-Encoding 字段中描述了如何壓縮 body 進行傳輸,這點很重要,我最初自己寫http客戶端的時候還不知道,於是從服務器獲得的 html 內容都是亂碼,其實這是因為服務端將 body 部分使用 br 、gzip、deflate 等方法進行了編碼。並且對其壓縮算法也有較詳細的描述,簡單來說都是基於哈夫曼樹的改進和優化(詳見 RFC 1951 DEFLATE、RFC 1951 GZIP)
對各種響應狀態碼的解釋、緩存控制、Session等。
最后,大家有沒有真正的意識到一點呢,其實計算機越學越發現,基本上就是在學習前人與計算機定下的規則呀~,無論哪一個方面(硬件、語言、算法、軟件、圖片等),然后終將有一天,我們也開始制定一些規則,讓別人參與進來。所以對各種協議、文檔存儲格式等有一定深入了解是非常重要的一件事。
以下為原文:
先推薦一篇很不錯的文章:https://imququ.com/post/four-ways-to-post-data-in-http.html
說一下,如果是自己編寫底層,那么要注意了,不能只有提交數據的類型,還必須要有數據內容的長度,大體這樣寫即可:
method << "POST / HTTP1/1\r\n"; headers << "Content-Length: 32\r\n"; headers << "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n"; ... body << "test=1&type=json&time=1513242234";
請求頭部分結束的后面就是請求主體(body),發送的body內容是key-value對應的url source,如果帶有中文或其他非英文語種,類似這樣:
type=1&message=%E5%8A%A9%E6%89%8B&plat=1&jsonp=jsonp
需按url編碼格式編碼。注意不用在字符串尾部加"\r\n"。
實際上這種類型的提交參數實現了通過接口對服務器后台的數據庫進行CRUD操作。
我之前僅僅只是會用python的requests庫,沒怎么了解底層,現在自己用C++寫爬蟲才知道,人家已經幫我們做好了這些事,十分便利,同時也看到了她的強大之處。
下面用C++測試B站的add接口(發表評論)為例:
#include <iostream>
#include <sstream>
#include "libhttp.hpp"
using http::Request;
using std::string;
using std::stringstream;
int main()
{
Request r;
string url = "https://api.bilibili.com/x/v2/reply/add";
stringstream headers;
headers << "Contention: keep-alive\r\n";
headers << "Content-Length: 102\r\n";
headers << "Content-Type: application/x-www-form-urlencoded; charset=UTF-8\r\n";
headers << "Cookie: xxx\r\n";
headers << "Host: api.bilibili.com\r\n";
headers << "Referer: https://www.bilibili.com/bangumi/play/ep85276\r\n";
headers << "User-agent: xxx\r\n";
string data = "oid=4492528&type=1&message=%E5%8A%A9%E6%89%8B&plat=1&jsonp=jsonp&csrf=14211ebe19f7a1500e3a4d910c9d4b44";
// decode: "oid=4492528&type=1&message=助手&plat=1&jsonp=jsonp&csrf=14211ebe19f7a1500e3a4d910c9d4b44";
r.post(url, "", headers.str(), data, "");
return 0;
}
運行結果:

到B站上看看:

測試成功,這里測試環境是Linux CentOS7。
如果使用python requests模塊,可以簡單這樣寫:
# coding=utf8
from requests import request
data = {
'oid': '4492528',
'type': '1',
'message': '正片被tx搶走了',
'plat': '1',
'jsonp': 'jsonp',
'csrf': '141500e3a4d910c9d4b44'
}
headers = {
'Cookie': 'xxx'
}
r = request('post', 'https://api.bilibili.com/x/v2/reply/add', data=data, headers=headers, timeout=1)
print r.status_code
print r.text
她的底層會根據你自定義的headers以及data進行解析,然后補充上一些缺少的重要key-value。
順便提下之前在該篇中提到的csrf
1.舊csrf + 舊cookie:
2.新csrf + 舊cookie:

3.舊csrf + 新cookie:

# 2018-12-15 12:02:22 咳咳,今天回頭看了一下這篇文章。。。はつかし。。。emmm,發現自己真是個文盲。。。基本上各大網站的csrf和cookie都是有生命周期的。。。一些網站做得更嚴謹,可能關閉掉網頁就死了,但一般都是通過session來持續一段生命周期,並且csrf是一種名為Cross-site request forgery(跨站請求偽造)的技術,嘛,做過網站應該就明白了。所以,我重新登錄,之前的csrf以及cookie當然會失效。。。
...然后B站的csrf怎么獲得呢?不可能每次都發表評論 + F12看吧,這也太事后諸葛亮了,所以找找還有沒有其他方法,當然我也是無意間找到一個辦法,當視頻播放時,有個接口會每隔幾秒鍾發送一次請求,其中也帶有該視頻下通用的csrf,這個接口名為:heartbeat,如圖:

點一下她,在Headers中From Data就可以看見了。
ps:今天才知道如何查看網站的robots.txt,在瀏覽器地址欄中輸入是格式:http://網站主頁/robots.txt,比如B站的:https://www.bilibili.com/robots.txt,上面寫清楚了網站中哪些是不允許爬取的。
參考:
1.https://www.zhihu.com/question/34980963
2.http://www.robotstxt.org/robotstxt.html
