導言:對於大多數前端開發者而言,談到命令行工具,你們確定都用過。可是談到開發命令行工具,估計就沒幾人有了解了。本文旨在用最短的時間內,幫您開發一個實用(斜眼笑)的圖片爬蟲命令行應用。
想追求更好的閱讀體驗,請移步拓跋的前端客棧。同時把項目地址放在顯眼的位置javascript
Puppeteer 是 Google Chrome 團隊官方的無界面(Headless)Chrome 工具。Chrome 做爲瀏覽器市場的領頭羊,Chrome Headless 將成爲 web 應用 自動化測試 的行業標杆。因此咱們頗有必要來了解一下它。css
Puppeteer 能夠作的事情有不少,包括但不限於:前端
安裝 Puppeteer 很簡單,以下:npm i --save puppeteer
oryarn add puppeteer
java
須要注意的是,因爲用到了 ES7 的 async/await 語法 ,node 版本最好是 v7.6.0 或以上。node
因爲本文不是專門講 Puppeteer 的文章,故這部分暫且略過,你們能夠去看下面的連接學習。git
Puppeteer Githubgithub
Puppeteer 中文 Api Docchrome
說了這麼多,Puppeteer 與咱們要開發的命令行應用有什麼關係呢?咱們準備製做一個抓圖命令行工具,不使用傳統的請求式爬蟲,咱們使用 Puppeteer 這種無頭瀏覽器,從 DOM 裏抓圖,這樣能有效規避部分爬蟲防護手段。shell
直接上代碼,很好理解:
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 爲名的圖片,最後關閉瀏覽器。
執行結果以下。
接下來學習如何用 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 文件,很是給力。
圖片爬蟲,這就是咱們要作的命令行應用的主題了。
一個最基本的思路是這樣的:
打開瀏覽器 —> 跳轉到百度圖片 —> 獲取 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 是一款重量輕,表現力和強大的命令行框架。提供了用戶命令行輸入和參數解析強大功能。
const program = require("commander"); program .version("0.0.1") .description("a test cli program") .option("-n, --name <name>", "your name", "zhl") .option("-a, --age <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十分容易上手,上面這一段代碼僅用了寥寥數行,就定義了一個命令行的輸入與輸出。其中:
option("-n, --name <name>", "your name", "GK")
,第一項是傳參的值,-n是簡寫形式,--name是全稱形式, <name>表示輸入的參數,<>是必填項,若是是[],則是選填項。第二項「your name"是求助help時的提示信息,告訴用戶應該輸入的內容,最後一項"GK"是默認值。要查詢更詳細的api,請參考Commander Api文檔。
執行一下上述腳本,能夠獲得:
這樣命令行就能夠作到簡單的交互效果了。可是有沒有以爲不夠好看呢,別急,繼續往下看。
inquirer能夠爲Node製做可嵌入式的美觀的命令行界面。
能夠提供問答式的命令輸入:
能夠提供多種形式的選擇界面:
能夠對輸入信息進行校驗:
最後能夠對輸入信息進行處理:
上面的例子是inquirer的官方例子,能夠參考pizza.js
inquirer的文檔能夠查看inquirer documents
有了inquirer,咱們就能夠製做更爲精美的交互式命令行工具了。
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,感受滿滿的幸福要溢出來了
最後用動圖來展現一下:
npm install -g img-spd
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