摘要:導言對于大多數前端開發者而言,談到命令行工具,大家肯定都用過。但是談到開發命令行工具,估計就沒幾人有了解了。如何優化這個圖片爬蟲工具目前還有點啊,我們的目標是要開發一個交互式的命令行應用,肯定不能止于此。
導言:對于大多數前端開發者而言,談到命令行工具,大家肯定都用過。但是談到開發命令行工具,估計就沒幾人有了解了。本文旨在用最短的時間內,幫您開發一個實用(斜眼笑)的圖片爬蟲命令行應用。
想追求更好的閱讀體驗,請移步拓跋的前端客棧。同時把項目地址放在顯眼的位置
Puppeteer 簡介 什么是 Puppeteer?Puppeteer 是 Google Chrome 團隊官方的無界面(Headless)Chrome 工具。Chrome 作為瀏覽器市場的領頭羊,Chrome Headless? 將成為 web 應用 ? 自動化測試 ? 的行業標桿。所以我們很有必要來了解一下它。
Puppeteer 可以做什么?Puppeteer 可以做的事情有很多,包括但不限于:
利用網頁生成 PDF、圖片
可以從網站抓取內容
自動化表單提交、UI 測試、鍵盤輸入等
幫你創建一個最新的自動化測試環境(chrome),可以直接在此運行測試用例
捕獲站點的時間線,以便追蹤你的網站,幫助分析網站性能問題
Puppeteer 有什么優勢?相對于真實瀏覽器而言,少了加載 css,js 以及渲染頁面的工作。無頭瀏覽器要比真實瀏覽器快的多。
可以在無界面的服務器或 CI 上運行,減少了外界的干擾,更穩定。
在一臺機器上可以模擬運行多個無頭瀏覽器,方便進行并發運行。
如何安裝 Puppeteer?安裝 ?Puppeteer? 很簡單,如下:
npm i --save puppeteer
or
yarn add puppeteer
需要注意的是,由于用到了 ES7 的 ?async/await? 語法 ,node? 版本最好是 v7.6.0 或以上。
如何使用 Puppeteer?由于本文不是專門講 Puppeteer 的文章,故這部分暫且略過,大家可以去看下面的鏈接學習。
Puppeteer Github
Puppeteer Api Doc
Puppeteer 中文 Api Doc
說了這么多,Puppeteer 與我們要開發的命令行應用有什么關系呢?我們準備制作一個抓圖命令行工具,不使用傳統的請求式爬蟲,我們使用 Puppeteer 這種無頭瀏覽器,從 DOM 里抓圖,這樣能有效規避部分爬蟲防御手段。
Puppeteer 簡單應用 case 1. 屏幕截圖直接上代碼,很好理解:
const puppeteer = require("puppeteer"); const getScreenShot = async () => { const browser = await puppeteer.launch({ headless: false }); const page = await browser.newPage(); await page.goto("https://baidu.com"); await page.screenshot({ path: "baidu.png" }); await browser.close(); }; getScreenShot();
這段代碼的意思就是以 headless(無頭)模式打開瀏覽器,然后打開一個新標簽頁,跳轉到百度網址, 并且進行屏幕截圖,保存為 baidu.png 為名的圖片,最后關閉瀏覽器。
執行結果如下。
case 2. 抓取網站信息接下來學習如何用 Puppeteer 抓取網站信息了。
這次我們來抓取 jd 書單信息。
// book info spider const puppeteer = require("puppeteer"); const fs = require("fs"); const spider = async () => { const browser = await puppeteer.launch(); const page = await browser.newPage(); await page.goto("https://search.jd.com/Search?keyword=javascript"); const result = await page.evaluate(() => { let elements = document.querySelectorAll(".gl-item"); const data = [...elements].map(i => { return { name: i.querySelector(".p-name em").innerText, description: i.querySelector(".p-name i").innerText, price: i.querySelector(".p-price").innerText, shop: i.querySelector(".p-shopnum").innerText, url: i.querySelector(".p-img a").href }; }); return data; // 返回數據 }); browser.close(); return result; }; spider().then(value => { fs.writeFile(`${__dirname}/javascript.json`, JSON.stringify(value), err => { if (err) { throw err; } console.log("file saved!"); }); console.log(value); // Success! });
我們做的就是跳轉到關鍵字是 javascript 的頁面,然后對頁面的 dom 結構進行分析,找到圖書列表所對應的書名、描述、價格、出版社、網頁鏈接信息,然后把數據寫入到 javascript.json 文件里去,方便我們保存瀏覽。
邏輯很簡單。這已經是一個爬蟲的雛形了,最后得到如下圖所示的 json 文件,非常給力。
case 3. 圖片爬蟲圖片爬蟲,這就是我們要做的命令行應用的主題了。
一個最基本的思路是這樣的:
打開瀏覽器 —> 跳轉到百度圖片 —> 獲取 input 框的焦點 —> 輸入 keywords —> 點擊搜索按鈕 —> 跳轉至結果列表頁 —> 下拉到底部 —> 操作 dom,獲取所有圖片的 src 備用 —> 根據 src 將對應圖片保存到本地 —> 關閉瀏覽器
代碼實現之:
首先是瀏覽器操作部分
const browser = await puppeteer.launch(); // 打開瀏覽器 const page = await browser.newPage(); // 打開新tab頁 await page.goto("https://image.baidu.com"); // 跳轉到百度圖片 console.log("go to https://image.baidu.com"); // 獲取input框的焦點 await page.focus("#kw"); // 把焦點定位到搜索input框 await page.keyboard.sendCharacter("貓咪"); // 輸入關鍵字 await page.click(".s_search"); // 點擊搜索按鈕 console.log("go to search list"); // 提示跳轉到搜索列表頁
然后是圖片處理部分
page.on("load", async () => { await autoScroll(page); // 向下滾動加載圖片 console.log("page loading done, start fetch..."); const srcs = await page.evaluate(() => { const images = document.querySelectorAll("img.main_img"); return Array.prototype.map.call(images, img => img.src); }); // 獲取所有img的src console.log(`get ${srcs.length} images, start download`); for (let i = 0; i < srcs.length; i++) { await convert2Img(srcs[i], target); console.log(`finished ${i + 1}/${srcs.length} images`); } // 保存圖片 console.log(`job finished!`); await browser.close(); });
因為百度圖片是往下滾動就可以繼續懶加載。因此,我們想要加載更多圖片,可以先往下滾動一會兒。然后通過分析 dom 結構來獲取列表里所有圖片的 src,最后進行下載。
執行以下,就能得到一系列貓咪的圖片:
圖片下載的地方只寫了主函數,更詳細的代碼可以去參見github.
至此,我們用 Node 和 Puppeteer 開發出了一個最基本的圖片爬蟲工具。
如何優化?這個圖片爬蟲工具目前還有點 low 啊,我們的目標是要開發一個交互式的命令行應用,肯定不能止于此。有哪些可以進一步優化的點呢?經過簡單的思考,我列了一下:
下載圖片的內容可以自定義
可以支持用戶選擇圖片下載張數
支持命令行傳參
支持命令行交互
交互界面美觀
支持雙擊直接運行
支持全局命令行調用
使用 commander.js 支持命令行傳參Commander 是一款重量輕,表現力和強大的命令行框架。提供了用戶命令行輸入和參數解析強大功能。
const program = require("commander"); program .version("0.0.1") .description("a test cli program") .option("-n, --name", "your name", "zhl") .option("-a, --age ", "your age", "22") .option("-e, --enjoy [enjoy]") .action(option => { console.log("name: ", option.name); console.log("age: ", option.age); console.log("enjoy: ", option.enjoy); }); program.parse(process.argv);
Commander十分容易上手,上面這一段代碼僅用了寥寥數行,就定義了一個命令行的輸入與輸出。其中:
version 定義版本號
description 定義描述信息
option 定義輸入選項,傳3個參數,如option("-n, --name
action 定義執行的操作,是一個回調函數,入參是前文輸入的option選項,如果沒有輸入option,則使用定義的默認值。
要查詢更詳細的api,請參考Commander Api文檔。
執行一下上述腳本,可以得到:
這樣命令行就可以做到簡單的交互效果了。但是有沒有覺得不夠好看呢,別急,繼續往下看。
使用inquirer制作可交互命令行應用inquirer可以為Node制作可嵌入式的美觀的命令行界面。
可以提供問答式的命令輸入:
可以提供多種形式的選擇界面:
可以對輸入信息進行校驗:
最后可以對輸入信息進行處理:
上面的例子是inquirer的官方例子,可以參考pizza.js
inquirer的文檔可以查看inquirer documents
有了inquirer,我們就可以制作更為精美的交互式命令行工具了。
使用 chalk.js來讓交互界面更美觀chalk的語法非常簡單:
const chalk = require("chalk"); const log = console.log; // Combine styled and normal strings log(chalk.blue("Hello") + " World" + chalk.red("!")); // Compose multiple styles using the chainable API log(chalk.blue.bgRed.bold("Hello world!")); // Pass in multiple arguments log(chalk.blue("Hello", "World!", "Foo", "bar", "biz", "baz")); // Nest styles log(chalk.red("Hello", chalk.underline.bgBlue("world") + "!")); // Nest styles of the same type even (color, underline, background) log(chalk.green( ??"I am a green line " + ??chalk.blue.underline.bold("with a blue substring") + ??" that becomes green again!" ));
可以輸出如下信息,一看便懂:
再讓我們做點有意思的事情...想必有人看到過下面知乎的控制臺效果,既然要做點有意思的事情,今天我們不妨也把這種效果加到命令行程序里面,提升一下逼格。
首先我們準備一副ASCII碼用來打印,各位可以自行搜索text轉ASCII,網上的轉化方案不要太多。我們準備制作的命令行image spider就制作一個IMG SPD的ASCII碼字符串吧~
經過挑選,效果如圖:
這種復雜的字符串怎么打印出來呢?直接保存為string一定是不行的,格式會亂的一塌糊涂。
想要能完整的打印出格式來,有一個取巧的方法,以注釋的形式打印出來。什么能保存注釋呢?~~ function。
所以事情就簡單到了打印一個function。但是直接打印函數還是不行的,這時候就用到了可以懟天懟地的toString()方法,我們只需要把注釋中間的部分用正則匹配出來就行了,easy~
最后看一看效果,鐺鐺鐺鐺~
支持雙擊運行這里使用一種叫做Shebang的技術。
Shebang(也稱為?Hashbang?)是一個由#和!構成的字符序列?#!?,其出現在文本文件的第一行的前兩個字符。 在文件中存在 Shebang 的情況下,類 Unix 操作系統的程序加載器會分析 Shebang 后的內容,將這些內容作為解釋器指令,并調用該指令,并將載有 Shebang 的文件路徑作為該解釋器的參數。
node下我們使用#! /usr/bin/env node即可
這時候我們便可以取消文件的擴展名.js了。
加入環境變量,支持全局調用package.json里面配置
"bin": { "img-spd": "app" },
執行npm link,它將會把img-spd這個字段復制到npm的全局模塊安裝文件夾node_modules內,并創建符號鏈接(symbolic link,軟鏈接),也就是將 app 的路徑加入環境變量 PATH。
這時,在任意目錄下,直接命令行輸入img-spd即可運行此命令行
尾聲至此,要改進的地方已經全部修改完畢,快來看看我們的成品吧~
看著一整個文件夾的gakki,感覺滿滿的幸福要溢出來了
最后用動圖來展示一下:
附錄 項目地址項目地址
Installnpm install -g img-spdUsage
img-spd
or
Usage: img-spd [options] img-spd is a spider get images from image.baidu.com Options: -v --version output the version number -k, --key [key] input the image keywords to download -i, --interval [interval] input the operation interval(ms,default 200) -n, --number [number] input the operation interval(ms,default 200) -m, --headless [headless] choose whether the program is running in headless mode -h, --help output usage information
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/102380.html
摘要:自阮大神的文章發布以來,有了一些改動,添加有很多有用的功能,特別是這個功能,對打造命令行工具集合非常有用,所以寫一個新版本的教程還是有必要的。 前言 使用命令行程序對程序員來說很常見,就算是前端工程師或者開發gui的,也需要使用命令行來編譯程序或者打包程序 熟練使用命令行工具能極大的提高開發效率,linux自帶的命令行工具都非常的有用,但是這些工具都是按照通用需求開發出來的,如果有一些...
摘要:管理文件當前用戶目錄下文件的增刪改查是配置文件是默認的配置發布將本腳手架發布至上。 腳手架 vue-cli, create-react-app、react-native-cli 等都是非常優秀的腳手架,通過腳手架,我們可以快速初始化一個項目,無需自己從零開始一步步配置,有效提升開發體驗。盡管這些腳手架非常優秀,但是未必是符合我們的實際應用的,我們可以定制一個屬于自己的腳手架(或公司通用...
摘要:成為最早一批運行的人復習一下上次文章的內容,系統主要有三個應用程序系統的核心進程,也就是所謂的節點。建立本地單節點測試網絡構建完成后,我們進入目錄使用和命令,運行節點程序命令中,參數表示使用了賬戶的權限,這是本地測試系統提供的原始賬戶。 成為最早一批運行EOS的人 復習一下上次文章的內容,EOS 系統主要有三個應用程序: nodeos: EOS 系統的核心進程,也就是所謂的節點。 ...
摘要:此命令下載測試鏡像并在容器中運行它。國內很多云服務商都提供了加速器服務,例如阿里云加速器注冊用戶并且申請加速器,會獲得如這樣的地址。獲取鏡像阿里云鏡像庫上有大量的高質量的鏡像可以用,這里我們就說一下怎么獲取這些鏡像并運行。 showImg(https://segmentfault.com/img/remote/1460000012924583); 這篇文章是我學習 Docker 的記錄...
閱讀 1767·2023-04-26 00:20
閱讀 1811·2021-11-08 13:21
閱讀 1939·2021-09-10 10:51
閱讀 1572·2021-09-10 10:50
閱讀 3254·2019-08-30 15:54
閱讀 2138·2019-08-30 14:22
閱讀 1434·2019-08-29 16:10
閱讀 3097·2019-08-26 11:50