上边我们学习了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('');
console.log($('img').attr('src'));
// 输出结果为:http://wwww.baidu.com/img/1.jpg
有多个img标签,以及获取标签中汉字举例:
let cheerio = require("cheerio");
const $ = cheerio.load(
'我是p元素
'
);
$("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、 使用步骤:
3、windwos获取ip地址用ipconfig命令,linux用ifconfig
4、服务器相关:
外网:ip地址对应对应的主机,port对应某个程序
5、api接口的组成要素:
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非关系型数据库相关