移动技术

nodejs(2)-如何使用node来向其他服务器发送请求

2017-11-15  本文已影响0人  浪流儿

##### URL模块

这个模块可以帮助我们解析url地址,从里面提取很多有用的内容供我们使用;

假设这是一个url地址http://localhost:8080/a/b/c?a=1&b=2#abc,里面包含的部分:

```

protocol: 'http:',

host: 'localhost:8080',

port: '8080',

hostname: 'localhost',

hash: '#abc',

search: '?a=1&b=2',

query: 'a=1&b=2',

pathname: '/a/b/c',

path: '/a/b/c?a=1&b=2',

href: 'http://localhost:8080/a/b/c?a=1&b=2#abc'

```

url.parse(urlString[, parseQueryString[, slashesDenoteHost]])

会返回一个解析后的对象,第一个参数为要解析的url地址,第二个参数为是否将query字符串解析成对象格式,第二个参数来控制在没有协议的情况下,是否解析域名等内容

url.format(urlObject)

将一个url解析后的对象还原成一个url地址

url.resolve(from, to)

可以将我们两段url解析成一个url地址

```

console.log(url.resolve('http://www.baidu.com','/api/index.html'))

//'http://www.baidu.com/api/index.html'

```

---

### queryString

可以将我们的queryString字符串(a=1&b=2&c=3)解析或反编译

querystring.stringify(obj[, sep[, eq[, options]]]):

可以将一个对象(键值对)解析成一个querystring,第二个参数可以设置分割符号(&),第三个参数确定键值对之间的链接符号(=)

querystring.parse(str[, sep[, eq[, options]]])

可以将一个qs字符串解析成一个对象,后面的参数是按照某种规则去解析

querystring.escape(str),querystring.unescape(str)

可以将我们的中文解析成百分号编码

```

console.log(qs.escape('北京'))//%E5%8C%97%E4%BA%AC

console.log(qs.unescape('%E5%8C%97%E4%BA%AC'))//北京

```

---

#### http小爬虫

##### 首先我们先研究一下什么是SEO

Search Engine Optimization 搜索引擎优化,是一种技术,目的是提高网页在搜索引擎的排行

如何能提高排行:

1. 给百度花钱

2. 找专业的优化团队

3. 开发过程中注意优化,例如,在不影响页面结构的情况下多使用语义化标签!img的title等也需要设置,title标签必须有,通过meta标签设置description、keywords、author;不需要使用ajax获取的数据就不要获取了,尽量可以使用服务端渲染数据的方式

---

利用http.get方法去获取到某网址的html文件内容,然后利用cheerio工具进行关键内容的提取

1. 获取到 https://www.lagou.com这个接口的数据(其实就是拉钩的首页文件)

因为http、https模块可以向其他的服务器发送请求,依靠的是request方法或者其子方法:get、post;

```

let target = 'https://www.lagou.com'

const https = require('https')

//发送一个get请求,res是一个返回的对象,里面并没有直接包含数据,需要使用data事件来接收返回的数据

//且每次返回的只是一个片段,所以会触发多次,因为可能请求到的数据量特大,如果一次性返回的话可能导致服务器崩溃,所以Node采取事件的方式多次返回

https.get(target, function(res) {

let result = ''

res.on("data",(chunk)=>{

result += chunk

})

res.on("end",()=>{//当接收完后触发

//在这里就可以处理数据result

})

})

```

2. 爬取其上面的一级、二级标题的内容

因为获取到的是一个大的html格式的字符串,所以想要爬取其中某些标签的内容不能直接使用jq,用正则匹配又有些繁琐,所以使用cheerio工具,这个工具可以在服务端将html格式的字符串进行编译后返回一个$对象,这样就可以像使用JQ一样去操作了

```

const cheerio = require("cheerio")

let spideContent = (result,callback)=>{

let $ = cheerio.load(result)

let menus = []

$(".menu_main").each(function (i) {

let title = $(this).find('h2').text().trim()

let navs = Array.prototype.slice.call($(this).find('a')).map((item)=>{

return $(item).text()

})

menus.push({title,navs})

})

callback(menus)//通过回调函数来处理解析后的数据

}

```

3.  再把爬取出的内容存到一个文件里

```

spideContent(result,(menus)=>{

fs.writeFileSync('./lagou.json',JSON.stringify(menus))

})

```

##### JS小知识扩展

如何将伪数组转换成真正的数组?

1. 常见的伪数组都有哪些?arguments、通过document.getElements..获取到的内容;

2. 伪数组有什么特点,具有length属性,也是一个一个的元素组成的,但是构造器不是Array,不能使用数组的方法

3. 转换为真正数组的方法:

通过遍历将伪数组里元素放入到一个新的数组里

```

let arg = arguments//这就是一个典型的伪数组

let arr = []

for (var i = 0; i < arg.length; i++) {

arr.push(arg[i])

}

console.log(arr)

```

通过call改变数组slice方法里的this指向

因为我想要让伪数组也能使用数组的方法,为什么伪数组就不能使用数组方法,为什么数组就能使用push方法了

一个数组都是由它的构造器实例化出来的,var a = []//这是js的语法糖;正规的用法:var a = new Array()

因为Array是一个构造函数,每一个构造函数都有原型,且构造函数构造出来的实例可以使用原型上的方法,也就是说因为Array的原型上有一些方法,所以每一个数组都可以使用这些push等等的方法

因为伪数组的构造器不是Array,当然不能使用Array原型上的push方法

现在数组有一个方法slice,这个方法每次都会返回一个新数组,如果不传参数的话,返回的新数组的元素和原数组的元素是一模一样的

如果伪数组也能执行这个slice方法的话,那么是不是就会返回一个新的真正的数组,并且元素一样,但是不能直接执行

所以我们使用偷梁换柱的方法,让一个真正的数据,或者直接从Array.prototype上执行slice方法,但是在执行的时候通过call来将里面的this换成咱们的伪数组,这样的话,就会返回一个元素和伪数组元素一样的真正数组了

```

let arr = [].slice.call(arg) //Array.prototype.slice.call(arg)

```

---

##### requestGet/requestPost

1. 通过request来get数据(请求的是豆瓣电影的api):

```

let target = 'http://api.douban.com/v2/movie/in_theaters'

const http = require('http')

const url_info = require('url').parse(target,true)

//定义配置选项

let options = {

hostname: url_info.hostname,//要请求的接口的域名

port: url_info.port||80,

path: url_info.pathname,

method: 'GET'

};

//创建一个请求的对象

var req = http.request(options, function(res) {//回调函数能接收到响应对象

//状态码 res.statusCode

//响应头 res.headers

res.setEncoding('utf8');//设置字符编码

let result = ''

res.on('data', function (chunk) {//通过data事件来接收数据

result+=chunk

});

res.on('end', function () {//接收完成后触发

fs.writeFileSync('./movie.json',result)

});

});

//绑定error事件,如果出错会执行

req.on('error', function(e) {

console.log('problem with request: ' + e.message);

});

//标识请求完成

req.end();

```

2. 通过request的post方法来请求:

注意,get方法发送的参数是拼接在url上的,post是以formdata存在的,但是发送过去的必须都得是字符串格式

大部分步骤基本一样,但是需要注意的是,post请求的时候需要设置请求头里的Content-Length,需要通过req.write方法来写入请求信息

```

const target = 'http://post.baibaoyun.com/api/0a953068ff01781ce22c0822c075018c'

const qs = require('querystring')

const http = require('http')

let url_info = require('url').parse(target)

//定义好要发送的数据

var postData = qs.stringify({

'a':'text1',

'b':'text2'

});//必须变成这样的格式 'a=text1&b=text2'

var options = {

hostname: url_info.hostname,

port: url_info.port||80,

path: url_info.pathname,

method: 'POST',

headers: {

'Content-Type': 'application/x-www-form-urlencoded',

'Content-Length': postData.length//post发送的时候需要将请求头的Content-Length设置为发送数据的leng

}

};

var req = http.request(options, function(res) {

res.setEncoding('utf8');

let result = ''

res.on('data', function (chunk) {

result+=chunk

});

res.on('end', function () {

console.log(result)

});

});

req.on('error', function(e) {

console.log('problem with request: ' + e.message);

});

//写入请求发送的数据

req.write(postData);

req.end();

```

###### 小作业

总结POST和GET的区别,每位同学都写一个md文档

---

##### fileStystem模块

后端语言都有操作文件系统的能力,在nodejs里我们依靠的是fs模块

每种操作的方法基本都有同异步的两种不同方法

1.查看文件信息(多用来判断文件是否存在)exists

```

var fs=require('fs');

//异步查询文件信息

fs.stat("../sources/temp.txt",(err,data)=>{

if(err){

console.log(err);

}else{

console.log(data)

//判断是否是文件

console.log(`文件:${data.isFile()}`)

//判断是否是路径

console.log(`目录:${data.isDirectory()}`)

}

})

//同步查询

//fs.statSync("../sources/temp.txt")

console.log(1)

```

2.创建一个目录

```

//创建一个目录,如果目录已存在的话就会返回错误信息

fs.mkdir("logs",function(err){

if(err){

console.log(err);

}else{

console.log('success');

}

})

```

3.写入文件

```

//给文件写内容,当文件不存在会创建一个文件,第二个参数为写入的内容,每次写入都会覆盖

fs.writeFile('logs/hello.log','hello everyone\n',function(err){

if(err){

console.log(err);

}else{

console.log('success')

}

})

//给文件中追加内容

fs.appendFile("logs/hello.log",'hello world\n',function(err){

if(err){

console.log(err);

}else{

console.log('success')

}

})

```

4.读取文件内容

```

//读取文件内容的方法,第二个参数可选,为读取的编码格式

fs.readFile("logs/hello.log",'utf-8',function(err,data){

if(err){

console.log(err);

}else{

console.log(data)

}

})

```

5.读取目录内容

```

//读取目录内容,返回一个数组

fs.readdir("logs",function(err,files){

if(err){

console.log(err);

}else{

console.log(files)

}

})

```

6.文件重命名

```

//重命名方法

fs.rename("logs/hello.log","logs/world.log",function(err){

if(err){

console.log(err)

}else{

console.log('success')

}

})

```

7.删除目录、文件

fs.rmdir可以删除目录但是,必须是空目录,fs.unlink可以删除文件,如果我们要删除一个目录及它下面的文件或子目录的话,我们需要先读取出来,删除完成后再进行根目录的删除

8.小练习:创建一个copy复制文件的函数

```

const fs = require('fs')

const _path = require('path')

let copy = (path,target)=>{

fs.stat(path,(err)=>{

if(err){

console.log('要复制的文件不存在')

}else{

let content = fs.readFileSync(path)

fs.writeFile(target+'/'+_path.basename(path).replace('.','-副本.'),content,(err)=>{

if(err){console.log('目标目录不存在')}else{

console.log('success')

}

})

}

})

}

copy('./sources/hello.txt','./sources/a')

```

9.fs-extra模块

这是一个第三方的fs模块,需要下载  npm install fs-extra

fs模块上的方法它都有,并且还封装了一些很好用的方法,比如:copy、remove..

[文档地址](https://www.npmjs.com/package/fs-extra)

###### ES6小知识

Object.assign方法可以将一些对象中的属性和方法扩展到某一个对象上

```

var a = {y:2}

var b ={x:1}

var c = {z:3}

Object.assign(a,b,c)

console.log(a)//{x:1,y:2,z:3}

```

---

上一篇下一篇

猜你喜欢

热点阅读