上边我们学习了node一些基础知识,相信对node有了一定的认知。下面继续学习node知识
九、使用nodejs做一个简易的爬虫
-
思路:1、获取目标网站
2、分析网站内容
3、获取有效信息,下载,存储操作
1、使用http模块获取网站信息,由于内存空间有限,因此数据的存储通过分批次的从内容中存入硬盘,所以有了数据流的概念。
let http=require('http'); let fs=require('fs'); const url='http://www.baidu.com'; http.get(url,(res)=>{ let rawData=''; // 数据分段,只有接收到数据就会触发data,chunk为每次的数据片段 res.on('data',(chunk)=>{ rawData+=chunk.toString('utf8'); console.log('--------', '数据流'); }) // 数据流传输完毕 res.on('end',()=>{ // 数据传输完后,将其保存在当前目录下的baidu.html文件里。 fs.writeFileSync('./baidu.html', rawData); console.log('数据流结束'); }) }).on('error', (err)=>{ console.log(err); console.log('请求错误') })
- 上述内容没有安全验证
let http=require('http'); let fs=require('fs'); const url='http://www.baidu.com'; http.get(url,(res)=>{ // 安全判断 const {statusCode}=res; // 状态码 const contentType=res.headers['content-type']; // 文件类型 let err=null; if(statusCode!==200){ err=new Error("请求状态失败"); }else if(!/^text\/html/.test(contentType)){ err=new Error("文件类型不是网页") } if(err){ res.resume(); //重置缓存 return; } // 数据分段,只有接收到数据就会触发data,chunk为每次的数据片段 let rawData=''; res.on('data',(chunk)=>{ rawData+=chunk.toString('utf8'); console.log('--------', '数据流'); }) // 数据流传输完毕 res.on('end',()=>{ fs.writeFileSync('./baidu.html', rawData); }) }).on('error', (err)=>{ console.log(err); console.log('请求错误') })
2、爬虫完善,需要对爬下来的内容进行正则匹配,匹配我们需要的东西,此时,自己写一个正则表达式比较的麻烦,因此,使用cheerio插件来完成。
npm install cheerio --save
用法举例:
let cheerio=require('cheerio'); const $=cheerio.load('<div><img src="http://wwww.baidu.com/img/1.jpg"/></div>'); console.log($('img').attr('src')); // 输出结果为:http://wwww.baidu.com/img/1.jpg
有多个img标签,以及获取标签中汉字举例:
let cheerio = require("cheerio"); const $ = cheerio.load( '<div><img src="http://wwww.baidu.com/img/1.jpg"/><p>我是p元素</p><img src="http://wwww.baidu.com/img/2.jpg"/></div>' ); $("img").each((index,el)=>{ console.log($(el).attr("src")); }) console.log($("p").text()); // 输出结果为: http://wwww.baidu.com/img/1.jpg http://wwww.baidu.com/img/2.jpg 我是p元素
最后附上爬取图片保存到本地代码:
let http=require('http'); let fs=require('fs'); let path=require('path') let request = require('request'); let cheerio = require("cheerio"); const url='http://www.hello.com/'; fs.readdir('./',(err,data)=>{ let isNo=false; for(let i=0;i<data.length;i++){ if(data[i]==='images'){ isNo=true; } } if(!isNo){ fs.mkdirSync('./images', (err)=>{ if(err){ console.log('创建文件夹失败'); }else{ console.log('创建文件夹成功'); } }); } }); http.get(url,(res)=>{ // 安全判断 const {statusCode}=res; // 状态码 const contentType=res.headers['content-type']; // 文件类型 let err=null; if(statusCode!==200){ err=new Error("请求状态失败"); }else if(!/^text\/html/.test(contentType)){ err=new Error("文件类型不是网页") } if(err){ res.resume(); //重置缓存 console.log(err); return; } // 数据分段,只有接收到数据就会触发data,chunk为每次的数据片段 let rawData=''; res.on('data',(chunk)=>{ rawData+=chunk.toString('utf8'); console.log('--------', '数据流'); }) // 数据流传输完毕 res.on('end',()=>{ const $=cheerio.load(rawData); $('img').each((index,el)=>{ request($(el).attr("src")).pipe(fs.createWriteStream(path.join(__dirname,'images','images'+index+'.jpg'))); }) }) }).on('error', (err)=>{ console.log(err); console.log('请求错误') })
十、使用express快速搭建node服务器
1、 模块的引用(三方外置模块)从当前目录开始找,找不到往上级目录开始找,最后依然找不到,报错找不到。
2、 使用步骤:
- 引入express模块
- 实例化express
- 监听
3、windwos获取ip地址用ipconfig命令,linux用ifconfig
4、服务器相关:
- 服务器是一台电脑
- 服务器软件(apach, iis, tomcat, node, ngnix)注:apach用于php的,ngnix用于linux系统的
- 服务器ip和端口号
外网:ip地址对应对应的主机,port对应某个程序
5、api接口的组成要素:
- ip 地址
- port 端口号
- pathname 路径
- 请求方式
- 接收用户的数据,数据格式由后端制定
- 返回值
6、使用get方式获取提交的数据,使用req.query来获取该提交的数据,post方式获取的数据,使用req.body来获取post提交的数据。
7、express不能解析消息体,因此使用post方式提交数据时候,需要使用第三方插件来进行解析body消息体。插件名字叫做bodyparser插件
8、使用postman来测试post接口
const express = require('express');
const bodyParser = require('body-parser')
const app = express() // express实例化
// parse application/x-www-form-urlencoded 处理表单格式post提交数据
app.use(bodyParser.urlencoded({ extended: false }))
// parse application/json 处理以json格式提交的数据,处理后req.body能取到值
app.use(bodyParser.json())
// post请求方式
app.post('/user/work', (req, res)=>{
const {name} = req.body;
if(name==="领哥"){
res.send({success: true, worker: '领哥'});
}
})
// get请求方式
app.get('/user/login', (req, res)=>{
if(req.query.name==='hello'&&req.query.ps==='123'){
res.send({success: "登录成功"});
}else{
res.send({err: "登录失败"});
}
})
app.listen(3000, ()=>{
console.log('服务在3000端口运行')
})
9、 express路由,将数据分层处理,不用将所有内容都写在一个文件里,这样将不同功能内容分开处理。
例如:项目中有两个模块,分别是食物模块和用户模块,使用router将他们在两个文件中书写,便于区分。
// 主入口
const express = require('express');
const app = express() // express实例化
const userRouter=require('./router/user.js')
const foodRouter=require('./router/food.js')
app.use('/user', userRouter);
app.use('/food', foodRouter);
app.listen(3000, ()=>{
console.log('服务在3000端口运行')
})
// 和主入口文件同级的router文件夹下的user.js文件
const express=require('express');
const router=express.Router();
router.get('/name', (req, res)=>{
res.send({name: 'ling'});
})
router.get('/age', (req, res)=>{
res.send({age: 22});
})
router.get('/login', (req, res)=>{
res.send({success: true});
})
module.exports=router;
// 和主入口文件同级的router文件夹下的food.js文件
const express= require('express');
const router = express.Router();
router.get('/del', (res, req)=>{
req.send('删除食物')
})
router.get('/add', (res, req)=>{
req.send('添加食物')
})
module.exports=router
在浏览器中访问 localhost:3000/user/name得到{name: ‘ling’}
10、middlewear中间件,也称作是拦截器。
-
内置中间件 static-静态资源目录
-
自定义中间件 (全局中间件,局部中间件)
- 全局中间件声明格式
app.use(pathname, (req,res,next)=>{})
- 局部中间件格式
app.get(pathname,function1,function2) // 其中function1要有三个参数,function2有两个参数 app.get(pathname,(req,res,next)=>{},(req,res)=>{})
-
第三方中间件 例如:body-parse
-
如果中间件作用于根目录可以简写为:
app.use((req,res,next)=>{})
// 其中body-parse在解析json格式时候使用方式是app.use(bodyParser.json()),因此是中间件
使用场景用于重复性的内容操作,比如token验证,每一个模块都验证一下挺麻烦,使用middlewear验证一次,所有的都使用,大大简化操作。可以自定义一个中间件,用法如下(全局的):
const express = require('express');
const app = express(); // express实例化
app.use('/', (req, res, next)=>{
console.log('中间件');
const {token}=req.query;
if(token){
next()
}else{
res.send('缺少token');
}
});
app.get('/user', (req, res)=>{
res.send('请求user');
});
app.listen(3000, ()=>{
console.log('服务在3000端口运行')
})
// 访问localhost:3000/user?token=1235得到 “请求user”
有两个参数,第一个是路径,什么路径下需要拦截,比如所有的都要拦截,使用 ‘/’ 因为所有的接口都要走 ‘/’ 路径。第二个为回调函数,此回调函数有三个参数,最后一个为next,用法和vue路由守卫类似。
使用局部中间件实现:
const express = require('express');
const app = express(); // express实例化
app.get('/user', (req, res, next)=>{
console.log('function1')
next()
},
(req, res)=>{
console.log('function2')
res.send('放行');
});
app.listen(3000, ()=>{
console.log('服务在3000端口运行')
})
// 在浏览器输入localhost:3000/user 返回 "放行" 并在控制台有输出function1, function2
-
静态资源目录static,用于声明服务器默认访问的路径,类似apache中的www
const express = require('express'); const path = require('path'); const app = express(); // express实例化 app.use(express.static(path.join(__dirname, './hello'))); console.log(path.join(__dirname, './hello')) app.listen(3000, ()=>{ console.log('服务在3000端口运行') })
此时访问域名:3000将直接访问指定的静态资源目录下的index.html文件,同样的,可以使用中间件指定路径
const express = require('express'); const path = require('path'); const app = express(); // express实例化 app.use('public', express.static(path.join(__dirname, './hello'))); console.log(path.join(__dirname, './hello')) app.listen(3000, ()=>{ console.log('服务在3000端口运行') }) // 此时访问域名:3000/public将会访问指定静态路径下的index.html文件。
10、mongodb非关系型数据库相关
- 首先,安装mongodb数据库,启动cmd之后输入mongod使用命令行启动数据库命令。然后不要关闭此cmd,新开一个cmd,输入mongo回车,如果报c/data/db找不到,需要去对应哪个文件下新建文件夹,然后,就可以操作数据了,show dbs查看数据库。
- mongoose是node操作mongodb的插件