上一篇文章《手把手教你如何用Crawlab構建技術文章聚合平臺(一)》介紹瞭如何使用搭建Crawlab的運行環境,而且將Puppeteer與Crawlab集成,對掘金、SegmentFault、CSDN進行技術文章的抓取,最後能夠查看抓取結果。本篇文章將繼續講解如何利用Flask+Vue編寫一個精簡的聚合平臺,將抓取好的文章內容展現出來。javascript
首先,咱們須要對爬蟲部分作點小小的補充。上篇文章中咱們只編寫了抓取文章URL的爬蟲,咱們還須要抓取文章內容,所以還須要將這部分爬蟲編寫了。上次爬蟲的結果collection所有更改成results
,文章的內容將以content
字段保存在數據庫中。html
經分析知道每一個技術網站的文章頁都有一個固定標籤,將該標籤下的HTML所有抓取下來就OK了。具體代碼分析就不展開了,這裏貼出具體代碼。前端
const puppeteer = require('puppeteer'); const MongoClient = require('mongodb').MongoClient; (async () => { // browser const browser = await (puppeteer.launch({ headless: true })); // page const page = await browser.newPage(); // 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'; const col = db.collection(colName); const col_src = db.collection('results'); const results = await col_src.find({content: {$exists: false}}).toArray(); for (let i = 0; i < results.length; i++) { let item = results[i]; // define article anchor let anchor; if (item.source === 'juejin') { anchor = '.article-content'; } else if (item.source === 'segmentfault') { anchor = '.article'; } else if (item.source === 'csdn') { anchor = '#content_views'; } else { continue; } console.log(`anchor: ${anchor}`); // navigate to the article try { await page.goto(item.url, {waitUntil: 'domcontentloaded'}); await page.waitFor(2000); } catch (e) { console.error(e); continue; } // scrape article content item.content = await page.$eval(anchor, el => el.innerHTML); // save to database await col.save(item); console.log(`saved item: ${JSON.stringify(item)}`) } // close mongodb client.close(); // close browser browser.close(); })();
而後將該爬蟲按照前一篇文章的步驟部署運行爬蟲,就能夠採集到詳細的文章內容了。vue
文章內容爬蟲的代碼已經更新到Github了。java
接下來,咱們能夠開始對這些文章作文章了。node
目前的技術發展來看,先後端分離已是主流:一來前端技術愈來愈複雜,要求模塊化、工程化;二來先後端分離可讓先後端團隊分工協做,更加高效地開發應用。因爲本文的聚合平臺是一個輕量級應用,後端接口編寫咱們用Python的輕量級Web應用框架Flask,前端咱們用近年來大紅大紫的上手容易的Vue。python
Flask被稱爲Micro Framework,可見其輕量級,幾行代碼即可以編寫一個Web應用。它靠Extensions插件來擴展其特定功能,例如登陸驗證、RESTful、數據模型等等。這個小節中咱們將搭建一個REST風格的後臺API應用。ios
首先安裝相關的依賴。git
pip install flask flask_restful flask_cors pymongo
安裝完成後咱們能夠新建一個app.py
文件,輸入以下代碼github
from flask import Flask from flask_cors import CORS from flask_restful import Api # 生成Flask App實例 app = Flask(__name__) # 生成API實例 api = Api(app) # 支持CORS跨域 CORS(app, supports_credentials=True) if __name__ == '__main__': app.run()
命令行中輸入python app.py
就能夠運行這個基礎的Flask應用了。
接下來,咱們須要編寫獲取文章的接口。首先咱們簡單分析一下需求。
這個Flask應用要實現的功能爲:
所以,咱們須要實現上述兩個API。下面開始編寫接口。
在app.py
中添加以下代碼,做爲列表接口。
class ListApi(Resource): def get(self): # 查詢 items = col.find({'content': {'$exists': True}}).sort('_id', DESCENDING).limit(40) data = [] for item in items: # 將pymongo object轉化爲python object _item = json.loads(json_util.dumps(item)) data.append({ '_id': _item['_id']['$oid'], 'title': _item['title'], 'source': _item['source'], 'ts': item['_id'].generation_time.strftime('%Y-%m-%d %H:%M:%S') }) return data
一樣的,在app.py
中輸入以下代碼。
class DetailApi(Resource): def get(self, id): item = col.find_one({'_id': ObjectId(id)}) # 將pymongo object轉化爲python object _item = json.loads(json_util.dumps(item)) return { '_id': _item['_id']['$oid'], 'title': _item['title'], 'source': _item['source'], 'ts': item['_id'].generation_time.strftime('%Y-%m-%d %H:%M:%S'), 'content': _item['content'] }
編寫完接口,咱們須要將它們映射到對應到URL中。
api.add_resource(ListApi, '/results') api.add_resource(DetailApi, '/results/<string:id>')
如下是完整的Flask應用代碼,很簡單,實現了文章列表和文章詳情兩個功能。接下來,咱們將開始開發前端的部分。
import json from bson import json_util, ObjectId from flask import Flask, jsonify from flask_cors import CORS from flask_restful import Api, Resource from pymongo import MongoClient, DESCENDING # 生成Flask App實例 app = Flask(__name__) # 生成MongoDB實例 mongo = MongoClient(host='192.168.99.100') db = mongo['crawlab_test'] col = db['results'] # 生成API實例 api = Api(app) # 支持CORS跨域 CORS(app, supports_credentials=True) class ListApi(Resource): def get(self): # 查詢 items = col.find({}).sort('_id', DESCENDING).limit(20) data = [] for item in items: # 將pymongo object轉化爲python object _item = json.loads(json_util.dumps(item)) data.append({ '_id': _item['_id']['$oid'], 'title': _item['title'], 'source': _item['source'], 'ts': item['_id'].generation_time.strftime('%Y-%m-%d %H:%M:%S') }) return data class DetailApi(Resource): def get(self, id): item = col.find_one({'_id': ObjectId(id)}) # 將pymongo object轉化爲python object _item = json.loads(json_util.dumps(item)) return { '_id': _item['_id']['$oid'], 'title': _item['title'], 'source': _item['source'], 'ts': item['_id'].generation_time.strftime('%Y-%m-%d %H:%M:%S'), 'content': _item['content'] } api.add_resource(ListApi, '/results') api.add_resource(DetailApi, '/results/<string:id>') if __name__ == '__main__': app.run()
運行python app.py
,將後臺接口服務器跑起來。
Vue近年來是熱得發燙,在Github上已經超越React,成爲三大開源框架(React,Vue,Angular)中star數最多的項目。相比於React和Angular,Vue很是容易上手,既能夠雙向綁定數據快速開始構建簡單應用,又能夠利用Vuex單向數據傳遞構建大型應用。這種靈活性是它受大多數開發者歡迎的緣由之一。
爲了構建一個簡單的Vue應用,咱們將用到vue-cli3,一個vue項目的腳手架。首先,咱們從npm上安裝腳手架。
yarn add @vue/cli
若是你尚未安裝yarn,執行下列命令安裝。
npm i -g yarn
接下來,咱們須要用vue-cli3構建一個項目。執行如下命令。
vue create frontend
命令行中會彈出下列選項,選擇default
。
? Please pick a preset: (Use arrow keys) ❯ default (babel, eslint) preset (vue-router, vuex, node-sass, babel, eslint, unit-jest) Manually select features
而後vue-cli3會開始準備構建項目必要的依賴以及生成項目結構。
此外,咱們還須要安裝完成其餘功能所須要的包。
yarn add axios
在views
目錄中建立一個List.vue
文件,寫入下列內容。
<template> <div class="list"> <div class="left"></div> <div class="center"> <ul class="article-list"> <li v-for="article in list" :key="article._id" class="article-item"> <a href="javascript:" @click="showArticle(article._id)" class="title"> {{article.title}} </a> <span class="time"> {{article.ts}} </span> </li> </ul> </div> <div class="right"></div> </div> </template> <script> import axios from 'axios' export default { name: 'List', data () { return { list: [] } }, methods: { showArticle (id) { this.$router.push(`/${id}`) } }, created () { axios.get('http://localhost:5000/results') .then(response => { this.list = response.data }) } } </script> <style scoped> .list { display: flex; } .left { flex-basis: 20%; } .right { flex-basis: 20%; } .article-list { text-align: left; list-style: none; } .article-item { background: #c3edfb; border-radius: 5px; padding: 5px; height: 32px; display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; } .title { flex-basis: auto; color: #58769d; } .time { font-size: 10px; text-align: right; flex-basis: 180px; } </style>
其中,引用了axios
來與API進行ajax交互,這裏獲取的是列表接口。佈局用來經典的雙聖盃佈局。methods
中的showArticle
方法接收id
參數,將頁面跳轉至詳情頁。
在views
目錄中,建立Detail.vue
文件,並輸入以下內容。
<template> <div class="detail"> <div class="left"></div> <div class="center"> <h1 class="title">{{article.title}}</h1> <div class="content" v-html="article.content"> </div> </div> <div class="right"></div> </div> </template> <script> import axios from 'axios' export default { name: 'Detail', data () { return { article: {} } }, computed: { id () { return this.$route.params.id } }, created () { axios.get(`http://localhost:5000/results/${this.id}`) .then(response => { this.article = response.data }) } } </script> <style scoped> .detail { display: flex; } .left { flex-basis: 20%; } .right { flex-basis: 20%; } .center { flex-basis: 60%; text-align: left; } .title { } </style>
這個頁面也是經典的雙聖盃佈局,中間佔40%。由API獲取的文章內容輸出到content
中,由v-html
綁定。這裏其實能夠作進一步的CSS優化,但做者太懶了,這個任務就交給讀者來實現吧。
編輯router.js
文件,將其修改成如下內容。
import Vue from 'vue' import Router from 'vue-router' import List from './views/List' import Detail from './views/Detail' Vue.use(Router) export default new Router({ mode: 'hash', base: process.env.BASE_URL, routes: [ { path: '/', name: 'List', component: List }, { path: '/:id', name: 'Detail', component: Detail } ] })
在命令行中輸入如下命令,打開http://localhost:8080
就能夠看到文章列表了。
npm run serve
最後的聚合平臺效果截屏以下,能夠看到基本的樣式已經出來了。
本文在上一篇文章《手把手教你如何用Crawlab構建技術文章聚合平臺(一)》的基礎上,介紹瞭如何利用Flask+Vue和以前抓取的文章數據,搭建一個簡易的技術文章聚合平臺。用到的技術很基礎,固然,確定也還有不少須要優化和提高的空間,這個就留給讀者和各位大佬吧。
Github
若是感受Crawlab還不錯的話,請加做者微信拉入開發交流羣,你們一塊兒交流關於Crawlab的使用和開發。