之前我寫過很多篇的Power Query的網絡抓取的教程,如果按照網絡數據傳遞的方式來說,那些文章中的抓取方式都是GET方法的抓取,那么POST與GET方法有什么區別呢?
這是W3C官網上的內容,大家參閱:
GET、POST兩種方法都可以用來向服務器發起請求,至于服務器支持哪種方法,我們要通過谷歌瀏覽器來查看一下:
像這樣在瀏覽器地址中提交查詢條件的就是GET方法:
GET方法下面通常是查詢的條件字符串Query String Parameters:
這樣的URL很標準,沒有各種問號等號連字符的,通常就是POST方法:
POST方法下面是表單Form Data:
很多時候網站是同時支持兩種查詢方法:
上面這個網站就是兩種方法都支持的網站。
最后GET、POST兩種方法在傳遞數據時最大的區別就是編碼方式不同:
- GET:使用TextEncoding.Ascii編碼傳遞查詢條件
- POST:使用BinaryEncoding.base64編碼傳遞表單內容
我們回到Power Query抓取上來,我之前也是不明白要怎么才能實現POST提交數據抓取,看了幾篇文章,基本上都是用一個函數實現GET、POST兩種抓取的,唯一的區別在于HEADER這個頭文件的書寫,以及查詢條件的編碼方式。
GET方法查詢:
let
url="", //
headers=[cookie=""], //頭文件
query=[], //Query String Parameters
web=Text.FromBinary(Web.Contents(url,[Headers=headers,Query=query]))
in
web
let
url="", //
headers=[cookie=""], //頭文件
query=[], //Query String Parameters
web=Text.FromBinary(Web.Contents(url,[Headers=headers,Query=query]))
in
web
上面這段代碼可以適用在大部分的GET方法的站點,其中:
- url:站點,不包括?號后面的部分
- headers:頭文件,如果不是登錄密碼的方式查詢,可以省略
- query:Query String Parameters這個上面截圖是View Parsed格式碼,下面的截圖是View Source源碼,網絡抓取的時候用的是這個源碼:
- web:在執行抓取時是使用的參數抓取,注意下面這句中沒有做任何的編碼,其實就是使用的普通文本發送的查詢請求:
Web.Contents(url,[Headers=headers,Query=query])
Web.Contents(url,[Headers=headers,Query=query])
當然了,我們很多時候,匿名抓取數據時,就不用登錄不用cookie,也就不用寫頭部文件,直接在一長串的url中添加頁碼等查詢條件,就省略掉了Web.Contents函數的第二參數,這就是我為什么說之前的那些一句話查詢都是GET查詢。
POST查詢:
我們主要講的就是這個POST方法的抓取,與GET方法最大的不同,就是不能省略頭文件Headers,因此也就必須使用Web.Contents函數的第二參數,我們就通過一個示例來說明怎么來寫代碼執行POST請求:
第一步:還是要做網站分析
首先我們確定這個網站是可以用POST方法抓取數據的:
然后要看頭文件中的關鍵字:
其實我沒有用用戶名密碼登錄網站,所以這個cookie是可以不用的,但是這個:
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
是POST方法中必須要使用的,劃上重點符號。
再然后我們要看表單的內容:
我們需要向服務器提交上面幾個查詢條件,然后才能返回查詢結果,上面的格式是為了方便查看,大家可以理解成是一個記錄Record格式的樣子,你按下View Source就會顯示出源碼:
我畫線的部分就是“離婚登記數”,正常的URI編碼形式好多百分號的漢字。
通過這些分析我們可以開始下一步了。
第二步:試抓
先把代碼丟上來,然后再一行一行的來說:
let
url="dataki/KeyIndicator/GetPageData",
headers=[#"Content-Type"="application/x-www-form-urlencoded; charset=UTF-8"],
content=Text.ToBinary("XjCode=xj35&Type=level&FullIndicate=%E7%A6%BB%E5%A9%9A%E7%99%BB%E8%AE%B0%E6%95%B0&FullIndicateCode=YI0116&Time=2016&pagesize=500"),
web=Web.Contents(url,[Headers=headers,Content=content])
in
web
let
url="dataki/KeyIndicator/GetPageData",
headers=[#"Content-Type"="application/x-www-form-urlencoded; charset=UTF-8"],
content=Text.ToBinary("XjCode=xj35&Type=level&FullIndicate=%E7%A6%BB%E5%A9%9A%E7%99%BB%E8%AE%B0%E6%95%B0&FullIndicateCode=YI0116&Time=2016&pagesize=500"),
web=Web.Contents(url,[Headers=headers,Content=content])
in
web
- url:,就是網站分析中的第一行那個;
- headers:注意內容就是我們之前復制的那段內容,注意書寫格式,#號以及引號的位置;
- content:就是Form Data表單內同的源碼,要注意的是使用了Text.ToBinary函數轉換成了BinaryEncoding.base64編碼;
- web:使用了第二參數,正常的書寫就行了
與GET方法相比,主要的區別在于,headers、content的寫法,
- GET:header可有可無,POST:必須有headers;
- GET:query無需編碼,POST:content必須用Text.ToBinary轉編碼格式;
我們看查詢的結果:
是個二進制文件,我們用JSON格式分析:
這樣就證明了我們的POST方法成功了,當然我們肯定不滿足,只抓取一年的數據,一個離婚登記數據,接下來就是要定義函數了,其實單個抓取成功,并展開表格到這一步就可以了,不要做過多的整理,避免抓取不同類別數據時出現沖突,導致函數的通用性降低。
第三步:自定義函數
我們需要通過三個參數來實現更多數據抓?。?/p>
- y:年,對應content中的time=2016,我們用y代替2016,當然最好是直接用數值2016,那么就要寫成Text.From(y),這樣即使是數值也不會出錯。
- f:關鍵字,對應著content中的FullIndicate=%E7%A6%BB%E5%A9%9A%E7%99%BB%E8%AE%B0%E6%95%B0,我們用Uri.BuildQueryString([FullIndicate=f])來生成這樣一段內容
- c:代碼,對應著content中的FullIndicateCode=YI0116,這里面的YI0116是隨著關鍵字變化的,如果是”結婚登記數“,對應的是FullIndicateCode=YI0114。
我們把三個參數帶到自定義函數中:
接下來我們要來做個多年的結婚登記數抓取測試。
第四步:數據抓取
展開表格就可得到2000-2017年的結婚登記數據了。
當然今天這個抓取是一行字符串式的數據傳遞相對容易一些,有的用記錄方式傳遞,有的用JSON格式,無非就是幾種格式之間的轉換。只要了解了基本的POST操作的過程,那么用戶名密碼登錄抓取也不是什么難辦到的事情了。


