爬百度文庫有償資料順便學習mongoose

寫本文大體分爲如下幾個心理活動。

本想作作爬蟲,而後持久化到mongodDb。後來,有需求要下載百度文庫的資料,又沒有下載券,因而想一想怎樣可以免費下載資料,順便保存下來。因此就有了獲取百度文庫的資料而順便學習mongoose。html

按心理活動排序本文敘述分爲如下幾點。node


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==git

  • 解決方法github

  • 我改爲這樣mongodb

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有兩個特色

    1. 經過關係型數據庫的思想來設計非關係型數據庫
    1. 基於mongodb驅動,簡化操做

Mongooose三個重要概念:

Schema: 至關於一個數據庫的模板,Schema不具有操做數據庫的能力。數據庫

Model: 由Schema編譯而成的構造器,具備抽象屬性和行爲,能夠對數據庫進行增刪查改。npm

Entity: 真實的數據。canvas

Schema 生成 ModelModel 創造 DocumentModelDocument均可對數據庫操做形成影響。bash

簡單demoapp

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"

參考:

mongoose基礎入門

深刻淺出mongoose


保存百度文庫資料爲圖片

(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時圖片變空白
  • 改爲保存爲圖片,部分圖片空白

發現 滾動操做的時候會 從新請求圖片資源,因此dom節點上面只會存在部分圖片。

看了看每張圖片的外層都有一個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的,後來感受偏離了路線。後面會在這個基礎上加上這方面功能。至於這個獲取百度文庫資源的,只是針對這篇三年級上冊數學期末試卷及答案,還沒作其餘靈活處理,之後會考慮更多實際狀況。

相關文章
相關標籤/搜索