摘要:本文將介紹如何使用和抓取主流的技術博客文章,然后用搭建一個小型的技術文章聚合平臺。是谷歌開源的基于和的自動化測試工具,可以很方便的讓程序模擬用戶的操作,對瀏覽器進行程序化控制。相對于,是新的開源項目,而且是谷歌開發,可以使用很多新的特性。
背景
說到爬蟲,大多數程序員想到的是scrapy這樣受人歡迎的框架。scrapy的確不錯,而且有很強大的生態圈,有gerapy等優秀的可視化界面。但是,它還是有一些不能做到的事情,例如在頁面上做翻頁點擊操作、移動端抓取等等。對于這些新的需求,可以用Selenium、Puppeteer、Appium這些自動化測試框架繞開繁瑣的動態內容,直接模擬用戶操作進行抓取。可惜的是,這些框架不是專門的爬蟲框架,不能對爬蟲進行集中管理,因此對于一個多達數十個爬蟲的大型項目來說有些棘手。
Crawlab是一個基于Celery的分布式通用爬蟲管理平臺,擅長將不同編程語言編寫的爬蟲整合在一處,方便監控和管理。Crawlab有精美的可視化界面,能對多個爬蟲進行運行和管理。任務調度引擎是本身支持分布式架構的Celery,因此Crawlab可以天然集成分布式爬蟲。有一些朋友認為Crawlab只是一個任務調度引擎,其實這樣認為并不完全正確。Crawlab是類似Gerapy這樣的專注于爬蟲的管理平臺。
本文將介紹如何使用Crawlab和Puppeteer抓取主流的技術博客文章,然后用Flask+Vue搭建一個小型的技術文章聚合平臺。
Crawlab在前一篇文章《分布式通用爬蟲管理平臺Crawlab》已介紹了Crawlab的架構以及安裝使用,這里快速介紹一下如何安裝、運行、使用Crawlab。
安裝到Crawlab的Github Repo用克隆一份到本地。
git clone https://github.com/tikazyq/crawlab
安裝相應的依賴包和庫。
cd crawlab # 安裝python依賴 pip install -r crawlab/requirements # 安裝前端依賴 cd frontend npm install
安裝mongodb和redis-server。Crawlab將用MongoDB作為結果集以及運行操作的儲存方式,Redis作為Celery的任務隊列,因此需要安裝這兩個數據庫。
運行在運行之前需要對Crawlab進行一些配置,配置文件為config.py。
# project variables PROJECT_SOURCE_FILE_FOLDER = "/Users/yeqing/projects/crawlab/spiders" # 爬蟲源碼根目錄 PROJECT_DEPLOY_FILE_FOLDER = "/var/crawlab" # 爬蟲部署根目錄 PROJECT_LOGS_FOLDER = "/var/logs/crawlab" # 日志目錄 PROJECT_TMP_FOLDER = "/tmp" # 臨時文件目錄 # celery variables BROKER_URL = "redis://192.168.99.100:6379/0" # 中間者URL,連接redis CELERY_RESULT_BACKEND = "mongodb://192.168.99.100:27017/" # CELERY后臺URL CELERY_MONGODB_BACKEND_SETTINGS = { "database": "crawlab_test", "taskmeta_collection": "tasks_celery", } CELERY_TIMEZONE = "Asia/Shanghai" CELERY_ENABLE_UTC = True # flower variables FLOWER_API_ENDPOINT = "http://localhost:5555/api" # Flower服務地址 # database variables MONGO_HOST = "192.168.99.100" MONGO_PORT = 27017 MONGO_DB = "crawlab_test" # flask variables DEBUG = True FLASK_HOST = "127.0.0.1" FLASK_PORT = 8000
啟動后端API,也就是一個Flask App,可以直接啟動,或者用gunicorn代替。
cd ../crawlab python app.py
啟動Flower服務(抱歉目前集成Flower到App服務中,必須多帶帶啟動來獲取節點信息,后面的版本不需要這個操作)。
python ./bin/run_flower.py
啟動本地Worker。在其他節點中如果想只是想執行任務的話,只需要啟動這一個服務就可以了。
python ./bin/run_worker.py
啟動前端服務器。
cd ../frontend npm run serve使用
首頁Home中可以看到總任務數、總爬蟲數、在線節點數和總部署數,以及過去30天的任務運行數量。
點擊側邊欄的Spiders或者上方到Spiders數,可以進入到爬蟲列表頁。
這些是爬蟲源碼根目錄PROJECT_SOURCE_FILE_FOLDER下的爬蟲。Crawlab會自動掃描該目錄下的子目錄,將子目錄看作一個爬蟲。Action列下有一些操作選項,點擊部署Deploy按鈕將爬蟲部署到所有在線節點中。部署成功后,點擊運行Run按鈕,觸發抓取任務。這時,任務應該已經在執行了。點擊側邊欄的Tasks到任務列表,可以看到已經調度過的爬蟲任務。
基本使用就是這些,但是Crawlab還能做到更多,大家可以進一步探索,詳情請見Github。
PuppeteerPuppeteer是谷歌開源的基于Chromium和NodeJS的自動化測試工具,可以很方便的讓程序模擬用戶的操作,對瀏覽器進行程序化控制。Puppeteer有一些常用操作,例如點擊,鼠標移動,滑動,截屏,下載文件等等。另外,Puppeteer很類似Selenium,可以定位瀏覽器中網頁元素,將其數據抓取下來。因此,Puppeteer也成為了新的爬蟲利器。
相對于Selenium,Puppeteer是新的開源項目,而且是谷歌開發,可以使用很多新的特性。對于爬蟲來說,如果前端知識足夠的話,寫數據抓取邏輯簡直不能再簡單。正如其名字一樣,我們是在操作木偶人來幫我們抓取數據,是不是很貼切?
掘金上已經有很多關于Puppeteer的教程了(爬蟲利器 Puppeteer 實戰、Puppeteer 與 Chrome Headless —— 從入門到爬蟲),這里只簡單介紹一下Puppeteer的安裝和使用。
安裝安裝很簡單,就一行npm install命令,npm會自動下載Chromium并安裝,這個時間會比較長。為了讓安裝好的puppeteer模塊能夠被所有nodejs爬蟲所共享,我們在PROJECT_DEPLOY_FILE_FOLDER目錄下安裝node的包。
# PROJECT_DEPLOY_FILE_FOLDER變量值 cd /var/crawlab # 安裝puppeteer npm i puppeteer # 安裝mongodb npm i mongodb
安裝mongodb是為了后續的數據庫操作。
使用以下是Copy/Paste的一段用Puppeteer訪問簡書然后截屏的代碼,非常簡潔。
const puppeteer = require("puppeteer"); (async () => { const browser = await (puppeteer.launch()); const page = await browser.newPage(); await page.goto("https://www.jianshu.com/u/40909ea33e50"); await page.screenshot({ path: "jianshu.png", type: "png", // quality: 100, 只對jpg有效 fullPage: true, // 指定區域截圖,clip和fullPage兩者只能設置一個 // clip: { // x: 0, // y: 0, // width: 1000, // height: 40 // } }); browser.close(); })();
關于Puppeteer的常用操作,請移步《我常用的puppeteer爬蟲api》。
編寫爬蟲啰嗦了這么久,終于到了萬眾期待的爬蟲時間了。Talk is cheap, show me the code!咦?我們不是已經Show了不少代碼了么...
由于我們的目標是建立一個技術文章聚合平臺,我們需要去各大技術網站抓取文章。資源當然是越多越好。作為展示用,我們將抓取下面幾個具有代表性的網站:
掘金
SegmentFault
CSDN
研究發現這三個網站都是由Ajax獲取文章列表,生成動態內容以作為傳統的分頁替代。這對于Puppeteer來說很容易處理,因為Puppeteer繞開了解析Ajax這一部分,瀏覽器會自動處理這樣的操作和請求,我們只著重關注數據獲取就行了。三個網站的抓取策略基本相同,我們以掘金為例著重講解。
掘金首先是引入Puppeteer和打開網頁。
const puppeteer = require("puppeteer"); const MongoClient = require("mongodb").MongoClient; (async () => { // browser const browser = await (puppeteer.launch({ headless: true })); // define start url const url = "https://juejin.im"; // start a new page const page = await browser.newPage(); ... })();
headless設置為true可以讓瀏覽器以headless的方式運行,也就是指瀏覽器不用在界面中打開,它會在后臺運行,用戶是看不到瀏覽器的。browser.newPage()將新生成一個標簽頁。后面的操作基本就圍繞著生成的page來進行。
接下來我們讓瀏覽器導航到start url。
... // navigate to url try { await page.goto(url, {waitUntil: "domcontentloaded"}); await page.waitFor(2000); } catch (e) { console.error(e); // close browser browser.close(); // exit code 1 indicating an error happened code = 1; process.emit("exit "); process.reallyExit(code); return } ...
這里try catch的操作是為了處理瀏覽器訪問超時的錯誤。當訪問超時時,設置exit code為1表示該任務失敗了,這樣Crawlab會將該任務狀態設置為FAILURE。
然后我們需要下拉頁面讓瀏覽器可以讀取下一頁。
... // scroll down to fetch more data for (let i = 0; i < 100; i++) { console.log("Pressing PageDown..."); await page.keyboard.press("PageDown", 200); await page.waitFor(100); } ...
翻頁完畢后,就開始抓取數據了。
... // scrape data const results = await page.evaluate(() => { let results = []; document.querySelectorAll(".entry-list > .item").forEach(el => { if (!el.querySelector(".title")) return; results.push({ url: "https://juejin.com" + el.querySelector(".title").getAttribute("href"), title: el.querySelector(".title").innerText }); }); return results; }); ...
page.evaluate可以在瀏覽器Console中進行JS操作。這段代碼其實可以直接在瀏覽器Console中直接運行。調試起來是不是方便到爽?前端工程師們,開始歡呼吧!
獲取了數據,接下來我們需要將其儲存在數據庫中。
... // open database connection const client = await MongoClient.connect("mongodb://192.168.99.100:27017"); let db = await client.db("crawlab_test"); const colName = process.env.CRAWLAB_COLLECTION || "results_juejin"; const taskId = process.env.CRAWLAB_TASK_ID; const col = db.collection(colName); // save to database for (let i = 0; i < results.length; i++) { // de-duplication const r = await col.findOne({url: results[i]}); if (r) continue; // assign taskID results[i].task_id = taskId; // insert row await col.insertOne(results[i]); } ...
這樣,我們就將掘金最新的文章數據保存在了數據庫中。其中,我們用url字段做了去重處理。CRAWLAB_COLLECTION和CRAWLAB_TASK_ID是Crawlab傳過來的環境變量,分別是儲存的collection和任務ID。任務ID需要以task_id為鍵保存起來,這樣在Crawlab中就可以將數據與任務關聯起來了。
整個爬蟲代碼如下。
const puppeteer = require("puppeteer"); const MongoClient = require("mongodb").MongoClient; (async () => { // browser const browser = await (puppeteer.launch({ headless: true })); // define start url const url = "https://juejin.im"; // start a new page const page = await browser.newPage(); // navigate to url try { await page.goto(url, {waitUntil: "domcontentloaded"}); await page.waitFor(2000); } catch (e) { console.error(e); // close browser browser.close(); // exit code 1 indicating an error happened code = 1; process.emit("exit "); process.reallyExit(code); return } // scroll down to fetch more data for (let i = 0; i < 100; i++) { console.log("Pressing PageDown..."); await page.keyboard.press("PageDown", 200); await page.waitFor(100); } // scrape data const results = await page.evaluate(() => { let results = []; document.querySelectorAll(".entry-list > .item").forEach(el => { if (!el.querySelector(".title")) return; results.push({ url: "https://juejin.com" + el.querySelector(".title").getAttribute("href"), title: el.querySelector(".title").innerText }); }); return results; }); // open database connection const client = await MongoClient.connect("mongodb://192.168.99.100:27017"); let db = await client.db("crawlab_test"); const colName = process.env.CRAWLAB_COLLECTION || "results_juejin"; const taskId = process.env.CRAWLAB_TASK_ID; const col = db.collection(colName); // save to database for (let i = 0; i < results.length; i++) { // de-duplication const r = await col.findOne({url: results[i]}); if (r) continue; // assign taskID results[i].task_id = taskId; // insert row await col.insertOne(results[i]); } console.log(`results.length: ${results.length}`); // close database connection client.close(); // shutdown browser browser.close(); })();SegmentFault & CSDN
這兩個網站的爬蟲代碼基本與上面的爬蟲一樣,只是一些參數不一樣而已。我們的爬蟲項目結構如下。
運行爬蟲在Crawlab中打開Spiders,我們可以看到剛剛編寫好的爬蟲。
點擊各個爬蟲的View查看按鈕,進入到爬蟲詳情。
在Execute Command中輸入爬蟲執行命令。對掘金爬蟲來說,是node juejin_spider.js。輸入完畢后點擊Save保存。然后點擊Deploy部署爬蟲。最后點擊Run運行爬蟲。
點擊左上角到刷新按鈕可以看到剛剛運行的爬蟲任務已經在運行了。點擊Create Time后可以進入到任務詳情。Overview標簽中可以看到任務信息,Log標簽可以看到日志信息,Results信息中可以看到抓取結果。目前在Crawlab結果列表中還不支持數據導出,但是不久的版本中肯定會將導出功能加入進來。
總結在這一小節,我們已經能夠將Crawlab運行起來,并且能用Puppeteer編寫抓取三大網站技術文章的爬蟲,并且能夠用Crawlab運行爬蟲,并且讀取抓取后的數據。下一節,我們將用Flask+Vue做一個簡單的技術文章聚合網站。能看到這里的都是有耐心的好同學,贊一個。
Github: tikazyq/crawlab
如果感覺Crawlab還不錯的話,請加作者微信拉入開發交流群,大家一起交流關于Crawlab的使用和開發。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/43387.html
摘要:上一篇文章手把手教你如何用構建技術文章聚合平臺一介紹了如何使用搭建的運行環境,并且將與集成,對掘金進行技術文章的抓取,最后可以查看抓取結果。本篇文章將繼續講解如何利用編寫一個精簡的聚合平臺,將抓取好的文章內容展示出來。 上一篇文章《手把手教你如何用Crawlab構建技術文章聚合平臺(一)》介紹了如何使用搭建Crawlab的運行環境,并且將Puppeteer與Crawlab集成,對掘金、...
摘要:本文將介紹如何使用和抓取主流的技術博客文章,然后用搭建一個小型的技術文章聚合平臺。是谷歌開源的基于和的自動化測試工具,可以很方便的讓程序模擬用戶的操作,對瀏覽器進行程序化控制。相對于,是新的開源項目,而且是谷歌開發,可以使用很多新的特性。 背景 說到爬蟲,大多數程序員想到的是scrapy這樣受人歡迎的框架。scrapy的確不錯,而且有很強大的生態圈,有gerapy等優秀的可視化界面。但...
摘要:是一個專注于爬蟲的集成了爬蟲管理任務調度任務監控數據分析等模塊的分布式爬蟲管理平臺,非常適合對爬蟲管理爬蟲工程化有要求的開發者及企業。從目前開源的框架來看,大部分爬蟲平臺是以為核心,因此只能支持框架的爬蟲,而不僅支持,還支持其他框架的爬蟲。 showImg(https://segmentfault.com/img/remote/1460000019143107?w=2559&h=112...
摘要:前言開發爬蟲是一件有趣的事情。的可配置爬蟲是基于的,因此天生是支持并發的。遵守協議這個默認是開啟的。的可配置爬蟲降低了爬蟲的開發時間,增加了爬蟲開發效率,完善了工程化水平,將爬蟲工程師從日常的繁瑣配置工作中解放出來。 前言 開發爬蟲是一件有趣的事情。寫一個程序,對感興趣的目標網站發起HTTP請求,獲取HTML,解析HTML,提取數據,將數據保存到數據庫或者存為CSV、JSON等格式,再...
摘要:前言開發爬蟲是一件有趣的事情。的可配置爬蟲是基于的,因此天生是支持并發的。的可配置爬蟲降低了爬蟲的開發時間,增加了爬蟲開發效率,完善了工程化水平,將爬蟲工程師從日常的繁瑣配置工作中解放出來。前言 開發爬蟲是一件有趣的事情。寫一個程序,對感興趣的目標網站發起HTTP請求,獲取HTML,解析HTML,提取數據,將數據保存到數據庫或者存為CSV、JSON等格式,再用自己熟悉的語言例如Python對...
閱讀 657·2021-11-24 09:39
閱讀 3029·2021-11-23 10:06
閱讀 989·2021-10-08 10:05
閱讀 760·2019-08-30 10:49
閱讀 1732·2019-08-29 14:08
閱讀 1331·2019-08-29 12:48
閱讀 3326·2019-08-26 14:04
閱讀 3622·2019-08-26 13:50