者:零一、檸檬加冰加水
一、簡介:
我是一枚小白,師從零一老師,用師傅的一句話與大家共勉:"時間是不會辜負你的"
二、說明:
1.Excel Power Query爬取網頁數據的強大之處不僅操作簡單,而且后期只需要一鍵刷新即可實時更新
2.中間獲取數據的時候,我是根據自己的需求來勾選數據
3.由于貝貝網的特賣寶貝每天都更新,所以會造成您做案例的時候和我做案例中的寶貝會不一樣,忽略就好,只要過程沒錯,案例中的寶貝不影響最終的數據效果
4.按照此方法,可以獲取貝貝網其他類目的銷售情況,以及其他同類網站的數據
三、具體步驟如下:
(友情提示:操作步驟很詳細,看起來有點繁瑣,但只要你動手做,其實很簡單)
1.打開貝貝官網:http://www.beibei.com/
2.對著網頁鼠標右鍵,點擊"檢查"
3.點擊"手機標志"
4.按F5刷新網頁,右邊列表區點擊"Network",再點擊"clear"
5.左邊網頁點擊女裝,左邊網頁往下拉直到底部,右邊列表區點擊 "JS" 出現數據
6.點擊"1-15","preview","martshows"
7.右邊列表區中的"1-15"、"2-15"..."17-15"中的數據均為左邊網頁女裝類目中的內容
8.點擊"Headers",復制"1-15"、"2-15"..."17-15"所有的Request URL發現,鏈接中只有頁碼在變化,其他都不變,所以頁碼就可以作為一個變量
9.桌面新建Excel,"數據","從表格",勾選"表包含標題"
10.頁碼列的格式換成文本
11.添加列,添加自定義列,復制"1-15"Request URL,點擊確定
Json.Document(Web.Contents("http://sapi.beibei.com/martshow/search/"&[頁碼]&"-15-woman_dress--.html"))
12.點擊"繼續","公共"
13.展開自定義列,勾"martshows",點擊確定 (取消勾選使用原始列名作為前綴)
14.接著展在"martshows",勾選"brand(品牌)","buying_info(正在購買人數)","mid(店鋪id)",點擊確定
15.把"mid(店鋪id)"的格式改成文本
16.切換到網頁,點擊第一個寶貝
17.左邊網頁往下拉直到底部,右邊列表區點擊 "JS" 出現數據
18.點擊"263710-1-20...","preview","martshow_items"
19.列表區中"1-20"..."17-20"的數據均為左邊網頁中的內容
(左邊網頁里的內容,能在右邊列表區中找到,此URL就是我們要找的URL)
20.點擊Header,復制Request URL
21.切換到excel,把復制好的URL復制到"自定義添加列"中去
Json.Document(Web.Contents("http://sapi.beibei.com/martshow/item/v3/"&[mid]&"-1-20-hot-0--0-0--0.html"))
用"mid(店鋪id)"替換"263710"
22.展開"自定義列",勾選"martshow_items",點擊確定
(取消勾選使用原始列名作為前綴)
23.接著展開"martshow_items",勾選"iid(商品id)","price(折扣價)","price_ori(原價)","sale_tip(折扣)",點擊確定
24.把"iid(商品id)"的格式改成文本
25.切換到網頁,點擊第一個寶貝
26.左邊網頁往下拉直到底部,右邊列表區"JS"出現數據
27.點擊"183682...","preview",列表區的數據均為左邊網頁中的內容
(左邊網頁里的內容,能在右邊列表區中找到,此URL就是我們要找的URL)
28.點擊Header,復制Request URL
29.切換到excel,把復制好的URL復制到"自定義添加列"中
Json.Document(Web.Contents("http://sapi.beibei.com/item/detail/new/"&[iid]&".html"))
用"iid(商品id)"替換"18368838"
30.展開"自定義列",勾選"sold_num(銷量)",點擊確定
(取消勾選使用原始列名作為前綴)
31.點擊"開始","關閉并上載至..."
32.加載到"表",點擊確定
33.至此,貝貝網女裝銷售數據獲取成功!
、頁中加JS代碼:
<script type="text/javascript">
function page()
{
var page1=document.getElementById("page1").value;
var href=location.href;
if (/page=\d+/.test(href)) {
href=href.replace(/page=\d+/, "page=" + page1);
} else if (href.indexOf('?')==-1) {
href=href + "?page=" + page1
} else {
href=href + "&page=" + page1
}
location.href=href;
}
</script>
二、加上輸入頁碼的文本框和跳轉按鈕
<input type="text" name="page1" id="page1">
<a href="javascript:void(0)" onclick="page()">跳轉</a>
ower Query并不是一個專門的網抓或者爬蟲工具,沒有編程語言那么專業,實現的功能也比較有限,但其優勢就是簡單易學,且無縫對接excel,所見即所得。
本文將以純新手的角度,介紹一些基礎的網抓知識,盡可能讓每個人都能學會。
網抓主要分為三個步驟:
1、抓包并分析請求
2、構建并發送請求
3、對返回的數據清洗。
靜態網頁
首先來看一個最簡單的案例:
http://quote.stockstar.com/stock/ranklist_a_3_1_1.html
這是一個靜態頁面,以html結尾,當你點擊不同欄目不同頁碼的時候,URL也在相應的發生變化。
對服務器來說,靜態頁面是實實在在保存在服務器上的文件,每個網頁都是一個獨立的文件。所以比如我們要抓某一頁的數據,只需要點擊新建查詢-從其他源-自網站,把對應的URL輸入進去就可以了,這和我們導入本地xlsx文件的原理是一樣的,至于如何批量抓取多頁我們下面再講。
但是靜態頁面缺點很明顯:沒有數據庫支持,交互性差,非常不便于維護,所以大多數網站會采用動態頁面設計,這就導致了我們想要抓取數據變得沒那么簡單。
動態網頁
什么是動態頁面?打個比方,打開百度首頁,搜索關鍵詞powerquery。
URL去掉無關參數精簡后為:
https://www.baidu.com/s?wd=powerquery
搜索不同關鍵詞只是wd=后面的部分在變化。
試想網民搜索關鍵詞的可能性有無窮多,百度會為每一種可能做一個文件放在服務器里么?顯然不會。
我們之所以能夠看到不同的結果是因為當我們搜索關鍵詞時,會使用GET方式向服務器提交帶參數的請求,比如上面?后面的就是參數,wd是字段名,powerquery是值。當服務器接收到參數后,計算并返回給我們相應的結果。
那GET又是什么?這就要從HTTP協議開始說起了。
我們之所以能夠使用瀏覽器訪問網頁,實際上就是瀏覽器向服務器提交請求(request),服務器收到請求后返回了響應(response)并把HTML代碼發送給瀏覽器,瀏覽器解析并顯示出來。
如上所說,對于動態頁面我們請求的訪問地址始終是同一個https://www.baidu.com/s,如果要讓服務器知道我們想看什么,就需要一種東西來在客戶端與服務器端之間進行消息傳遞,這就是HTTP協議請求。
HTTP協議請求主要有GET,POST,PUT,DELETE等等,分別對應查、改、增、刪四個操作,其中最常見的就是GET和POST。GET僅請求資源,POST會附帶一個Body包含用戶數據。
GET請求的數據會附在URL之后,以?分割URL和傳輸數據,參數之間以&相連。上面百度的案例就是GET,再長點的話比如這樣:
https://www.baidu.com/s?wd=power%20query%E7%BD%91%E6%8A%93&pn=10
這里有wd和pn兩個參數,以&相連,中間的亂碼是中文經過URL encoded轉碼后的結果,如果再有其他參數則繼續在后面追加&即可。
POST對應改,請求提交的數據放置在HTTP包的Body中。比如你在下方發表評論,你并不能通過URL看出來你究竟向我提交了什么,而當你評論完,頁面上出現了你的評論,我的網站已經被你修改了,這就是在提交POST請求。當然POST也可以用來查詢數據,是一種更加安全的傳遞方式。
看到這里,我們已經知道了客戶端和服務器端是如何進行信息傳輸的:客戶端提交request,服務器返回response。注意我們這里說的是客戶端,瀏覽器算一種,當然還有其他很多種,比如我們今天要講的Power Query。
那么網抓的原理其實就是,找出我們所需要抓的數據,分析是瀏覽器向服務器提交了什么請求哪些參數,然后用Power Query構建同樣的請求發送給服務器,服務器便會把對應的數據在Power Query中返回給我們。
了解了這個,下面我們就開始進行第一步:抓包。
網抓的關鍵在于抓包,一旦抓到包后面都好做。而在抓包的時候,一定要靈活變通,比如在抓一些電商網站數據時,PC端往往比較難抓,所以可以考慮PC轉移動,去抓移動端網站,在Chrome中的F12窗口左上角可以一鍵切換PC和移動模式。再比如抓QQ好友列表,你直接抓軟件是抓不到的,那你就思考除了在軟件還可能在哪里有接口?比如QQ空間里@好友的時候,給好友充值QB的時候,都可以輕松獲取到好友列表。一旦想通這點,你會發現網抓原來這么簡單。抓包可以用瀏覽器自帶的調試工具,按F12即可調出,比較輕量,但缺點是不能支持一些復雜的抓包,但話說回來Power Query本身就不太支持復雜的網抓,所以基本也夠用了。
建議使用Chrome瀏覽器,下面的案例全部是基于Chrome,所以不要問我其他瀏覽器怎么找。
以
http://221.215.38.136/grcx/kscx/list.action?kscxVo.jsp=ylypmlcx.jsp
為例,我們點擊下方無論選多少頁發現URL都不會變化,那么如何獲取到比如第5頁的數據呢?按下F12調出調試工具。如果不起作用的話就右擊選擇檢查-然后點擊Network。
先簡單介紹一下控制臺。最上面圈出來的那一排,最常見的Elements顯示網頁的結構,Network顯示瀏覽器和服務器的通信。
我們選擇Network后應該是空的,因為才剛剛呼出控制臺監控通信,而之前的行為是沒有監控的,下方文字提示"通信記錄已激活,請執行request或按F5"。
下面要做的就是讓瀏覽器發送請求,比如你要抓第5頁你就點擊第5頁或者下一頁,你要抓其他欄目你就點擊對應的欄目,總之目的就是產生查詢數據的行為,讓瀏覽器監測到。
如果你的點擊讓頁面產生了變化,那么就會出現一條條記錄,每條記錄都是一次請求,瀏覽器會對這些請求按照類型進行分類,如圖中框出來的部分所示,我們要做的就是在產生的這么多條請求中找出是哪條請求讓服務器返回了我們所要的數據。
其中All是所有類型,數據不可能在CSS,Font之類的里面,按照我的經驗可能性DOC>XHR>JS>其他,當記錄過多產生干擾時可以點擊左上角圈出來的Clear清空重新查找。
下面請動動你們的小手和我一起做如下幾個操作:1、打開上面的URL,2、按下F12調出Network窗口,3、拉到頁面最下方點擊第5頁。
按照剛才說的優先級依次找一下:Doc有一條,XHR空的,JS空的,如圖所示。
在Headers標簽下分為了幾個模塊:
General中的Requset URL表示請求的訪問地址,Request Method表示請求所使用的方法為GET,所以我們可以通過這里判斷是GET還是POST。
Response Headers是服務器返回的響應頭,因為我們目前主要是想構建請求所以這部分內容不重要。
Request Headers是請求頭,其中Cookie經常會用到,Referer有可能會用到,User-Agent是讓服務器識別客戶的設備信息的參數,在其他語言的復雜網抓下有可能用到,但在PQ中基本用不到,這個后面再說。順便說一下,當你訪問一個網站的時候,你電腦什么系統、用的什么瀏覽器、顯示器多大、分辨率多少、瀏覽軌跡這些信息已經全部暴露給對方了,想想是不是感覺很恐怖。
Query String Parameters是查詢參數,因為是GET方法,所以這些參數已經全部在最上面的Requset URL的?后面了。
這里所看到的信息是已經經過解析的,在每個模塊的右邊還有一兩個小按鈕,view source表示查詢源代碼,view URL encoded表示查詢轉碼后的。
講了這么多,那么我們如何確定目前所看到的這條記錄是不是就是能夠返回我們想要數據的請求呢?
首先我們回想下我們是如何找到這條記錄的,是因為點擊了第5頁。
所以我們大致能夠推斷出應該是提交了一個字段名和page相關且值=5的參數,而看一下最下方的Query String Parameters或者最上方的Requset URL,其中有個page_ylypmlcxQuery=5,假如我們再點擊第6頁第7頁,這個參數也會對應的變成6,7,所以基本能夠確定就是它了。
又因為是GET,參數已經全部在URL里了,所以我們可以直接把Requset URL復制粘貼到瀏覽器看一下。
我們在瀏覽器地址欄里把5改成6,7,8,看到數據也會跟著發生變化。這里除了5還有另一個參數,也不知道是什么玩意,有強迫癥的可以去掉試試,變成
http://221.215.38.136/grcx/pages/kscx/ylypmlcx.jsp?page_ylypmlcxQuery=5
發現對結果沒影響,那么要抓第5頁的數據我們只需要把這個地址復制到PQ中就可以了。
繼續下一個案例:
http://www.drugfuture.com/cndrug/national.aspx?ApprovalDateStart=2016-01-01&ApprovalDateEnd=2016-12-31
同樣要求抓第5頁。
F12,點擊第5頁,在Doc里發現一條記錄如下圖:
我們發現Request Method是POST,并且在URL中已經找不到控制頁數的參數了,因為POST提交的參數在Body中。
在Request Headers中比之前多了一個Content-Type,這個參數用來描述Body的內容類型,所以一般POST類型都需要加上這個參數,否則服務器無法識別提交的Body內容。注意response里也有個Content-Type,不要填錯了。
在最下方相比GET多了一個Form Data的模塊,其中包含__EVENTTARGET,__EVENTARGUMENT,__VIEWSTATE,__VIEWSTATEGENERATOR四個參數,這里就是我們上面一直所說的POST提交的Body內容。我們很容易發現__EVENTARGUMENT的值為Page就是控制頁數的,繼續點擊第6頁發現參數變成了Page,也就驗證了我們的猜想。
所以同上一個案例相比,我們只需要把GET換成POST,提交上面的4個參數,再加一個Content-Type表明類型,即可抓到指定的數據。
以上介紹了使用瀏覽器自帶調試工具分別實現GET和POST抓包的方法,但是畢竟案例比較簡單,基本上都只有一條記錄我們一下子就能找到。但是如果出現的記錄很多,我們可以使用Fiddler來快速定位。
Fiddler是一款系統代理服務器軟件,下載地址請自行百度。原本是客戶端發送request,服務器返回response,而有了代理之后就相當于在客戶端和服務器之間夾了個小三,客戶端發送request給Fiddler,Fiddler再轉發給服務器,反之亦如此。由于所有的網絡數據都會經過Fiddler,自然Fiddler能夠截獲這些數據,實現網絡數據的抓包。
安裝好Fiddler后首先進行初始設置。
Rules,勾選"Remove all Encodings","Hide Image Requests","Hide CONNECTs",然后Tools-Options-HTTPS全部勾上。
還以上面POST那個案例為例,在瀏覽器中點擊第5頁,在Fiddler中按ctrl+F調出查找窗口,然后在想要抓取的頁面中隨便找一個比較有特征的數據,比如第5頁中有一個產品名稱叫做"維血康顆粒"。又因為我們要找的是服務器返回的response中的數據,所以我們可以選擇Responses Only以縮小查找范圍。
這樣我們需要的數據所在的請求就會被高亮標記出來。Fiddler界面有很多標簽,我們選擇"Inspectors",此時界面分為三部分,左邊為session框,右上是request框,右下是response框。
所以在Fiddler中我們要做的就是,ctrl+F查找,然后查看response框確認數據是否在范圍內,然后在request框里找出請求參數。
request框和response框都提供了多種視圖供我們查看,最好是都選擇Raw,也就是原始數據。這里只是舉了個例子,在實際應用中我們搜索的時候最好搜英文或數字,而不要搜中文,因為可能因為轉碼的問題導致搜不到。
剛才說到所有的網絡數據都會經過Fiddler,所以使用Fiddler不僅可以抓到瀏覽器中的數據,甚至可以抓到一些應用軟件中的數據,比如說抓QQ群成員。
打開QQ群資料-成員,剛打開的時候會看到短暫的"正在加載數據,請稍候"的提示。當加載完成后,Fiddler中多了幾條記錄,嘗試搜索"施陽",高亮定位到其中的一條記錄,查看response發現,沒錯這正是我們要的數據。
一般點擊出現"正在加載"的數據多是如此,大家都可以動手找一下QQ群文件。
不知不覺已經5000字下去了,但到現在似乎都和Power Query沒多大關系。
抓包是網抓的第一步也是最重要的一步,這步沒做好或者找錯了那么后面都是徒勞。不管你是使用PQ,還是VBA,還是python,到這一步的方法都是一樣的。
至此我們已經知道了瀏覽器之所以能夠獲取到數據是因為它到底做了什么,那么下面就開始進入下一步,把剛才瀏覽器做的事交給Power Query去做。
在M語言中,實現網抓的核心函數是Web.Contents,它能夠對指定的URL向服務器發出request并接受返回的response,先看下函數介紹。
Web.Contents(url as text, optional options as nullable record) as binary
第一參數就是文本形式的URL,第二參數是可省略的record,包含上圖中那一大坨參數,其中有3個比較常見,其他比較少見就不講了。
Query:也就是F12中的Query String Parameters,之前也講過這部分的參數也可以加在URL的?后面,以&相連,比如
=Web.Contents("http://www.baidu.com/s?wd=powerquery")
和
=Web.Contents("http://www.baidu.com/s", [Query=[wd="powerquery"]])
兩者是等價的,但是后者結構更清晰更便于修改和維護,所以在簡單的情況下使用前者更方便,在參數比較多的復雜情況下更推薦后者。
Content:如果是GET則不填,一旦填寫此參數將改為POST。填寫內容就是F12里的Form Data,然后點擊view source所看到的一整串文本,同樣參數與參數之間是以&連接的。在Fiddler中就是request框中Raw下的最底部的部分。
Headers:也就是F12中看到的Request Headers,其中當請求方式為Post時需要Content-Type,需要登錄時要Cookie,部分防盜鏈網站需要Referer。
服務器返回的數據可能有很多種類型,這個我們先不管,這一步我們的目的就是構建帶參數的request,獲取目標數據的response,我們先全部統一獲取源碼,到下一步再講如何轉換。
Text.FromBinary能夠將Web.Contents返回的binary解析出HTML源碼,而不同網站的編碼方式不同,中文網站常見的有GBK和UTF-8兩種編碼方式,一般在網頁頭部的meta部分都有聲明。
所以如果網頁采用GBK編碼,就要給Text.FromBinary加上第二參數0,否則會亂碼,下面有案例會講到。
講完這個,剩下的就是填空題了。
GET:
let url="", //Requset URL中?前面的部分 headers=[Cookie=""], //如果不需要登錄請刪除整行,同時刪除下一行中的Headers=headers query=[], //Query String Parameters,即Requset URL中?后面的部分 web=Text.FromBinary(Web.Contents(url,[Headers=headers,Query=query])) in web
POST:
let url="", headers=[#"Content-Type"="",Cookie=""], //Content-Type必填,如不需要登錄Cookie可省略 query=[], content="", web=Text.FromBinary(Web.Contents(url,[Headers=headers,Query=query,Content=Text.ToBinary(content)])) in web
其中的""和[]就是需要你自己去填的,建議在編輯器軟件中編輯好再粘貼到Power Query的高級編輯器中,在此推薦一款好用的編輯器——Sublime Text,輕量方便顏值高。
下面結合案例來做填空題:抓QQ郵箱收件箱。
F12,點擊收件箱-下一頁,在Doc里出現一條mail_list,觀察發現是GET方式,所以用第一個模板。
又因為郵箱肯定是需要登錄才能訪問的,所以要加上cookie。
把代碼編輯好復制到高級編輯器,發現返回的源碼有亂碼,再看一眼編碼方式是GBK,所以加上第二參數0,結果正確,你收件箱里某一頁的數據就都出來了。
很簡單吧,你可以再嘗試抓QQ空間,百度網盤這些,方法都一樣。
再來舉個特殊情況的案例:http://bond.sse.com.cn/bridge/information/。
F12,點擊下一頁,這回是在JS里有個commonQuery.do,也不難找到,是GET方式,但是把Request URL復制粘貼到瀏覽器地址欄卻發現打不開,用Power Query也無法抓到數據。
簡單來說就是網站做了防盜鏈處理,需要加上Request Headers里的Referer。
當然特殊情況也不止這一種,如果你很確定數據就在這條請求里,但是就是出不來,思考下什么原因?因為F12中本來有很多參數,我們只填了幾個必填的參數,其他都省略了,出不來數據說明我們把某個必要的參數給省略了,那么可以把F12中看到的所有參數全部填上去試下,多半都能返回正確結果。
另外目前許多網站都部署了SSL認證的HTTPS協議,相當于HTTP的加強版,更加安全,比如本文一開始講到的百度的案例。
但是Power Query目前對HTTPS支持不是太友好,所以如果碰到URL是https開頭的請改成http,否則很可能會出錯。
一般來說默認提交的GET或POST參數有很多,但很多都是無效或者不相關的參數,去掉也不影響結果,所以像我這種有強迫癥的就習慣挨個把參數去掉測試。
本節介紹了如何在Power Query中構建參數并向服務器發出請求,這是最簡單的一步,其實就是填空題套模板。
這步完成后,可以大致瀏覽下返回的源碼,看下我們要的數據是否在其中,如果沒問題就進行下一步——數據清洗。
經過上兩步,我們已經抓到所需要的數據,但是格式比較亂,我們需要對其清洗成為規范的表格。
服務器返回的數據,有可能是HTML,JSON,XML等格式,舉幾個例子,請分別復制到瀏覽器打開比較下區別:
HTML:
http://datacenter.mep.gov.cn:8099/ths-report/report!list.action?xmlname=1462261004631
普通的網頁結構,最簡單的一種情況,HTML源碼中包含table標簽,使用Web.Page能夠直接解析成表格,再深化出table即可。
JSON:
http://platform.sina.com.cn/sports_all/client_api?_sport_t_=football&_sport_s_=opta&_sport_a_=teamOrder&app_key=3571367214&type=10&season=2016
純文本形式的結構化數據,一個字段對應一個值,使用Json.Document解析。但解析出來的不是表格而是record,除了我們要的數據還可能包含狀態碼、頁數等等,所以需要找到數據所在的list,使用Table.FromReocrds還原成表。不會也沒關系,到這一步剩下的基本靠純界面操作就能完成。
XML:
http://www.cffex.com.cn/sj/hqsj/rtj/201710/18/index.xml
與JSON類似,都是純文本形式的結構化數據,但沒有JSON常見,使用Xml.Tables解析。
以上都屬于結構化數據,就是能夠通過函數直接或間接轉換為表格,但很多時候我們遇到的可能是一些非結構化數據,比如要抓本站所有文章的標題,它既不是表格,也不是JSON,函數解析不出來,那要怎么搞呢?
常見的有正則,XPath,CSS Selector等方法,但很遺憾PQ一個都不支持。。。所以PQ在處理這些數據的時候就比較痛苦了,只能保持第二步中Text.FromBinary解析出來的源碼,然后當作文本來用Text類函數提取。
Web.Contents返回的數據類型為binary,Text.FromBinary把binary解析為text,我們可以直接使用上面三個函數來替換Text.FromBinary的位置解析binary,也可以套在Text.FromBinary的外面來解析text,理論上效果是一樣的,但是有些時候卻直接解析不出來,必須加一個Text.FromBinary,比如案例篇的練習題。
接下來講很多人關心的翻頁問題,如何批量抓取多個網頁然后合并呢?
以第一個靜態頁的案例為例
http://quote.stockstar.com/stock/ranklist_a_3_1_1.html
我們先寫出完整的代碼:
let 源=Web.Page(Web.Contents("http://quote.stockstar.com/stock/ranklist_a_3_1_1.html")){0}[Data] in 源
URL結尾的a_3_1_1中最后一個1表示頁數,往后翻會依次變成23456..現在要抓1到10頁,那么我們只要把最后一個數改成23456..依次抓一遍即可。
但問題是抓不同頁數的代碼只是改了一個數字,其他部分都是一樣的,我們不可能要抓10頁就把代碼重復10遍,這太麻煩了,所以可以把變化的部分做成變量封裝為自定義函數,寫成
fx=(page)=> Web.Page(Web.Contents("http://quote.stockstar.com/stock/ranklist_a_3_1_1"&Text.From(page)&".html")){0}[Data]
然后用List.Transform遍歷循環{1..10},分別調用自定義函數fx得到一個包含10張表的列表,最后用Table.Combine將10張表合并為一張表,寫成:
let fx=(page)=> Web.Page(Web.Contents("http://quote.stockstar.com/stock/ranklist_a_3_1_1"&Text.From(page)&".html")){0}[Data], 結果=Table.Combine(List.Transform({1..10},fx)) in 結果
注意,頁數是數字,與URL的文本相連時要用Text.From進行數據類型轉換。
同理,如果要批量抓取多日期、多ID之類的,只要更改自定義函數中的變量即可。
而如果我們不是要抓前10頁而是所有頁,而且事先是不知道一共有多少頁的怎么辦?如果返回的是JSON,大部分情況下數據中都會包含一個叫做totalpage的字段,所以可以分兩步,第一步先隨便提交一個頁碼拿到totalpage,可參考案例篇附件。
但是比如你現在正在使用我介紹的方法批量抓取我的網站數據,如果再多幾個你這樣的,那我的網站基本上就炸了。
一直如此高頻率地訪問網站,那得給服務器帶來多大的壓力。
所以很多網站會采取防爬蟲措施,如果訪問太頻繁就會被封IP。PQ雖然不支持代理IP池之類的,但是延時還是支持的。
如果你要抓的網站會封IP,那么可以在自定義函數外面嵌套Function.InvokeAfter,設置每爬一次停頓個5秒。
比如
=Function.InvokeAfter(()=>1+1,#duration(0,0,0,5))
你會發現你的電腦算1+1還沒你算的快。
很多時候我們希望能夠實現類似網頁中的體驗,輸入指定條件比如開始和結束日期,根據指定條件抓取數據,如下圖:
那么也很簡單,只需要把需要查詢的內容導入PQ,替換自定義函數中的變量即可,可參考案例篇附件。
另外值得一提的是,上面介紹過抓取需要登錄的網站要加cookie,而cookie是有生命周期的,有時候你發現昨天抓到的數據今天就報錯了,就是因為cookie過期了,你可以打開高級編輯器修改cookie的代碼,但是顯然太麻煩了。所以也可以把cookie寫在單元格中,然后導入PQ,這樣就可以實現在不打開PQ的情況下實現本需要修改代碼的刷新。
調用API:
API即應用程序編程接口,調用API能夠實現很多Power Query本身無法實現的功能,比如根據地址獲取經緯度、翻譯、分詞、正則、機器人自動回復等等功能,可參考《使用PQ調用API》。
調用API的原理和網抓是一樣的,其實很多網站的數據本身也是使用API調出來的。
一般開發文檔都有詳細說明,是使用GET還是POST,然后根據說明向服務器提交參數即可,返回的數據一般都是JSON。
部分API有免費限額,就是可以免費調用多少次,超出限額的需要收費,所以常見的比如地圖、翻譯等API都需要去開放平臺注冊為開發者,然后把自己的密鑰加入到提交的參數中。
編程語言接口:
上面簡單介紹了一下API,你可以把API理解成封裝在服務器中的自定義函數,只需要向服務器提交函數所需要的參數,就能夠返回你想要的結果。
那么服務器中的函數是怎么來的?那肯定是用編程語言來寫的,比如PHP,Python等等,流程就是:你通過API提交參數,服務器中的編程語言寫的程序來計算,得出結果并返回給你。
所以理論上只要是能夠配置服務器環境的編程語言,都可以與PQ形成交互,比如《在Power Query中使用正則表達式》就是用PHP寫的。
再比如使用Python的bottle包搭建本地服務器框架,便能夠通過訪問localhost與Python交互,可參考《M與Python交互》。
此篇長文是施陽大神的手筆,來自于pqfans。本來我想寫一篇:
但折騰來折騰去發現還是沒法超越施陽這篇文章,于是發來頭條分享。僅作了細微文字上的梳理。擴展鏈接給出了原文鏈接。
*請認真填寫需求信息,我們會在24小時內與您取得聯系。