目录:
HTTP(get)
方法 http.get(url[, options][, callback])
- 参数:
url < string> | < URL>
options < Object> ,接受与 http.request() 相同的 options,且 method 始终设置为 GET。从原型继承的属性将被忽略。
callback < Function> - 这个方法与 http.request() 的唯一区别是它将方法设置为 GET 并自动调用 req.end()。
- callback 调用时只有一个参数,该参数是 http.IncomingMessage 的实例。
IncomingMessage 对象由 http.Server 或 http.ClientRequest 创建,并分别作为第一个参数传给 ‘request’ 和 ‘response’ 事件。 它可用于访问响应状态、消息头、以及数据。
const http=require('http');
//之前自己新建的jobslist.json,用json-server开启数据模拟服务,会生成这么一个地址
let str='http://localhost:3333/jobslist';
// 或着自己添加参数,生成完整路径
// const url=require('url');//先引入url内置模块
// var params={
// protocol:'http',
// host:'localhost:3333',
// pathname:'/jobslist'
// }
// let str=url.format(params);//这样str="http://localhost:3333/jobslist"
http.get(str,(response)=>{ //由于大多数请求都是没有主体的 GET 请求,因此 Node.js 提供了这个便捷的方法。
// console.log(response);//看下面截图
let {statusCode}=response;//解构
// console.log("statusCode:"+statusCode);//statusCode:200
var error;
// statusCode状态码,在浏览器的显示看下面截图
if(statusCode!=200){
error=new Error('数据异常');
}
// 验证返回的数据 内容的编码,决定浏览器用什么的形式,来读取这个文件
let contentType=response.headers["content-type"];//浏览器里面是Content-Type,这俩不算一样
// console.log("contentType:"+contentType);//contentType:application/json; charset=utf-8
// console.log("查看contentType内容是否正确:"+/application\/json/.test(contentType));//查看contentType内容是否正确:true
if(!/application\/json/.test(contentType)){
error=new Error('数据内容不正确');
}
if(error){
console.log(error.message);//把存在error里的信息打印出来,用Error的message属性
response.resume();//释放内存 消费响应数据来释放内存。
return;
}
// 如果没有异常,那就是能够正常执行
var rawdata='';
var num=0;
// 数据搜集
response.on('data',function(chunk){
// data事件 管道流方式
// 数据流返回的是数据块 多次响应
// data 如果数据大 会多次执行 持续的过程
rawdata+=chunk;
// console.log(chunk);
})
// 数据完成
response.on('end',function(){
// 做解析
// 考虑代码的健壮性 加上异常处理,避免程序直接崩掉
try{
// console.log(JSON.parse(rawdata));//将一个 JSON 字符串转换为对象
}catch (err){
console.log(err);
}
})
// error事件
}).on('error',function(err){
console.log(err,err.message);
})
console.log(response),response是callback 调用时的参数(http.IncomingMessage 的实例),然后把statusCode解构拿出来了 let {statusCode}=response;
F12或ctrl+shift+i,点Network—>all,能够查看状态码、文本类型等信息
console.log(chunk) 多个数据块接收拼接,最终形成完整的数据
http常用 statusCode 状态码:
1**:通知 100~200
2**:成功 200~300 200成功 288 端口号被占用
3**:重定向 300~400 302移动 304缓存
4**:客户端错误 404资源不存在
5**:服务器错误
拉钩代理例子(request)
通过nodejs的http模块实现简单的代理功能。
比如说我们打开拉勾网的网站,现在我们想要自己本地设置一个端口打开,那么怎么去做这个代理呢?
拉勾网原网址:https://m.lagou.com/listmore.json?pageNo=2&pageSize=15
现在我们用这个网址打开:http://localhost:8099/listmore.json?pageNo=2&pageSize=15
1、先用原网址打开:把这串数据的url拿过来,直接地址栏输入,就能得到这串数据。
2、然后现在我们尝试用本地端口打开。
const https=require('https');
const http=require('http');
const baseUrl="https://m.lagou.com";
http.createServer((req,res)=>{
let url=req.url;
console.log("req.url:",req.url);//用户请求路径,如果不写默认为 "/"
res.setHeader('Content-Type','application/json;charset:utf-8;');// 如果乱码 字符集有问题 content-type出了问题 那就重设一下
res.setHeader('Access-Control-Allow-Origin','*');// 允许跨域,不过一般不设置为*,一般设置为例如 *.baidu.com,baidu.com是一级域名,*是二级域名
https.get(baseUrl+url,function(res1){
var rowdata='';
res1.on('data',function(chunk){ // data 数据流事件
rowdata+=chunk;//做数据的拼接
})
res1.on('end',function(){
res.write(rowdata);
res.end();
})
})
}).listen(8099,function(){
// 8099 端口号
console.log('lcoalhost 8099 start');
})
开启服务:
如果不写具体的请求参数,那么,request.url会默认为 “/”(服务端控制台查看)
现在完整路径打开,能够看到现在 req.url: /listmore.json?pageNo=2&pageSize=15;
另外,显示的这串数据其实就是服务端响应的数据res.write(rowdata)
小知识点:一级域名、二级域名
比如百度 m.baidu.com 、 fe.baidu.com, 如果配置 * .baidu.com 那么前面两个网址就能跨域访问。
baidu.com 一级域名(顶级域名) m fe * 这些位置的都是二级域名
大师兄:Noodjs 代理的实现:手动封装代理与http-proxy-middleware插件实现代理
拉钩代理例子(http-proxy-middleware)
安装:
- npm init -y 先生成package.json文件
- yarn add http-proxy-middleware@0.21.0 注意不要安装过新的版本,可能不稳定
现在我想配置一个代理,通过匹配 “/fetch” 来走这个代理,
http://localhost:8099/fetch/listmore.json?pageNo=2&pageSize=15 ,同样能访问原站点访问的数据;
原站点:https://m.lagou.com/listmore.json?pageNo=2&pageSize=15
const https = require('https');
const http = require('http');
const proxy = require('http-proxy-middleware');//使用第三方模块 http-proxy-middleware完成代理
http.createServer((req, res) => {
//console.log(req.url);// "/fetch/listmore.json?pageNo=2&pageSize=15"
if (/\/fetch/.test(req.url)) {//正则匹配url;如果请求中有"/fetch",就走这个代理
var params = {
target: 'https://m.lagou.com',//需要代理服务的站点url
changeOrigin: true,//基于名称的虚拟托管网站的选项, 设置为true, 本地就会虚拟一个服务器接收你的请求并代你发送该请求,
pathRewrite: {
'^/fetch': ''//在真实的请求中,不加"/fetch",所以替换成空""
}
}
var demoProxy = proxy(params);//返回一个高级函数
return demoProxy(req, res);//把服务的req和res传进代理模块,响应代理结果给客户端
} else {
res.write('hello world')
res.end();
}
}).listen(8099, function () {
console.log('localhost 8099 start ...')
})
能够看到用代理的站点也能访问。
http.request 请求发送、接收
- 两种形式:
http.request(options[, callback])
http.request(url[, options][, callback]) - 参数:
url < string> | < URL>
options < Object>
callback < Function>
(注意注意:咱们下来的两个例子,客户端与服务端都得开着,分两个终端来开启。)
一、post
client.js 客户端
const http=require('http');
const qs=require('querystring');
// 客户端向服务端发送数据
// 假如有这么一个用户信息
var user={name:'sky',age:20}
// 比如发送数据 到 server url://http://localhost:3000
var app=http.request({
protocol:'http:',
hostname:'localhost',
port:3000,
method:'post'
},(res)=>{
// 接收响应数据
var rawdata='';
res.on('data',(chunk)=>{
rawdata+=chunk;
})
res.on('end',(chunk)=>{
console.log(rawdata);//服务端响应的数据
})
})
// 数据发送,先转化成JSON字符串再发送
app.write(qs.stringify(user));
app.end();
server.js 服务端
const http=require('http');
// 接收数据
var serverApp=http.createServer((request,response)=>{
var rawdata='';//预备拼接数据
// 接收请求数据,订阅事件
request.on('data',(chunk)=>{
rawdata+=chunk;
})
request.on('end',()=>{
console.log(rawdata);//打印接收的全部数据
response.write('user ok');//回发响应消息
response.end();
})
});
serverApp.listen(3000,(err)=>{
if(!err){
console.log("端口3000 服务启动正常");
}
})
服务正常开启,服务端打印接收到的数据,响应结果给客户端:
客户端收到服务器的响应:
二、get
通俗来讲,与post最大的不同是url直接携带请求参数。
client.js 客户端
const http = require('http')
const qs = require('querystring');
var user = { name: 'skyget', age: 18 }
//server url:http://localhost:3000
var app = http.request({
protocol: 'http:',
hostname: 'localhost',
port: 3000,
path: '/user?' + qs.stringify(user),//直接携带参数
method: 'GET'
}, (res) => {
// 接收服务端的响应数据
var rawdata = '';
res.on('data', (chunk) => {
rawdata += chunk;
})
res.on('end', (chunk) => {
console.log(rawdata);
})
})
app.end();
server.js 服务端
const http = require('http');
const url = require('url');
// 接收数据
var serverApp = http.createServer((request, response) => {
let strUrl = request.url;//请求url
console.log(request.url);
let query = url.parse(strUrl, true).query;//解析参数,?后面的
console.log(query.name, query.age)
response.write('ok:' + query.name);
response.end();
})
serverApp.listen(3000, (err) => {
if (!err) {
console.log('localhost 3000 start...')
}
})
开启服务器,接收数据,回复响应:
客户端接收响应信息:
小知识点:中间层的概念,node作为中间层,比如node把Java的多个接口整合成一个接口。
HTTP爬虫 (Request)
先说下原则,能爬的爬,不能爬的不爬,反爬的绝对不能爬。
然后,下面用nodejs来实现网页内容的抓取,也就是简单的爬虫。
比如我们爬 https://www.microsoftstore.com.cn/ 这个网站的一些开元内容。
用到一个插件cheerio,是jquery核心功能简洁实现,主要是用在服务器端需要对DOM进行操作的地方。
安装:yarn add cheerio (可以先生成package.json文件,npm init -y 以便查看安装信息)
// 爬虫
const https=require('https');
const $ =require('cheerio');//引入cheerio模块
function filterData(data){
// console.log("data:",data);//data是链接页面的html代码
var items=$(data).find('.listContainerInner li');//类似选择器,找到要爬数据的唯一标识
// console.log("items.length:",items.length);//5
var result=[];//最有希望被爬出的数据以数组返回
$(items).each((index,item)=>{
// console.log("item:",item);
// console.log("$(item):",$(item));//比item多了个"0",放在第一位
// console.log($(item).find('.price strong').text());//能够把价格信息都拿到
let $item=$(item);//不重新命名也行
var param={
url:$item.find('img').attr('data-src'),
name:$(item).find('.name h4').text(),
price:$item.find('.price strong').text(),
}
result.push(param);//把每一条拿出来数据以对象的形式每次放入数组
})
console.log(result);
// return result;//以数组返回
}
var app=https.request({
protocol:'https:',
hostname:'www.microsoftstore.com.cn',
port:443,//https的默认端口
method:'GET'
},(res)=>{
var rawdata='';
res.on('data',(chunk)=>{
rawdata+=chunk;
})
res.on('end',()=>{
// console.log(rawdata);//rawdata是链接页面的html代码
filterData(rawdata);
})
})
app.end();
我们想要把这五条数据的一些信息拿到手:
然后信息就拿到手啦:
登录、退出 小例子
// /api/login
// /api/logout
const http = require('http');
const qs = require('querystring');
http.createServer((req, res) => {
var url = req.url;//用户请求路径
var rawdata = '';
console.log('method:',req.method);//查看请求方式是get还是post
res.setHeader('Content-Type', 'application/json;charset:utf-8;');// 如果遇到字符乱码的问题,就设置文本类型
req.on('data', (chunk) => {
rawdata += chunk;
})
req.on('end', () => {
// console.log(qs.parse(rawdata));//就相当于JSON.parse()
console.log("url:",url);//判断是否登录或退出 /api/login
console.log("rawdata:",rawdata);//客户端发过来的参数
switch (url) {
case '/api/login':
let username = qs.parse(rawdata).username;//相当于JSON.parse(),尝试使用不同方法
console.log(username);
var result = {
code: 1,
message: '登陆成功',
username
}
res.end(JSON.stringify(result));//res.write()与res.end()的缩写版
break;
case '/api/logout':
var result = {
code: 2,
message: '退出成功',
}
res.write(JSON.stringify(result));//跟JSON.stringify()功能一样
res.end();
break;
}
})
}).listen(3000, () => { // listen是绑定端口,监听端口
console.log('正常启动 localhost 3000...');
})
介绍一款插件insomnia,用于前后端分离,可以模拟浏览器发送各种方式的请求。
登录login,客户端向服务端发送请求:
服务端:
退出logout,客户端退出登录:
服务端: