整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          (詳解)從瀏覽器輸入 URL 到頁面展示過程發生了什

          (詳解)從瀏覽器輸入 URL 到頁面展示過程發生了什么?

          薦閱讀:

          阿里架構師精選Nginx+Redis+Sping+SpringBoot源碼級PDF文檔分享

          微服務+Docker完美教程,被阿里架構師匯集到這2份文檔里面了!

          引言

          對于面試常問的從瀏覽器輸入 URL 到頁面展示過程發生了什么?,我想大家都或多或少能說出一二。但是,其實這個問題很有深度,而你是否回答的有深度,在很大程度上會影響到面試官對你的印象。

          并且,網上各種資料都是淺嘗輒止地講解這個過程,經常會出現今天看到這個版本,明天看到另一個版本地情況。所以,這次我們就來深入淺出一下這整個過程~

          一、Chrome 多進程架構

          首先,在開始講解整個過程前,我們需要認識一下 Chrome 多進程架構。因為,從瀏覽器輸入 URL 到頁面渲染的整個過程都是由 Chrome 架構中的各個進程之間的配合完成。

          Chrome 的多進程架構:

          • 瀏覽器進程,它負責用戶界面(地址欄、菜單等等)、子進程的管理(例如,進程間通信和數據傳遞)、存儲等等
          • 渲染進程,它負責將接收到的 HTML 文檔和 JavaScript 等轉化為用戶界面
          • 網絡進程,它負責網絡資源的請求,例如 HTTP請求、WebSocket 模塊
          • GPU(圖形處理器)進程,它負責對 UI 界面的展示
          • 插件進程,它負責對插件的管理

          二、過程詳解

          2.1 解析輸入

          發生這個過程的前提,用戶在地址欄中輸入了 URL,而地址欄會根據用戶輸入,做出如下判斷:

          • 輸入的是非 URL 結構的字符串,則會用瀏覽器默認的搜索引擎搜索該字符串
          • 輸入的是 URL 結構字符串,則會構建完整的 URL 結構,瀏覽器進程會將完整的 URL 通過進程間通信,即 IPC,發送給網絡進程

          2.2 請求過程

          在網絡進程接收到 URL 后,并不是馬上對指定 URL 進行請求。首先,我們需要進行 DNS 解析域名得到對應的 IP,然后通過 ARP 解析 IP 得到對應的 MAC(Media Access Control Address)地址。

          域名是我們取代記憶復雜的 IP 的一種解決方案,而 IP 地址才是目標在網絡中所被分配的節點。MAC 地址是對應目標網卡所在的固定地址。

          1. DNS 解析

          而 DNS 解析域名的過程分為以下幾個步驟:

          • 詢問瀏覽器 DNS 緩存
          • 詢問本地操作系統 DNS 緩存(即查找本地 host 文件)
          • 詢問 ISP(Internet Service Provider)互聯網服務提供商(例如電信、移動)的 DNS 服務器
          • 詢問根服務器,這個過程可以進行遞歸和迭代兩種查找的方式,兩者都是先詢問頂級域名服務器查找

          2. 通信過程

          首先,建立 TCP 連接,即三次握手過程

          • 客戶端發送標有 SYN 的數據包,表示我將要發送請求。
          • 服務端發送標有 SYN/ACK 的數據包,表示我已經收到通知,告知客戶端發送請求。
          • 客戶端發送標有 ACK 的數據包,表示我要開始發送請求,準備被接受。



          然后,利用 TCP 通道進行數據傳輸

          • 服務端接收到數據包,并發送確認數據包已收到的消息到客戶端,不斷重復這個過程
          • 客戶端在發送一個數據包后,未接收到服務端的確定消息,則重新發送該數據包,即 TCP 的重發機制
          • 當接收完所有的數據包后,接收端會按照 TCP 頭中的需要進行排序,形成完整的數據

          最后,斷開 TCP 連接,即四次握手過程

          • 客戶端發送請求,申請斷開連接,進入等待階段,此時不會發送數據,但是會繼續接收數據。
          • 服務端接收請求后,告知客戶端已明白,此時服務端進入等待狀態,不會再接收數據,但是會繼續發送數據。
          • 客戶端收到后,進入下一階段等待。
          • 服務端發送完剩余的數據后,告知客戶端可以斷開連接,此時服務端不會發送和接收數據。
          • 客戶端收到后,告知服務端我開始斷開連接。
          • 服務端收到后,開始斷開連接。



          而這整個過程的客戶端則是網絡進程。并且,在數據傳輸的過程還可能會發生的重定向的情況,即當網絡進程接收到狀態碼為 3xx 的響應報文,則會根據響應報文首部字段中的 Location 字段的值進行重新向,即會重新發起請求

          3. 數據處理

          當網絡進程接收到的響應報文狀態碼,進行相應的操作。例如狀態碼為 200 OK 時,會解析響應報文中的 Content-Type 首部字段,例如我們這個過程 Content-Type 會出現 application/javascript、text/css、text/html,即對應 Javascript 文件、CSS 文件、HTML 文件。

          詳細的 MIME 類型講解可以看 MDN

          2.3 創建渲染進程

          當前需要渲染 HTML 時,則需要創建渲染進程,用于后期渲染 HTML。而對于渲染進程,如果是同一站點是可以共享一個渲染進程,例如 a.abc.com 和 c.abc.com 可以共享一個渲染渲染進程。否則,需要重新創建渲染進程

          需要注意的是,同站指的是頂級域名二級域名相等

          2.4 開始渲染

          在創建完渲染進程后,網絡進程會將接收到的 HTML、JavaScript 等數據傳遞給渲染進程。而在渲染進程接收完數據后,此時用戶界面上會發生這幾件事:

          • 更新地址欄的安全狀態
          • 更新地址欄的 URL
          • 前進后退此時 enable,顯示正在加載狀態
          • 更新網頁



          2.5 渲染過程

          大家都知道頁面渲染的過程也是面試中單獨會考的點,并且時常會由這個點延申出另一個問題,即如何避免回流和重繪。

          渲染過程,是整個從理器輸入 URL 到頁面渲染過程的最后一步。而頁面渲染的過程可以分為 9 個步驟:

          • 解析 HTML 生成 DOM 樹
          • 解析 CSS 生成 CSSOM
          • 加載或執行 JavaScript
          • 生成渲染樹(Render Tree)
          • 布局
          • 分層
          • 生成繪制列表
          • 光柵化
          • 顯示

          2.5.1 構建 DOM 樹

          由于網絡進程傳輸給渲染進程的是 HTML 字符串,所以,渲染進程需要將 HTML 字符串轉化成 DOM 樹。例如:



          需要注意的是這個 DOM 樹不同于 Chrome-devtool 中 Element 選項卡的 DOM 樹,它是存在內存中的,用于提供 JavaScript 對 DOM 的操作。

          2.5.2 構建 CSSOM

          構建 CSSOM 的過程,即通過解析 CSS 文件、style 標簽、行內 style 等,生成 CSSOM。而這個過程會做這幾件事:

          • 規范 CSS,即將 color: blue 轉化成 color: rgb() 形式,可以理解成類似 ES6 轉 ES5 的過程
          • 計算元素樣式,例如 CSS 樣式會繼承父級的樣式,如 font-size、color 之類的。



          CSS Object Model 是一組允許用 JavaScript 操縱 CSS 的 API。詳細 API 講解可以看 MDN

          2.5.3 加載 JavaScript

          通常情況下,在構建 DOM 樹或 CSSOM 的同時,如果也要加載 JavaScript,則會造成前者的構建的暫停。當然,我們可以通過 defer 或 sync 來實現異步加載 JavaScript。雖然 defer 和 sync 都可以實現異步加載 JavaScript,但是前者是在加載后,等待 CSSOM 和 DOM 樹構建完后才執行 JavaScript,而后者是在異步加載完馬上執行,即使用 sync 的方式仍然會造成阻塞。

          而 JavaScript 執行的過程,即編譯和運行 JavaScript 的過程。由于 JavaScript 是解釋型的語言。所以這個過程會是這樣的:

          • 針對每句代碼進行分行處理,即 Token 化
          • 根據 Token,生成 AST(Abstract Sytanx Tree) 抽象語法樹和創建上下文
          • 解釋器解析和執行 AST,生成字節碼。
          • 編譯器針對需要反復執行的代碼,生成對應的機器碼,提高運行效率

          2.5.4 生成渲染樹(Render Tree)

          在有了 DOM 樹和 CSSOM 之后,需要將兩者結合生成渲染樹 Render Tree,并且這個過程會去除掉那些 display: node 的節點。此時,渲染樹就具備元素和元素的樣式信息。

          2.5.5 布局

          根據 Render Tree 渲染樹,對樹中每個節點進行計算,確定每個節點在頁面中的寬度、高度和位置。

          需要注意的是,第一次確定節點的大小和位置的過程稱為布局,而第二次才被稱為回流

          2.5.6 分層

          由于層疊上下文的存在,渲染引擎會為具備層疊上下文的元素創建對應的圖層,而諸多圖層的疊加就形成了我們看到的一些頁面效果。例如,一些 3D 的效果、動畫就是基于圖層而形成的。

          值得一提的是,對于內容溢出存在滾輪的情況也會進行分層

          2.5.7 生成繪制列表

          對于存在圖層的頁面部分,需要進行有序的繪制,而對于這個過程,渲染引擎會將一個個圖層的繪制拆分成繪制指令,并按照圖層繪制順序形成一個繪制列表。

          2.5.8 光柵化

          有了繪制列表后,渲染引擎中的合成線程會根據當前視口的大小將圖層進行分塊處理,然后合成線程會對視口附近的圖塊生成位圖,即光柵化。而渲染進程也維護了一個柵格化的線程池,專門用于將圖塊轉為位圖。

          柵格化的過程通常會使用 GPU 加速,例如使用 wil-change、opacity,就會通過 GPU 加速顯示

          2.5.9 顯示

          當所有的圖塊都經過柵格化處理后,渲染引擎中的合成線程會生成繪制圖塊的指令,提交給瀏覽器進程。然后瀏覽器進程將頁面繪制到內存中。最后將內存繪制結果顯示在用戶界面上。

          而這個整個從生成繪制列表、光柵化、顯示的過程,就是我們常說的重繪的過程

          結語

          整個瀏覽器輸入 URL 到頁面渲染的過程涉及到的知識點非常廣,如 Chrome 多進程的架構、HTTP 通信過程、瀏覽器解析 JavaScript 過程、瀏覽器繪制頁面過程以及一些計算機的基礎知識等等,并且,這整個過程的分析其實和 Chrome-devtools 密切相關,所以很好的使用 Chrome-devtools 是非常重要的,后續應該會出一篇關于使用 Chrome-devtools 的指南。當然,本篇文章仍然存在諸多不足,歡迎提 issue ~


          作者:五柳
          鏈接:https://juejin.im/post/5e871ee56fb9a03c832b0013

          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。本來我想寫一篇:

          但折騰來折騰去發現還是沒法超越施陽這篇文章,于是發來頭條分享。僅作了細微文字上的梳理。擴展鏈接給出了原文鏈接。

          加國考的小伙伴們看過來!

          中央機關及其直屬機構2021年度考試錄用公務員筆試成績今早公布。中央機關和省級(含副省級)直屬機構職位合格分數線為總分不低于105分,且行政職業能力測驗不低于60分。國考調劑公告今天也同步發布。

          考生可點擊本條微信左下角的“閱讀原文”登錄后查詢考試成績,或在電腦中打開網址http://bm.scs.gov.cn/pp/gkweb/core/web/ui/business/auth/login.html查詢。

          各檔分數線確定

          國家公務員局說,今年劃定筆試合格分數線時,既考慮到新錄用公務員必須具備的基本素質,又兼顧了不同層級機關招考職位對公務員能力的不同要求,采取了分層劃線的方法,同時對西部地區和艱苦邊遠地區基層職位予以一定的政策傾斜。其中,中央機關和省級(含副省級)直屬機構職位合格分數線為總分不低于105分,且行政職業能力測驗不低于60分;市(地)級和縣(區)級直屬機構職位合格分數線為總分不低于95分,且行政職業能力測驗不低于50分;西部地區和艱苦邊遠地區(山西、內蒙古、吉林、黑龍江、廣西、重慶、四川、貴州、云南、西藏、陜西、甘肅、青海、寧夏、新疆等15個省區市)市(地)級和縣(區)級直屬機構職位,定向招錄大學生村官、“三支一扶”等服務基層項目人員和在軍隊服役5年(含)以上的高校畢業生退役士兵職位,非通用語職位,以及特殊專業職位合格分數線為總分不低于90分,且行政職業能力測驗不低于45分。此外,中國銀保監會、中國證監會職位和公安機關人民警察職位統一組織了專業科目筆試,專業科目筆試合格分數線為不低于45分。

          各職位進入面試的人員名單將根據規定的面試比例,按照筆試成績從高到低的順序確定,面向社會統一公布。面試工作由各招錄機關具體實施。面試前,將在“中央機關及其直屬機構2021年度考試錄用公務員專題網站”發布面試公告,載明面試人員名單、面試最低分數線、面試時間、面試地點、聯系方式以及其他須知事項。對于公共科目筆試合格人數與擬錄用人數之比未達到規定面試比例的部分職位,國家公務員局將在今年1月組織公開調劑,后續還將針對出現人員空缺的職位,面向社會統一進行補充錄用。

          國家公務員局根據公務員法和公務員錄用有關規定,對違反考試紀律的人員作出了考試成績無效、取消考試資格、限制報考等處理,進一步嚴肅考風考紀。

          中央機關及其直屬機構2021年度

          考試錄用公務員調劑公告

          中央機關及其直屬機構2021年度考試錄用公務員筆試成績已經公布,部分職位筆試合格人數與擬錄用人數之比未達到規定的面試比例,根據中央機關及其直屬機構2021年度考試錄用公務員工作實施方案,需要面向社會公開調劑補充面試人選?,F將有關事項公告如下:

          一、調劑的原則和條件

          (一)調劑在公共科目考試內容相同(即指報考人員應答的是同一類試卷)的職位之間進行;

          (二)已進入首批面試名單的報考人員不得參加調劑;

          (三)參加調劑的報考人員只能申請一個調劑職位;

          (四)申請調劑的報考人員應當符合調劑職位規定的資格條件和要求;

          (五)申請調劑的報考人員公共科目筆試成績應當同時達到原報考職位的最低合格分數線和擬調劑職位的最低合格分數線;

          (六)進入調劑職位面試的人員不得參加原報考職位的遞補。

          二、調劑程序

          (一)報考人員從即日起,可以登錄“中央機關及其直屬機構2021年度考試錄用公務員專題網站”(以下簡稱“專題網站”,http://bm.scs.gov.cn/kl2021)查詢調劑職位、調劑人數、考試類別、資格條件、招錄機關聯系方式等。

          (二)2021年1月12日8:00至1月14日18:00期間,報考人員通過報名時的用戶名和密碼登錄“專題網站”,填報申請調劑的相關信息。報考人員對調劑職位所需專業、學歷、學位、資格條件以及備注內容等信息需要咨詢時,請直接與招錄機關聯系。在提交調劑申請時間結束之前,報考人員可以改變申請調劑的職位。

          非網上報名的報考人員參加調劑時,請直接與擬申請調劑的招錄機關聯系,在2021年1月14日18:00前提交書面調劑申請。

          (三)2021年1月15日8:00至1月16日18:00期間,招錄機關對涉及本機關(單位)職位的網上調劑申請和書面調劑申請一并進行資格審查。資格審查時,按照調劑人員公共科目筆試成績從高分到低分的順序進行。公共科目筆試總成績相同的,按行政職業能力測驗科目的成績排序。公共科目筆試總成績和行政職業能力測驗科目成績都相同的,一并進入資格審查。審查合格人數達到規定的調劑人數后,招錄機關不再對其他人員進行資格審查。

          三、查詢調劑結果

          調劑結束后,將形成進入面試的人選名單,在“專題網站”統一公布。2021年1月18日之后,可以登錄“專題網站”查詢調劑結果。

          打開網址查詢http://bm.scs.gov.cn/pp/gkweb/core/web/ui/business/auth/login.html,登錄后可查分哦

          資料:國家公務員局

          來源: 上海發布


          主站蜘蛛池模板: 无码一区二区三区在线| 无码少妇A片一区二区三区| 无码AⅤ精品一区二区三区| 熟妇人妻一区二区三区四区| 亚洲av无码天堂一区二区三区 | 在线观看中文字幕一区| 国产品无码一区二区三区在线| 国产中的精品一区的| 精品人妻系列无码一区二区三区| 亚洲综合色一区二区三区小说| 日韩有码一区二区| 亚洲视频一区网站| 国产成人无码一区二区三区 | 亚洲AV无码一区二区乱子伦| 欧洲亚洲综合一区二区三区| 国产精品夜色一区二区三区| 一区二区国产在线播放| 亚洲日韩一区精品射精| 色欲综合一区二区三区| 亚洲国产精品一区二区久| 成人一区二区免费视频| 狠狠色成人一区二区三区| 国产一区二区高清在线播放| 中文字幕无码一区二区三区本日| 日本美女一区二区三区| 美女免费视频一区二区三区| 亚洲欧洲一区二区三区| 大屁股熟女一区二区三区| 亚洲熟女乱综合一区二区| 国产AV国片精品一区二区| 一区二区三区人妻无码| 性色A码一区二区三区天美传媒| 精品视频无码一区二区三区| 无码人妻精品一区二区三区9厂 | 2021国产精品视频一区| 另类国产精品一区二区| 精品一区二区三区电影| 日本不卡在线一区二区三区视频| 无码一区二区三区老色鬼| 亚洲丶国产丶欧美一区二区三区 | 精品一区二区91|