写本文大致分为以下几个心理活动。
本想做做爬虫,然后持久化到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的,后来感觉偏离了路线。后面会在这个基础上加上这方面功能。至于这个获取百度文库资源的,只是针对这篇三年级上册数学期末试卷及答案,还没做其他灵活处理,以后会考虑更多实际情况。