寫本文大致分為以下幾個心理活動。
本想做做爬蟲,然後持久化到mongodDb。後來,有需求要下載百度文庫的資料,又沒有下載券,於是想想怎樣能夠免費下載資料,順便儲存下來。所以就有了獲取百度文庫的資料而順便學習mongoose。
按心理活動排序本文敘述分為以下幾點。
-
1. mongoDb安裝
-
2. mongoose簡介
-
3. 儲存百度文庫資料為圖片
-
4. 結語
mongoDb安裝
1.安裝
sudo brew install mongodb
複製程式碼
2. 建立一個資料庫儲存目錄 /data/db:
sudo mkdir -p /data/db
複製程式碼
3.啟動Mongodb
sudo mongod
複製程式碼
4.新開視窗,進入mongodb命令列模式
mongo
複製程式碼
連線mongodb
- 在根目錄下安裝mongodb資料驅動庫
cd ~ && cnpm i mongodb
複製程式碼
- 新建一個連線檔案connect.js
var MongoClient = require('mongodb').MongoClient;
// 連線資料庫
var url_test = 'mongodb://localhost:27017/test'; //資料庫test本不存在,連線時會自動建立
var insertData = function(db){
// 往test資料庫裡新建一個site集合,並插入一條資料
db.collection('site').insertOne({name: 'guojc', age: 99, hobby: 'movie'}, function(err, result){
console.log('inserted successly');
console.log(result);
db.close();
console.log('close');
});
}
MongoClient.connect(url_test, function(err, db) {
console.log('Connected successly to server.');
insertData(db);
});
複製程式碼
-
node connect.js,發現連線成功,但是插入資料包錯 ==db.collection is not a function==
-
我改成這樣
var MongoClient = require('mongodb').MongoClient;
// 連線資料庫
var url = 'mongodb://localhost:27017';
var insertData = function(client){
// 往test資料庫裡新建一個site集合,並插入一條資料
client.db('test').collection('site').insertOne({name: 'guojc', age: 99, hobby: 'movie'}, function(err, result){
console.log('inserted successly');
console.log(result);
client.close();
console.log('close');
});
}
MongoClient.connect(url, function(err, client) {
console.log('Connected successly to server.');
insertData(client);
});
複製程式碼
- show dbs 能夠看到建立的資料庫
- use test 選擇建立愛的資料庫
- show tables 顯示錶
- db.site.find() 查詢該表所有資料
Mongoose簡介
Mongoose是在node.js非同步環境下對mongodb進行便捷操作的物件模型工具。本文將詳細介紹如何使用Mongoose來操作MongoDB。
Mongoose是NodeJS的驅動,不能作為其他語言的驅動。Mongoose有兩個特點
-
- 通過關係型資料庫的思想來設計非關係型資料庫
-
- 基於mongodb驅動,簡化操作
Mongooose三個重要概念:
Schema: 相當於一個資料庫的模板,Schema不具備運算元據庫的能力。
Model: 由Schema編譯而成的構造器,具有抽象屬性和行為,可以對資料庫進行增刪查改。
Entity: 真實的資料。
Schema 生成 Model ,Model 創造 Document,Model和Document都可對資料庫操作造成影響。
簡單demo
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/test');
const con = mongoose.connection;
con.on('error', console.error.bind(console, '連線資料庫失敗'));
con.once('open',()=>{
//定義一個schema
let Schema = mongoose.Schema({
name:String,
age:Number
});
// 自定義方法
Schema.methods.getAge = function(){
console.log("I am "+this.age + "years old");
}
//繼承一個schema
let Model = mongoose.model("student",Schema);
//生成一個document
let student = new Model({
name:'hanmeimei',
age:16
});
//存放資料
student.save((err,res)=>{
if(err) return console.log(err);
res.getAge();
//查詢資料
Model.find({name:'hanmeimei'},(err,data)=>{
console.log(data);
})
});
})
複製程式碼
輸出
I am 16years old
[ { _id: 5ab1cad40b0132e9a9e6c65b,
name: 'hanmeimei',
age: 16,
__v: 0 } ]
複製程式碼
檢視資料庫,發現多了一個students的table,Mongoose會將集合名稱設定為模型名稱的小寫版。如果名稱的最後一個字元是字母,則會變成複數;如果名稱的最後一個字元是數字,則不變;如果模型名稱為"MyModel",則集合名稱為"mymodels";如果模型名稱為"Model1",則集合名稱為"model1"
參考:
儲存百度文庫資料為圖片
- 需求:要獲取百度文庫資料,下面拿這份三年級上冊數學期末試卷及答案作為例子。
(PS) 前提是百度文庫能看到內容,只是下載需要下載券。
一看到這個需求第一反應就是
- 用爬蟲
- 開啟百度文
- 然後爬取需要的資料儲存到本地
然而開啟文庫看了看裡面的內容是多張 圖片 來的, 而且有 載入更多按鈕 emmmm...
那就用 puppeteer吧,之前也用過,於是思路分為以下幾點
- 開啟連結
- 點選全屏檢視(感覺省了一堆功夫)
- 點選載入更多
- 去掉頁面上的多餘的dom節點
- 儲存為pdf/圖片
直接上程式碼
1. 開啟連結
await page.goto(url);
複製程式碼
2. 點選全屏
page.click('a[data-toolsbar-log=fullscreen]')
複製程式碼
3. 點選載入更多
page.click('.moreBtn')
複製程式碼
4. 去掉頁面上的多餘的dom節點
await page.evaluate(v => {
// dom操作
})
複製程式碼
5. 儲存為pdf
page.pdf({path: 'page.pdf'});
or
page.screenshot({
path: '1.png',
fullPage:true
});
複製程式碼
問題來了
- 儲存為pdf時圖片變空白
- 改成儲存為圖片,部分圖片空白
看了看每張圖片的外層都有一個pageNo-x的ID,
根據這個為切入點的話,就改良了上面步驟。
為
- 開啟連結(同上)
- 儲存已經載入的圖片
- 點選載入更多
- 儲存載入的並且id值不等於之前幾個的圖片
- 下拉
- 儲存剩餘的圖片
- 將最後合成的圖片儲存為圖片(資料只需要列印出來,所以儲存為圖片也可以)
- 優化(去掉圖片背景的廣告)
部分程式碼
// 找圖片,並用一個新節點存起來
async function collectPng(index) {
const res = await page.evaluate(v => {
const div = document.getElementById('collection') || document.createElement('div')
div.id = 'collection'
document.getElementsByTagName('body')[0].appendChild(div)
const item = document.getElementById('pageNo-'+v)
const rpi = item?item.getElementsByClassName('reader-pic-item')[0]: null
rpi&&(rpi.style.position = 'relative')
rpi&&div.appendChild(rpi)
return {index:v, exist:!!rpi}
},index)
return res
}
// 根據返回值,判斷是否繼續查詢還是下拉頁面
async function collecting(index) {
const res = await collectPng(index)
if(res.exist) {
index+=1
await collecting(index)
} else {
if(!hasLoadMore){
console.log('載入更多')
hasLoadMore = true
await loadMore()
await collecting(index)
} else if(index<9){
console.log('下拉')
await pressDown()
await collecting(index)
} else {
}
}
}
// 生成純圖片組合成的dom
async function createDom(){
await page.evaluate(v => {
const div = document.getElementById('collection');
const body = document.createElement('body');
body.appendChild(div)
document.getElementsByTagName('body')[0].remove()
document.getElementsByTagName('html')[0].appendChild(body)
})
}
//pressDown
async function pressDown() {
await page.keyboard.press('ArrowDown',{delay: 2500});
await timeout(1000);
}
複製程式碼
實際執行情況
- 開始,儲存前三張圖片
- 發現沒有了,載入更多
- 發現沒有了,下拉
- 知道真的沒有了就儲存圖片
最後優化圖片背景
由於圖片背景會有這些教育機構,為了列印出來更加清晰,可以嘗試去掉背景
自己的思路是
- 將儲存的圖片用canvas畫出來,然後對比畫素點rgb的值均大於100的話就變成白色,再儲存成圖片。
- 當然,你可以用PS摳 :)
結語
如果你能看到這裡,謝謝。文章、程式碼寫的較為之粗糙,只是將自己的想法用程式碼實現,本文初衷是爬蟲並持久化到mongoDb的,後來感覺偏離了路線。後面會在這個基礎上加上這方面功能。至於這個獲取百度文庫資源的,只是針對這篇三年級上冊數學期末試卷及答案,還沒做其他靈活處理,以後會考慮更多實際情況。