摘要:導(dǎo)言對于大多數(shù)前端開發(fā)者而言,談到命令行工具,大家肯定都用過。但是談到開發(fā)命令行工具,估計就沒幾人有了解了。如何優(yōu)化這個圖片爬蟲工具目前還有點(diǎn)啊,我們的目標(biāo)是要開發(fā)一個交互式的命令行應(yīng)用,肯定不能止于此。
導(dǎo)言:對于大多數(shù)前端開發(fā)者而言,談到命令行工具,大家肯定都用過。但是談到開發(fā)命令行工具,估計就沒幾人有了解了。本文旨在用最短的時間內(nèi),幫您開發(fā)一個實(shí)用(斜眼笑)的圖片爬蟲命令行應(yīng)用。
想追求更好的閱讀體驗(yàn),請移步拓跋的前端客棧。同時把項(xiàng)目地址放在顯眼的位置
Puppeteer 簡介 什么是 Puppeteer?Puppeteer 是 Google Chrome 團(tuán)隊(duì)官方的無界面(Headless)Chrome 工具。Chrome 作為瀏覽器市場的領(lǐng)頭羊,Chrome Headless? 將成為 web 應(yīng)用 ? 自動化測試 ? 的行業(yè)標(biāo)桿。所以我們很有必要來了解一下它。
Puppeteer 可以做什么?Puppeteer 可以做的事情有很多,包括但不限于:
利用網(wǎng)頁生成 PDF、圖片
可以從網(wǎng)站抓取內(nèi)容
自動化表單提交、UI 測試、鍵盤輸入等
幫你創(chuàng)建一個最新的自動化測試環(huán)境(chrome),可以直接在此運(yùn)行測試用例
捕獲站點(diǎn)的時間線,以便追蹤你的網(wǎng)站,幫助分析網(wǎng)站性能問題
Puppeteer 有什么優(yōu)勢?相對于真實(shí)瀏覽器而言,少了加載 css,js 以及渲染頁面的工作。無頭瀏覽器要比真實(shí)瀏覽器快的多。
可以在無界面的服務(wù)器或 CI 上運(yùn)行,減少了外界的干擾,更穩(wěn)定。
在一臺機(jī)器上可以模擬運(yùn)行多個無頭瀏覽器,方便進(jìn)行并發(fā)運(yùn)行。
如何安裝 Puppeteer?安裝 ?Puppeteer? 很簡單,如下:
npm i --save puppeteer
or
yarn add puppeteer
需要注意的是,由于用到了 ES7 的 ?async/await? 語法 ,node? 版本最好是 v7.6.0 或以上。
如何使用 Puppeteer?由于本文不是專門講 Puppeteer 的文章,故這部分暫且略過,大家可以去看下面的鏈接學(xué)習(xí)。
Puppeteer Github
Puppeteer Api Doc
Puppeteer 中文 Api Doc
說了這么多,Puppeteer 與我們要開發(fā)的命令行應(yīng)用有什么關(guān)系呢?我們準(zhǔn)備制作一個抓圖命令行工具,不使用傳統(tǒng)的請求式爬蟲,我們使用 Puppeteer 這種無頭瀏覽器,從 DOM 里抓圖,這樣能有效規(guī)避部分爬蟲防御手段。
Puppeteer 簡單應(yīng)用 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(無頭)模式打開瀏覽器,然后打開一個新標(biāo)簽頁,跳轉(zhuǎn)到百度網(wǎng)址, 并且進(jìn)行屏幕截圖,保存為 baidu.png 為名的圖片,最后關(guān)閉瀏覽器。
執(zhí)行結(jié)果如下。
case 2. 抓取網(wǎng)站信息接下來學(xué)習(xí)如何用 Puppeteer 抓取網(wǎng)站信息了。
這次我們來抓取 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; // 返回數(shù)據(jù) }); 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! });
我們做的就是跳轉(zhuǎn)到關(guān)鍵字是 javascript 的頁面,然后對頁面的 dom 結(jié)構(gòu)進(jìn)行分析,找到圖書列表所對應(yīng)的書名、描述、價格、出版社、網(wǎng)頁鏈接信息,然后把數(shù)據(jù)寫入到 javascript.json 文件里去,方便我們保存瀏覽。
邏輯很簡單。這已經(jīng)是一個爬蟲的雛形了,最后得到如下圖所示的 json 文件,非常給力。
case 3. 圖片爬蟲圖片爬蟲,這就是我們要做的命令行應(yīng)用的主題了。
一個最基本的思路是這樣的:
打開瀏覽器 —> 跳轉(zhuǎn)到百度圖片 —> 獲取 input 框的焦點(diǎn) —> 輸入 keywords —> 點(diǎn)擊搜索按鈕 —> 跳轉(zhuǎn)至結(jié)果列表頁 —> 下拉到底部 —> 操作 dom,獲取所有圖片的 src 備用 —> 根據(jù) src 將對應(yīng)圖片保存到本地 —> 關(guān)閉瀏覽器
代碼實(shí)現(xiàn)之:
首先是瀏覽器操作部分
const browser = await puppeteer.launch(); // 打開瀏覽器 const page = await browser.newPage(); // 打開新tab頁 await page.goto("https://image.baidu.com"); // 跳轉(zhuǎn)到百度圖片 console.log("go to https://image.baidu.com"); // 獲取input框的焦點(diǎn) await page.focus("#kw"); // 把焦點(diǎn)定位到搜索input框 await page.keyboard.sendCharacter("貓咪"); // 輸入關(guān)鍵字 await page.click(".s_search"); // 點(diǎn)擊搜索按鈕 console.log("go to search list"); // 提示跳轉(zhuǎn)到搜索列表頁
然后是圖片處理部分
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(); });
因?yàn)榘俣葓D片是往下滾動就可以繼續(xù)懶加載。因此,我們想要加載更多圖片,可以先往下滾動一會兒。然后通過分析 dom 結(jié)構(gòu)來獲取列表里所有圖片的 src,最后進(jìn)行下載。
執(zhí)行以下,就能得到一系列貓咪的圖片:
圖片下載的地方只寫了主函數(shù),更詳細(xì)的代碼可以去參見github.
至此,我們用 Node 和 Puppeteer 開發(fā)出了一個最基本的圖片爬蟲工具。
如何優(yōu)化?這個圖片爬蟲工具目前還有點(diǎn) low 啊,我們的目標(biāo)是要開發(fā)一個交互式的命令行應(yīng)用,肯定不能止于此。有哪些可以進(jìn)一步優(yōu)化的點(diǎn)呢?經(jīng)過簡單的思考,我列了一下:
下載圖片的內(nèi)容可以自定義
可以支持用戶選擇圖片下載張數(shù)
支持命令行傳參
支持命令行交互
交互界面美觀
支持雙擊直接運(yùn)行
支持全局命令行調(diào)用
使用 commander.js 支持命令行傳參Commander 是一款重量輕,表現(xiàn)力和強(qiáng)大的命令行框架。提供了用戶命令行輸入和參數(shù)解析強(qiáng)大功能。
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十分容易上手,上面這一段代碼僅用了寥寥數(shù)行,就定義了一個命令行的輸入與輸出。其中:
version 定義版本號
description 定義描述信息
option 定義輸入選項(xiàng),傳3個參數(shù),如option("-n, --name
action 定義執(zhí)行的操作,是一個回調(diào)函數(shù),入?yún)⑹乔拔妮斎氲膐ption選項(xiàng),如果沒有輸入option,則使用定義的默認(rèn)值。
要查詢更詳細(xì)的api,請參考Commander Api文檔。
執(zhí)行一下上述腳本,可以得到:
這樣命令行就可以做到簡單的交互效果了。但是有沒有覺得不夠好看呢,別急,繼續(xù)往下看。
使用inquirer制作可交互命令行應(yīng)用inquirer可以為Node制作可嵌入式的美觀的命令行界面。
可以提供問答式的命令輸入:
可以提供多種形式的選擇界面:
可以對輸入信息進(jìn)行校驗(yàn):
最后可以對輸入信息進(jìn)行處理:
上面的例子是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!" ));
可以輸出如下信息,一看便懂:
再讓我們做點(diǎn)有意思的事情...想必有人看到過下面知乎的控制臺效果,既然要做點(diǎn)有意思的事情,今天我們不妨也把這種效果加到命令行程序里面,提升一下逼格。
首先我們準(zhǔn)備一副ASCII碼用來打印,各位可以自行搜索text轉(zhuǎn)ASCII,網(wǎng)上的轉(zhuǎn)化方案不要太多。我們準(zhǔn)備制作的命令行image spider就制作一個IMG SPD的ASCII碼字符串吧~
經(jīng)過挑選,效果如圖:
這種復(fù)雜的字符串怎么打印出來呢?直接保存為string一定是不行的,格式會亂的一塌糊涂。
想要能完整的打印出格式來,有一個取巧的方法,以注釋的形式打印出來。什么能保存注釋呢?~~ function。
所以事情就簡單到了打印一個function。但是直接打印函數(shù)還是不行的,這時候就用到了可以懟天懟地的toString()方法,我們只需要把注釋中間的部分用正則匹配出來就行了,easy~
最后看一看效果,鐺鐺鐺鐺~
支持雙擊運(yùn)行這里使用一種叫做Shebang的技術(shù)。
Shebang(也稱為?Hashbang?)是一個由#和!構(gòu)成的字符序列?#!?,其出現(xiàn)在文本文件的第一行的前兩個字符。 在文件中存在 Shebang 的情況下,類 Unix 操作系統(tǒng)的程序加載器會分析 Shebang 后的內(nèi)容,將這些內(nèi)容作為解釋器指令,并調(diào)用該指令,并將載有 Shebang 的文件路徑作為該解釋器的參數(shù)。
node下我們使用#! /usr/bin/env node即可
這時候我們便可以取消文件的擴(kuò)展名.js了。
加入環(huán)境變量,支持全局調(diào)用package.json里面配置
"bin": { "img-spd": "app" },
執(zhí)行npm link,它將會把img-spd這個字段復(fù)制到npm的全局模塊安裝文件夾node_modules內(nèi),并創(chuàng)建符號鏈接(symbolic link,軟鏈接),也就是將 app 的路徑加入環(huán)境變量 PATH。
這時,在任意目錄下,直接命令行輸入img-spd即可運(yùn)行此命令行
尾聲至此,要改進(jìn)的地方已經(jīng)全部修改完畢,快來看看我們的成品吧~
看著一整個文件夾的gakki,感覺滿滿的幸福要溢出來了
最后用動圖來展示一下:
附錄 項(xiàng)目地址項(xiàng)目地址
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
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/102380.html
摘要:自阮大神的文章發(fā)布以來,有了一些改動,添加有很多有用的功能,特別是這個功能,對打造命令行工具集合非常有用,所以寫一個新版本的教程還是有必要的。 前言 使用命令行程序?qū)Τ绦騿T來說很常見,就算是前端工程師或者開發(fā)gui的,也需要使用命令行來編譯程序或者打包程序 熟練使用命令行工具能極大的提高開發(fā)效率,linux自帶的命令行工具都非常的有用,但是這些工具都是按照通用需求開發(fā)出來的,如果有一些...
摘要:管理文件當(dāng)前用戶目錄下文件的增刪改查是配置文件是默認(rèn)的配置發(fā)布將本腳手架發(fā)布至上。 腳手架 vue-cli, create-react-app、react-native-cli 等都是非常優(yōu)秀的腳手架,通過腳手架,我們可以快速初始化一個項(xiàng)目,無需自己從零開始一步步配置,有效提升開發(fā)體驗(yàn)。盡管這些腳手架非常優(yōu)秀,但是未必是符合我們的實(shí)際應(yīng)用的,我們可以定制一個屬于自己的腳手架(或公司通用...
摘要:成為最早一批運(yùn)行的人復(fù)習(xí)一下上次文章的內(nèi)容,系統(tǒng)主要有三個應(yīng)用程序系統(tǒng)的核心進(jìn)程,也就是所謂的節(jié)點(diǎn)。建立本地單節(jié)點(diǎn)測試網(wǎng)絡(luò)構(gòu)建完成后,我們進(jìn)入目錄使用和命令,運(yùn)行節(jié)點(diǎn)程序命令中,參數(shù)表示使用了賬戶的權(quán)限,這是本地測試系統(tǒng)提供的原始賬戶。 成為最早一批運(yùn)行EOS的人 復(fù)習(xí)一下上次文章的內(nèi)容,EOS 系統(tǒng)主要有三個應(yīng)用程序: nodeos: EOS 系統(tǒng)的核心進(jìn)程,也就是所謂的節(jié)點(diǎn)。 ...
摘要:此命令下載測試鏡像并在容器中運(yùn)行它。國內(nèi)很多云服務(wù)商都提供了加速器服務(wù),例如阿里云加速器注冊用戶并且申請加速器,會獲得如這樣的地址。獲取鏡像阿里云鏡像庫上有大量的高質(zhì)量的鏡像可以用,這里我們就說一下怎么獲取這些鏡像并運(yùn)行。 showImg(https://segmentfault.com/img/remote/1460000012924583); 這篇文章是我學(xué)習(xí) Docker 的記錄...
閱讀 1773·2023-04-26 00:20
閱讀 1822·2021-11-08 13:21
閱讀 2016·2021-09-10 10:51
閱讀 1581·2021-09-10 10:50
閱讀 3312·2019-08-30 15:54
閱讀 2143·2019-08-30 14:22
閱讀 1439·2019-08-29 16:10
閱讀 3101·2019-08-26 11:50