迎來閱讀第三篇Windows 命令行系列文章。在這篇,我們開始深入Windows 控制臺和命令行,它是什么,你可以用它可以做什么……和它不能做什么!
系列文章:
在開始開發Windows NT操作系統的那時候,大概是1989年,那時候還沒有GUI(圖形化用戶界面),也沒有桌面操作系統,只有最原始的全屏的命令行界面,類似于MS-DOS的可視化界面越來越重要!Windows GUI 開始開發的時候是在開發團隊需要開發一個基于控制臺的應用的背景下誕生的!Windows 控制臺是第一個Windows NT的GUI應用,并且可以保證兼容運行繼續使用已有的Windows應用。
Windows 控制臺最初的代碼到現在(2018年)已經有30年的歷史……古老的東西,事實上,今天還有很多開發者在使用它!
控制臺程序能做什么?
就像之前的文章說的,終端的工作其實很簡單:
但是,Windows 控制臺能做的事情有些不同:
深入Windows控制臺內部
Windows控制臺是一種傳統的Win32可執行文件,雖然它最初是用“C”編寫的,但隨著團隊現代化和模塊化控制臺的代碼庫,大部分代碼都已正在遷移到現代C++了。
對于那些關心此類事物的人:許多人都在詢問Windows是用C還是C++編寫的。答案是 - 盡管NT是基于對象的設計 - 像大多數操作系統一樣,Windows幾乎完全用C語言編寫!為什么? C++在內存占用和代碼執行開銷方面引入了開銷。即使在今天,使用C++編寫的代碼的其所隱藏的開銷也會令人大吃一驚,但早在1990年代后期,此時內存價格約為60$/MB(是的......每個MEGABYTE為60美元!)時,vtable等隱藏機制的內存開銷非常高。此外,虛方法間接調用和對象解引用的開銷可能導致當時的C++代碼存在非常顯著的性能和規模損耗。雖然你仍然需要當心,現代C++在現代計算機上的性能開銷并不是一個值得關注的問題,同時考慮到其安全性、可讀性和可維護性方面的優勢,這通常是一種可接受的折衷...這就是為什么我們將Console的代碼穩步升級到現代C++這樣做的原因!
那么,Windows 控制臺內部是什么樣?
在 Windows 7 之前,Windows 控制臺實例托管于核心的客戶-服務器運行子系統(Client Server Runtime Subsystem,CSRSS)!然而,在 Windows 7 中,考慮到安全性和可靠性因素,控制臺從CSRSS 中剝離出來,組件了一個包含如下二進制文件的新家庭:
控制臺當前的內部結構總體結構圖就像這樣:
控制臺的核心組件包含如下內容(自下而上):
Windows控制臺API
從上述的控制臺架構圖中可以看出,與NIX終端不同的是,控制臺發送/接收API調用和/或數據序列化為IO控制(IOCTL)消息,而不是序列化后的文本! 甚至從(主要是Linux)命令行應用程序接收的文本中所嵌入的ANSI/VT序列也被提取、解析并轉換為API調用!
這種差異揭示了*NIX和Windows之間關鍵的基本哲學差異:在*NIX中,“一切都是文件”,然而在Windows中,“一切都是對象”!
兩種方法都有利有弊,我們將概括之,但避免在這里進行長篇大論。請記住,哲學中的這一關鍵差異是Windows和* NIX之間諸多差異的基礎!
在 *NIX系統中,一切都是文件
在60年代末和70年代初Unix被第一次實現的時候,其中一個核心原則就是任何東西都可以被抽象成文件流,一個關鍵目標是簡化對設備和外設的訪問處理:如果所有的設備都在系統中以文件系統的形式存在,那么現存的代碼就可以不做修改地直接訪問這些設備。
這個原則影響深遠:你可以通過偽文件系統或虛擬文件系統來瀏覽和查詢大量的基于*NIX的系統和機器配置,它們僅僅是”表現得“像是“文件”或“文件夾”,實際可能是機器配置或硬件。
例如,在Linux中,你可以通過訪問 /proc/cpuinfo 虛擬文件節點來查看CPU的一些信息:
這個模型是如此簡單和一致,但它也存在一些額外開銷:從這些偽文件中提取或查詢特殊的文本信息并從執行命令中返回,經常需要一些工具的輔助,比如:sed,awk,perl,python等。這些工具經常被用來寫腳本和命令來解析文本內容、查找特殊模式、區域和值。這些腳本可以變得非常復雜,難以維護和碎片化。如果文本的結構、布局或格式發生變更,那么許多腳本也需要隨之更新。
在Windows中,任何事物都是對象
當Windows NT被設計和構建時,“對象”被視為軟件設計的未來:“面向對象”的語言比洞穴里的兔子更快出現 - Simula和Smalltalk已經建立起來,而C ++正變得越來越流行。其他面向對象的語言,如Python,Eiffel,Objective-C,ObjectPascal / Delphi,Java,C#等許多其他語言都在快速發展緊隨其后。
不可避免的是,它成型于面向對象大好時期(大約1989年)中,Windows NT的設計理念是“一切都是對象”。事實上,NT內核最重要的部分之一是“對象管理器”!
Windows NT公開了一組豐富的Win32 API,可以調用這些API來從操作系統獲取和/或操作對象。開發人員使用Win32 API來收集和呈現* NIX偽文件和工具提供的類似信息,但是通過對象和結構。并且因為解析器,編譯器和分析器理解對象的結構,所以通常可以更早地捕獲許多編碼錯誤,從而幫助驗證程序員的意圖在語法和邏輯上是否正確。隨著時間的推移,這也可以減少系統破損,波動和“攪動”。
所以,回到我們關于Windows控制臺的中心討論:NT團隊決定構建一個“控制臺”,它在幾個關鍵領域區別于傳統的* NIX終端:
Windows控制臺的問題
雖然Console的API已經證明在Windows命令行工具和服務領域非常流行,但以API為中心的模型對命令行方案提出了一些挑戰:
只有Windows實現了Console API
許多Windows命令行工具和應用程序廣泛使用Console API。
問題呢?這些API僅適用于Windows。
因此,結合其他差異化因素(例如過程生命周期差異等),Windows命令行應用程序并不總是易于移植到* NIX,反之亦然。
因此,Windows生態系統開發了自己的,通常類似但通常不同的命令行工具和應用程序。這意味著用戶在使用Windows時必須學習一組命令行應用程序和工具,shell,腳本語言等,而在使用* NIX時則需要學習另一組。
這個問題沒有簡單的快速解決方案:Windows控制臺和命令行不能簡單地丟棄并被bash和iTerm2取代 - 有數以億計的應用程序和腳本依賴于Windows控制臺和Cmd / PowerShell shells。
像Cygwin這樣的第三方工具可以很好地將許多核心GNU工具和兼容性庫移植到Windows,但是它們無法運行未移植的,未經修改的Linux二進制文件。這非常重要,因為許多Ruby,Python,Node包和模塊依賴于或包裝Linux二進制文件,或者依賴于* NIX運轉狀態。
這些原因促使微軟通過在 Windows的子系統Linux(WSL)上本地運行真正的,未經修改的Linux二進制文件和工具來擴展Windows的兼容性。使用WSL的用戶現在可以在同一臺機器上并行下載和安裝一個或多個Linux發行版,并使用apt / zypper / npm / gem / etc.安裝和運行絕大多數Linux命令行工具以及他們喜歡的Windows應用程序和工具。
但是,還有一些控制臺提供的東西尚未被非Microsoft終端采用:具體來說,Windows控制臺提供命令歷史記錄和命令別名服務,從而無需每個命令行shell(特別是)重新實現相同的功能。
把 Windows 命令行遠程化是困難的
正如我們在 Command-Line Backgrounder 一文中所討論的那樣,終端最初與它們所連接的計算機是分開的。快進到今天,這種設計仍然存在:大多數現代終端和命令行應用程序/shell 等等是由進程或機器邊界分隔的。
在基于 *NIX 的平臺上,終端和命令行應用程序的分離并通過簡單的字符進行通信的概念導致 *NIX 命令行易于從遠程計算機/設備訪問和操作:只要終端和命令行應用程序可以通過某種類型的有序串行通信基礎架構(TTY/PTY 等)傳輸字符流,遠程操作 *NIX 機器的命令行是非常簡單的。
但是在 Windows 上,許多命令行應用程序依賴于調用 Console API,并假設它們與控制臺本身在同一臺機器上運行。這使得遠程操作 Windows 命令行 shell/工具等變得很困難:在遠程計算機上運行的命令行應用程序如何調用在用戶本地計算機的控制臺上的 API 呢?更糟糕的是,如果遠程命令行應用程序通過 Mac 或 Linux 機器上的終端訪問,它如何調用 Console API 呢?!
很抱歉開個玩笑,但我們將在以后的文章中更詳細地闡釋這個主題!
啟動控制臺或者不!
通常,在基于 *NIX 的系統上,當用戶想要啟動一個命令行工具時,他們首先會啟動一個終端。然后終端啟動一個默認的 shell ,或者可以配置為啟動一個特定的應用程序/工具。終端和命令行應用程序通過偽終端(PTY)交換字符流進行通信,直到一個或兩個字符終止。
然而,在 Windows 系統上,事情就不一樣了:Windows 用戶永遠不會啟動控制臺(conhost.exe)——然而他們會啟動像是 Cmd.exe,PowerShell.exe,wsl.exe 等等這樣的命令行 shell 和應用程序。Windows 系統將新啟動的應用程序連接到當前控制臺(如果是從命令行啟動的話),或者連接到新創建的控制臺實例。
# 現在要說的?
是的,在 Windows 系統中,用戶啟動命令行應用程序,而不是控制臺本身。
如果用戶從現有的命令行 shell 啟動命令行應用程序,Windows 通常會將新啟動的 .exe(可執行文件) 附加到當前控制臺。否則,Windows 會將一個新的控制臺實例與新推出的應用程序綁定在一起。
小白說:很多人說“命令行程序在控制臺運行”。這不是真的,而且導致很多關于控制臺和命令行應用程序如何工作的困惑!命令行應用程序和它們的控制臺都在各自獨立的 Win32 進程中運行。請通過指出“命令行工具/應用程序連接到控制臺運行”(或類似的)來幫助糾正這種誤解。謝謝!
聽起來不錯,對吧?嗯…不;這里有一些問題:
1.控制臺和命令行應用程序通過經由驅動程序的 IOCTL 消息進行通信,而不是通過文本流進行通信
2.windows 要求 ConHost.exe 必須是連接到命令行應用程序的控制臺程序
3.Windows 控制了控制臺和命令行應用程序通信之間通信“管道”的創建
這些都是明顯的限制:如果你想為 Windows 創建一個替代控制臺的應用程序,該怎么辦?你將如何發送鍵盤、鼠標、筆等等外設的信息?如果你無法訪問連接你新控制臺和命令行應用程序的通信“管道”,用戶將怎么對命令行應用程序進行操作?
遺憾的是,這些情況并不好:有一些很棒的用于 Windows 的第三方控制臺(和服務器應用程序)(例如 ConEmu/Cmder, Console2/ConsoleZ, Hyper, Visual Studio Code, OpenSSH 等),他們必須通過離奇的跳轉才能像正常的控制臺一樣運行!
舉例來說,第三方控制臺必須在屏幕外啟動一個命令行應用程序,例如(-32000,-32000)。然后,他們必須向屏幕外控制臺發送擊鍵信息,然后收集屏幕外控制臺的文本內容并在自己的 UI 上重新繪制它們!
我知道,這很瘋狂,對吧? !這證明了這些應用程序創造者們的獨創性和決心,這些程序甚至還在有效的運行!
這顯然是我們急于補救的一種情況。請繼續關注這部分內容的更多信息——在這方面有一些好消息!
Windows 控制臺 & VT
如上所述,Windows 控制臺提供了大量 API。使用控制臺 API,命令行應用程序和工具可寫入文本,更改文本顏色,移動光標等。并且,由于控制臺 API 的存在,Windows 控制臺幾乎不需要支持 ANSI/VT 序列,這些序列在其他平臺上提供非常類似的功能。
實際上,在 Windows 10 之前,Windows 控制臺僅實現了對 ANSI/VT 序列的最低限度支持:
從2014年開始,微軟組建了一個新的 Windows 控制臺團隊,使得這一切都發生了變化。控制臺團隊的最高優先級事項之一是實現對 ANSI/VT 序列的全面支持,以便渲染在 Windows 子系統之Linux(WSL)和遠程 *NIX 機器上運行的 *NIX 應用程序的輸出。您可以在本系列的上一篇文章中閱讀更多關于這個故事的內容。
控制臺團隊迅速為 Windows 10 的控制臺添加了對 ANSI/VT 序列的全面支持,使用戶能夠使用和享用大量 Windows 和 Linux 命令行工具和應用程序。
該團隊繼續改進和完善每個操作系統發布版本上的控制臺對 VT 的支持,并對您在我們的 GitHub 問題跟蹤器上提交的任何問題表示感謝。
處理Unicode
一個快速的Unicode回顧:
Unicode或ISO/IEC 10646是一個國際標準,定義了地球上幾乎每個書寫系統中所使用的每個字符/字形,以及當今使用的許多非腳本符號和字符大小的圖像(例如表情符號)。目前(2018年7月),Unicode 11定義了137439個字符,包含146個現代和歷史文字系統!
Unicode還定義了幾種字符編碼,包括UTF-8, UTF-16, 和UTF-32:
由于UTF-8的高效的存儲要求以及在HTML頁面中的廣泛使用,它是目前最流行的編碼。
UTF-16/UCS-2都是常見的,盡管在已存儲文檔(例如網頁、代碼等)中其使用比例正在降低。UTF-32是很少使用的,因為它的效率低且存儲需要相當大的空間。
很好,所以我們有有效并且高效的方式來表示和存儲Unicode字符了!
所以?
哎呀,Windows控制臺及其API是在創建Unicode之前創建的!
Windows控制臺將文本(隨后在屏幕上繪制)存儲為每個單元需要2個字節的UCS-2字符。
命令行應用程序使用控制臺API將文本寫入到控制臺中。處理文本的控制臺API有兩種形式 - 帶有A后綴處理的單字節/字符串的函數,帶有W后綴處理雙字節(wchar)/字符串的函數:
例如,WriteConsoleOutputCharacter()函數編譯為ASCII項目的WriteConsoleOutputCharacterA(),或Unicode項目的WriteConsoleOutputCharacterW()。如果需要指定處理方式,代碼中可以直接調用... A或...W后綴的函數。
注意:每個W API至少支持UCS-2,因為這是在進行A/W拆分時就存在的事情,我們認為這樣做會很棒。但許多W API已更新為在同一渠道上也支持UTF-16
。并非所有W API都可以支持UTF-16,但所有W API至少可以支持UCS-2。
此外,控制臺不支持一些較新的Unicode功能,包括零寬度連接符(ZWJ),該符號被用于連接阿拉伯語和印度語中的其他單獨字符,并將表情符號字符組合成一個可視字形!
那么如果你想在控制臺上輸出一個ninjacat表情符號或復雜的多字節中文/阿拉伯字符會怎樣呢? 糟糕的是,你做不到!
Console API不僅不支持長度超過2字節/字形的Unicode字符(NinjaCat表情符號需要8個字節!),但Console內部的UCS-2緩沖區不能存儲該數據的額外字節,更糟糕的是 ,Console當前的基于GDI的渲染器甚至無法繪制字形,即使緩沖區可以存儲它!
可嘆! 這就是遺留代碼的樂趣。
但是,我也會希望你們到此打住 - 我們將在本系列的新一篇文章中回到這個主題。 敬請關注!
再一次,親愛的讀者,如果你讀過以上的所有內容,謝謝你,也祝賀你 —— 你現在比你的大多數朋友都更了解 Windows 控制臺,甚至可能比你想知道的還要多!祝你幸運!
在這篇文章中,我們涵蓋了很多內容:
Windows控制臺的主要構建模塊:
控制臺做什么?
控制臺與 *NIX 終端有什么不同
控制臺存在的問題
在本系列的后續文章中,我們將深入探討控制臺,并討論如何處理這些問題……和更多其他內容!
像往常一樣,請繼續關注我們。
本文由oschina作者參與翻譯,如有侵權,請聯系刪除。
著網絡技術的發展,越來越多的應用基于互聯網發布,再好的應用,如果打開速度慢,10個用戶會有9個用戶選擇離開,相關統計數據顯示,每增加0.1秒的加載延遲,將會導致客戶活躍度下降1%。在目前獲客成本較高,用戶面臨眾多可選項的情況下,如何提高用戶訪問的體驗,給用戶留下良好的第一印象,提高ROI,是所有開發互聯網應用的企業都關注的核心問題。
影響應用資源加載的因素很多,服務器性能、網絡傳輸質量、網站出口帶寬狀況、DNS解析時間、網頁內容大小、終端用戶網絡質量等,在云計算技術高度發達的今天,并非每一個環節的優化都需要企業自己造輪子,更為便捷可行的選擇是:借助云服務商提供的相應加速服務來優化企業的應用,可以實現更低的成本、更敏捷快速的建設、更強壯和高性能的服務,用來取代傳統的用昂貴的成本購買大量服務器、帶寬做自建的模式。
標準CDN服務所擅長加速的內容是靜態內容,如文件、圖片、視頻等,通過CDN的緩存策略來緩存并實現加速。但互聯網上的應用復雜,源站往往也會有很多經常變化的內容---動態內容,如用戶登錄、內容搜索、視頻彈幕、直播評論、購物交易、股票行情、體育實況等,很多時候這些應用沒有做動靜分離設計和動靜內容分別處理,這也造成很多應用即使采用了標準CDN服務,卻沒有達到很好的加速效果,因為動態部分內容的加速性能并沒有得到很好的優化。
對應上敘的情況,我們推薦使用阿里云全站加速產品,阿里云全站加速產品是阿里云自主研發的融合了動態加速技術和靜態加速技術的CDN產品,可以很好的解決頁面動靜態資源混合、跨運營商、網絡不穩定、單線源站、突發流量、網絡擁塞等諸多因素導致的響應慢、丟包、服務不穩定的問題,全面提升全站性能和用戶體驗。
阿里云全站加速產品可以有效提升靜態、動態內容的加速效果。下面的圖我們可以具體看下,使用全站加速產品前后的效果對比。從實際的測試結果圖中可以看到使用全站加速前,無論訪問效率,還是節點加速效果,都較未使用前有了明顯的提升。
那阿里云全站加速怎么樣才能夠實現以上的效果呢?主要通過以下四個方面完成:
第一、全球覆蓋的加速節點阿里云在全球有2800+節點,這些節點涵蓋了中國國內、歐洲、美洲、亞洲、非洲等全世界的大部分區域,同時這些節點都是互聯了當地最核心的運營商網絡,阿里云全站加速可以保證全球用戶都能夠找到離他最近的、訪問質量最好的和用戶接入網絡運營商相對應的加速節點。
第二、全球智能調度系統在上面我們講到了覆蓋,只有覆蓋還是不夠的,還需要把用戶調度到對應的最合理的節點,這個環節就非常取決于調度的IP庫的完善性和準確性。阿里云全球智能調度系統結合阿里整體龐大的用戶基礎(淘寶、天貓、優酷等),基于這些用戶基礎可以打造非常詳盡、精準的用戶IP庫,可以有效保證用戶接入匹配的高準確度。
第三、智能自適應緩存業界一般對于全站加速的場景,需要客戶手動配置動靜態內容,來讓CDN平臺執行動態和靜態兩種加速模式。但是很多網站特別是中小客戶,動靜態內容區分不是很清晰,不便于做動靜態的區分。全站加速推出的動靜態智能自適應功能,可以讓客戶不再需要繁瑣配置動靜態內容區分,全站加速會自動的分析和識別請求和響應特征,智能的對訪問的內容進行動靜態分類,讓可以緩存的靜態內容避免了動態化訪問源站,從而降低了回源帶寬、回源時間而提升了性能。當然,阿里云全站加速平臺也支持客戶通過自定義的方案,很方便的自行定義實動靜態內容加速規則。
此外,針對靜態內容,阿里云全站加速還可以通過智能壓縮功,自動對靜態文件進行Gzip壓縮,以及通過頁面優化,對當前域名下所有HTML頁面中冗余的注釋和重復的空白符進行優化,以減小傳輸文件大小,減少流量支出和提升加速分發效率。
第四、智能路由對于無法緩存或者不允許緩存的內容,最核心的處理邏輯是實現路徑加速,阿里云的路徑加速是通過智能路由來實現的,在廣泛覆蓋的節點之間,通過實時探測通信網絡質量,并根據探測的質量,進行路徑的有效分析,同時結合阿里達摩院的最佳數據計算模型,提供一條從用戶的接入點到源站之間一個最優的路徑,實現最好的加速效果。
阿里云全站加速產品除了通過以上的策略提供了優秀的加速效果之外,還提供了下面的相關的功能模塊讓客戶的服務可以變得更靈活和更健壯,以及還提供了更廣泛和新穎的加速模式:
第一、源站策略1、智能回源策略全站加速除了提供最優鏈路回源來保證最好的服務質量的同時,還提供了豐富的回源策略管理。很多客戶的場景處于安全和自身業務的需求,往往會有一些復雜的策略需要CDN來適配。目前全站加速在回源策略方面具有豐富的功能。
2、靈活回源配置和重試容災策略阿里云全站加速提供了靈活的回源配置策略,可以根據需求配置多主源(可設置不同的回源權重)、主備源,源站可以使用IP和域名。
網絡情況瞬息萬變,連接抖動和擁堵時常發生,在長鏈路傳輸時,情況會更加嚴峻。通常情況下在回源階段,因為鏈路的加長,整體的網絡可控性降低。經常會遇到回源節點的機房網絡有問題,回源的某條運營商鏈路斷了等等相關的問題。
結合阿里云全站加速的提供了多種回源配置以及回源重試容災策略,可以避免單源站問題、源站單IP問題、源站偶發不通等問題,給業務提供更健壯的一個支撐。
3、 WaitingRoom回源方案在回源的時候,有的時候會面臨一種場景,就是某次突發活動請求的壓力非常大,舉個例子,比如在春運火車票購票的時候,請求的壓力可能是平時壓力的上百倍,短時間之內沒有辦法擴容這么大倍數能力的源站來解決請求問題的(短時間內擴容源站上百倍的能力,會面臨到很大的成本壓力以及很長的時間周期問題)。針對上面這種場景,全站加速提供了WaitingRoom解決方案,可以靈活根據請求的URL、配置的回源比例、排隊時長,實現突發情況下有序的回源,保證源站服務穩定性。
第二、全鏈路https加速我們知道HTTP協議以明文方式發送內容,不提供任何方式的數據加密。HTTPS協議是以安全為目標的HTTP通道, HTTPS提供了身份驗證與加密通訊方法,被廣泛用于網上安全敏感的通訊,例如交易支付、金融應用、API接口、政務信息等。
通過阿里云全站加速的控制臺,可快速開啟HTTPS協議,實現客戶端和全站加速之間請求的HTTPS加密,保障數據傳輸的安全性,防止HTTP明文傳輸中的被竊聽、篡改、冒充和劫持風險。
目前主流瀏覽器已將HTTP協議標識為不安全,若堅持使用HTTP協議,除了安全會埋下隱患外,終端客戶在訪問網站時出現的不安全標識,也將影響訪問。
第三、WebSocket加速WebSocket協議是基于TCP的一種新的網絡協議。實現了瀏覽器與服務器全雙工(full-duplex)通信,允許服務器主動發送信息給客戶端。在WebSocket中,瀏覽器和服務器只需要完成一次握手,兩者之間創建持久性的連接,進行雙向數據傳輸,客戶端和服務器之間的數據交換變得更加簡單高效。
阿里云的全站加速產品也提供了對應的Websocket加速模式,通過阿里云全站加速的Websocket, 可在視頻彈幕、在線教育筆記大綱等信息推送、股票、金融產品實時報價、體育實況更新、視頻會議和聊天、基于位置的應用等場景中使用,能偶更好的節省服務器資源和帶寬,并且能夠更實時高效地進行通訊。
第四、IP應用加速IP應用加速旨在提供非標準HTTP協議用戶,特別是四層私有協議服務場景下,如金融類、游戲類、語音交互類等客戶提供網絡傳輸加速,降低服務的延遲和提升訪問的可用性。
阿里云IP應用加速可以提供:私有協議做傳輸控制、智能選路優化網絡層、源站透傳、業務透明轉發無任何侵入,可以通過IP應用加速靈活的使用TCP、UDP等相關協議做業務的傳輸。
通過以上的了解,我們可以看到通過阿里云全站加速產品,可以有效的提升網站(APP)加速性能和用戶體驗。在更多的業務搬到線上的時代,全站加速為游戲、在線教育、互聯網媒體、金融、商等行業中的數字化應用提供了更優的加速方案。如果您的業務中有全站加速的需求,可以通過阿里云官網、工單、服務群等方式進行了解和反饋。
比Python,JavaScript才是更適合寫爬蟲的語言。原因有如下三個方面:
一、任務:爬取用戶在Github上的repo信息
通過實例的方式學習爬蟲是最好的方法,先定一個小目標:爬取github repo信息。入口URL如下,我們只需要一直點擊next按鈕就能夠遍歷到用戶的所有repo。
https://github.com/{{username}}?tab=repositories
獲取repo之后,可以做什么?
二、爬蟲雙股劍:axios和jQuery
axios是JavaScript中很常用的異步網絡請求庫,相比jQuery,它更輕量、更專業。既能夠用于瀏覽器端,也可以用于Node。它的語法風格是promise形式的。在本任務中,只需要了解如下用法就足夠了:
axios.get(url).then((resp) => { 請求成功,處理resp.data中的html數據 }).catch((err) => { 請求失敗,錯誤處理 })
請求之后需要處理回復結果,處理回復結果的庫當然是用jQuery。實際上,我們有更好的選擇:cheerio。
在node下,使用jQuery,需要使用jsdom庫模擬一個window對象,這種方法效率較低,四個字形容就是:笨重穩妥。
如下代碼使用jQuery解析haha.html文件
fs = require("fs") jquery=require('jquery') jsdom=require('jsdom') //fs.readFileSync()返回結果是一個buffer,相當于byte[] html = fs.readFileSync('haha.html').toString('utf8') dom= new jsdom.JSDOM(html) $=jquery(dom.window) console.log($('h1'))
cheerio只實現了jQuery中的DOM部分,相當于jQuery的一個子集。cheerio的語法和jQuery完全一致,在使用cheerio時,幾乎感覺不到它和jQuery的差異。在解析HTML方面,毫無疑問,cheerio是更好的選擇。如下代碼使用cheerio解析haha.html文件。
cheerio=require('cheerio') html=require('fs').readFileSync("haha.html").toString('utf8') $=cheerio.load(html) console.log($('h1'))
只需20余行,便可實現簡單的github爬蟲,此爬蟲只爬取了一頁repo列表。
var axios = require("axios") var cheerio = require("cheerio") axios.get("https://github.com/weiyinfu?tab=repositories").then(resp => { var $ = cheerio.load(resp.data) var lis = $("#user-repositories-list li") var repos = [] for (var i = 0; i < lis.length; i++) { var li = lis.eq(i) var repo = { repoName: li.find("h3").text().trim(), repoUrl: li.find("h3 a").attr("href").trim(), repoDesc: li.find("p").text().trim(), language: li.find("[itemprop=programmingLanguage]").text().trim(), star: li.find(".muted-link.mr-3").eq(0).text().trim(), fork: li.find(".muted-link.mr-3").eq(1).text().trim(), forkedFrom: li.find(".f6.text-gray.mb-1 a").text().trim() } repos.push(repo) } console.log(repos) })
三、更豐富的功能
爬蟲不是目的,而是達成目的的一種手段。獲取數據也不是目的,從數據中提取統計信息并呈現給人才是最終目的。
在github爬蟲的基礎上,我們可以擴展出更加豐富的功能:使用echarts等圖表展示結果。
要想讓更多人使用此爬蟲工具獲取自己的github統計信息,就需要將做成一個網站的形式,通過搜索頁面輸入用戶名,啟動爬蟲立即爬取github信息,然后使用echarts進行統計展示。網站肯定也要用js作為后端,這樣才能和js爬蟲無縫銜接,不然還要考慮跨語言調用。js后端有兩大web框架express和koa,二者API非常相似,并無優劣之分,但express更加流行。
如上設計有一處用戶體驗不佳的地方:當啟動爬蟲爬取github信息時,用戶可能需要等待好幾秒,這個過程不能讓用戶干等著。一種解決思路是:讓用戶看到爬蟲爬取的進度或者爬取過程。可以通過websocket向用戶推送爬取過程信息并在前端進行展示。展示時,使用類似控制臺的界面進行展示。
如何存儲爬取到的數據呢?使用MongoDB或者文件都可以,最好實現兩種存儲方式,讓系統的存儲方式變得可配置。使用MongoDB時,用到js中的連接池框架generic-pool。
整個項目用到的庫包括:
試用地址:
https://weiyinfu.cn/githubstatistic/search.html?
案例地址:https://github.com/weiyinfu/GithubStatistic
原文鏈接:https://zhuanlan.zhihu.com/p/53763115
*請認真填寫需求信息,我們會在24小時內與您取得聯系。