• 欢迎访问开心洋葱网站,在线教程,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站,欢迎加入开心洋葱 QQ群
  • 为方便开心洋葱网用户,开心洋葱官网已经开启复制功能!
  • 欢迎访问开心洋葱网站,手机也能访问哦~欢迎加入开心洋葱多维思维学习平台 QQ群
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏开心洋葱吧~~~~~~~~~~~~~!
  • 由于近期流量激增,小站的ECS没能经的起亲们的访问,本站依然没有盈利,如果各位看如果觉着文字不错,还请看官给小站打个赏~~~~~~~~~~~~~!

NodeJs 入门到放弃 — 常用模块及网络爬虫(二)

JAVA相关 Echoyya、 2425次浏览 0个评论

码文不易啊,转载请带上本文链接呀,感谢感谢 https://www.cnblogs.com/echoyya/p/14473101.html

目录

  • 码文不易啊,转载请带上本文链接呀,感谢感谢 https://www.cnblogs.com/echoyya/p/14473101.html
  • Buffer (缓冲区)
    • Buffer 创建
    • Buffer 写入
    • Buffer 读取
    • 更多>>
  • fs (文件系统)
    • 读取文件
    • 获取文件信息
    • 写入文件
    • 删除文件
    • 目录操作
  • Stream (流)
    • 读取流
    • 写入流
    • 管道 pipe
    • 链式流
  • path (路径)
  • url (URL)
  • http (协议)
    • B/S开发模式
    • 定义、约束、交互特点
  • http (模块)
  • 网络爬虫
    • 小案例
  • 上集: NodeJs 入门到放弃 — 入门基本介绍(一)
  • 续集: NodeJs 入门到放弃 — 网络服务器(三)

Buffer (缓冲区)

JavaScript 语言自身只有字符串数据类型,没有二进制数据类型。二进制可以存储任意类型的数据,电脑中所有的数据都是二进制。

在处理像TCP流或文件流时,必须使用到二进制数据。因此在 Node.js中,定义了一个 Buffer 类,该类用来创建一个专门存放二进制数据的缓存区。

Buffer 与字符编码:当在 Buffer 和字符串之间转换时,可以指定字符编码。 如果未指定字符编码,则默认使用 UTF-8 。

Buffer 创建

Buffer对象可以通过多种方式创建,v6.0之前直接使用new Buffer()构造函数来创建对象实例,v6.0以后,官方文档建议使用Buffer.from() 创建对象。nodejs中文网、菜鸟教程-nodejs

Buffer.from(buffer):复制传入的 Buffer ,返回一个新的 Buffer 实例

Buffer.from(string[, encoding]):要编码的字符串。字符编码。默认值: 'utf8'

Buffer.alloc(size[, fill[, encoding]]): 返回一个指定大小的 Buffer 实例,如果没有设置 fill,则默认填满 0

var buf = Buffer.from('Echoyya'); 
var buf1 = Buffer.from(buf);   
console.log(buf);  // <Buffer 45 63 68 6f 79 79 61>
buf1[0] = 0x65;
console.log(buf.toString());// Echoyya
console.log(buf1.toString()); // echoyya

var buf2 = Buffer.from('4563686f797961', 'hex');  // 设置编码
console.log(buf2);             // <Buffer 45 63 68 6f 79 79 61>
console.log(buf2.toString());  // Echoyya

var buf3 = Buffer.from('4563686f797961');  // 默认编码
console.log(buf3);              // <Buffer 34 35 36 33 36 38 36 66 37 39 37 39 36 31>
console.log(buf3.toString());   // 4563686f797961

var buf4 = Buffer.alloc(4);
console.log(buf4);      // <Buffer 00 00 00 00>

Buffer 写入

buf.write(string[, offset[, length]][, encoding])

参数描述:

  • string – 写入缓冲区的字符串。

  • offset – 缓冲区开始写入的索引值,默认为 0 。

  • length – 写入的字节数,默认为 buffer.length

  • encoding – 使用的编码。默认为 ‘utf8’ 。

根据 encoding 的字符编码写入 string 到 buf 中的 offset 位置。 length 参数是写入的字节数。

返回值:返回实际写入的大小。如果 buffer 空间不足, 则只会写入部分字符串。

//buffer的大小一旦被确定则不能被修改
var buf5 = Buffer.alloc(4);
console.log(buf5.length);  // 4

var len = buf5.write("Echoyya");
console.log(buf5.toString());  // Echo 
console.log("写入字节数 : "+  len);  // 写入字节数 : 4

Buffer 读取

读取 Node 缓冲区数据:buf.toString([encoding[, start[, end]]])

参数描述:

  • encoding – 使用的编码。默认为 ‘utf8’ 。

  • start – 指定开始读取的索引位置,默认为 0。

  • end – 结束位置,默认为缓冲区的末尾。

返回值:解码缓冲区数据并使用指定的编码返回字符串。

buf = Buffer.alloc(26);
for (var i = 0 ; i < 26 ; i++) {
  buf[i] = i + 97;
}
console.log(buf); // <Buffer 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70 71 72 73 74 75 76 77 78 79 7a>
console.log( buf.toString('ascii'));       // abcdefghijklmnopqrstuvwxyz
console.log( buf.toString('ascii',0,5));   // abcde
console.log( buf.toString('utf8',0,5));    // abcde
console.log( buf.toString(undefined,0,5)); // abcde 默认utf8

更多>>

除上述最基本的读写操作外,还有许多强大的API:

  • 缓冲区合并:Buffer.concat(list[, totalLength])

  • 缓冲区比较:Buffer.compare(target[, targetStart[, targetEnd[, sourceStart[, sourceEnd]]]])

  • 缓冲区判断:Buffer.isBuffer(obj)

  • 缓冲区拷贝:Buffer.copy(target[, targetStart[, sourceStart[, sourceEnd]]])

fs (文件系统)

Node.js提供一组文件操作API,fs 模块可用于与文件系统进行交互。所有的文件系统操作都具有同步的、回调的、以及基于 promise 的形式。同步与异步的区别,主要在于有无回调函数,同步方法直接在异步方法后面加上Sync

const fs = require('fs');

读取文件

异步:fs.readFile(path, callback)

同步:fs.readFileSync(path)

fs.readFile('文件名', (err, data) => {
  if (err) throw err;
  console.log(data);
});

var data = fs.readFileSync('文件名');

获取文件信息

异步模式获取文件信息:fs.stat(path, callback)

  • path – 文件路径。
  • callback – 回调函数,带有两个参数如:(err, stats), stats 是 fs.Stats 对象。

fs.stat(path)执行后,将stats实例返回给回调函数。可以通过提供方法判断文件的相关属性。

var fs = require('fs');

fs.stat('./data', function (err, stats) {
	// stats 是文件的信息对象,包含常用的文件信息
	// size: 文件大小(字节)
	// mtime: 文件修改时间
	// birthtime:文件创建时间
	// 等等...
    console.log(stats.isDirectory());         //true
})

stats类中的方法有:

方法 描述
stats.isFile() 判断是文件返回 true,否则返回 false。
stats.isDirectory() 判断是目录返回 true,否则返回 false。
var fs = require("fs");

console.log("准备打开文件!");
fs.stat('./data', function (err, stats) {
   if (err) {
       return console.error(err);
   }
   console.log(stats);
   console.log("读取文件信息成功!");
   
   // 检测文件类型
   console.log("是否为文件(isFile) ? " + stats.isFile());
   console.log("是否为目录(isDirectory) ? " + stats.isDirectory());    
});

写入文件

异步:fs.writeFile(file, data, callback)

同步:fs.writeFileSync(file, data),没有返回值

如果文件存在,该方法写入的内容会覆盖原有内容,反之文件不存在,调用该方法写入将创建一个新文件

const fs = require('fs')

var hello = '<h1>hello fs</h1>'
fs.writeFile('./index.html',hello,function(err){
  if(err) throw err
  else console.log('文件写入成功');
})

var helloSync= '<h1>hello fs Sync</h1>'
fs.writeFileSync('./index.html',helloSync)

删除文件

异步:fs.unlink(path, callback)

同步:fs.unlinkSync(path),没有返回值

对空或非空的目录均不起作用。 若要删除目录,则使用 fs.rmdir()。

const fs = require('fs')

fs.unlink('./index.html',function(err){
  if(err) throw err
  else console.log('文件删除成功');
})

fs.unlinkSync('./index.html')

目录操作

  1. 创建目录

    • 异步:fs.mkdir(path, callback)

    • 同步:fs.mkdirSync(path),没有返回值

  2. 读取目录文件

    • 异步:fs.readdir(path, callback)callback 回调有两个参数err, files,files是目录下的文件列表

    • 同步:fs.readdirSync(path)

    const fs = require("fs");
    
    console.log("查看 ./data 目录");
    fs.readdir("./data",function(err, files){
       if(err) throw err
       else{
       	 files.forEach( function (file){
           console.log( file );
         });
       }
    });
    
    var files = fs.readdirSync('./data')
    
  3. 删除空目录

    注:不能删除非空目录

    • 异步:fs.rmdir(path, callback) ,回调函数,没有参数

    • 同步:fs.rmdirSync(path)

  4. 删除非空目录(递归)

    实现思路:

    • fs.readdirSync:读取文件夹中所有文件及文件夹

    • fs.statSync:读取每一个文件的详细信息

    • stats.isFile():判断是否是文件,是文件则删除,否则递归调用自身

    • fs.rmdirSync:删除空文件夹

    const fs = require('fs')
    
    function deldir(p) {
      // 读取文件夹中所有文件及文件夹
      var list = fs.readdirSync(p)
      list.forEach((v, i) => {
        // 拼接路径
        var url = p + '/' + v
        // 读取文件信息
        var stats = fs.statSync(url)
        // 判断是文件还是文件夹
        if (stats.isFile()) {
          // 当前为文件,则删除文件
          fs.unlinkSync(url)
        } else {
          // 当前为文件夹,则递归调用自身
          arguments.callee(url)
        }
      })
      // 删除空文件夹
      fs.rmdirSync(p)
    }
    
    deldir('./data')
    

Stream (流)

是一组有序的、有起点、有终点的字节数据的传输方式,在应用程序中,各种对象之间交换与传输数据时:

  1. 总是先将该对象总所包含的数据转换为各种形式的流数据(即字节数据)

  2. 在流传输到达目的对象后,再将流数据转换为该对象中可以使用的数据

与直接读写文件的区别:可以监听它的’data’,一节一节处理文件,用过的部分会被GC(垃圾回收),所以占内存少。 readFile是把整个文件全部读到内存里。然后再写入文件,对于小型的文本文件,没多大问题,但对于体积较大文件,使用这种方法,很容易使内存“爆仓”。理想的方法应该是读一节写一节

流的类型:

  • Readable – 可读操作。

  • Writable – 可写操作。

  • Duplex – 可读可写操作.

  • Transform – 操作被写入数据,然后读出结果。

常用的事件:

  • data – 当有数据可读时触发。

  • end – 没有更多的数据可读时触发。

  • error – 在接收和写入过程中发生错误时触发。

  • finish – 所有数据已被写入到底层系统时触发。

读取流

var fs = require('fs')
var data = '';

// 创建可读流
var readerStream = fs.createReadStream('./file.txt');

// 设置编码为 utf8。
readerStream.setEncoding('UTF8');

// 处理流事件 --> data, end,  error
readerStream.on('data', function(chunk) {
   data += chunk;
   console.log(chunk.length)  // 一节65536字节, 65536/1024 = 64kb
});

readerStream.on('end',function(){
   console.log(data);
});

readerStream.on('error', function(err){
   console.log(err.stack);
});

console.log("程序执行完毕");

写入流

var fs = require('fs')
var data = '创建一个可以写入的流,写入到文件 file1.txt 中';

// 创建写入流,文件 file1.txt 
var writerStream = fs.createWriteStream('./file1.txt')

// 使用 utf8 编码写入数据
writerStream.write(data,'UTF8');

// 标记文件末尾
writerStream.end();

// 处理流事件 --> finish、error
writerStream.on('finish', function() {
    console.log("写入完成。");
});

writerStream.on('error', function(err){
   console.log(err.stack);
});

console.log("程序执行完毕");

管道 pipe

管道提供了一个输出流 -> 输入流的机制。通常用于从一个流中获取数据传递到另外一个流中。用一根管子(pipe)连接两个桶使得水从一个桶流入另一个桶,这样就可实现大文件的复制过程。

NodeJs 入门到放弃 — 常用模块及网络爬虫(二)

管道语法:reader.pipe(writer);

读取 input.txt 文件内容,并写入 output.txt 文件,两种实现方式对比:

var fs = require("fs");

var readerStream = fs.createReadStream('input.txt'); // 创建一个可读流
var writerStream = fs.createWriteStream('output.txt'); // 创建一个可写流

//1. 以流的方式实现大文件复制
readerStream.on('data',function(chunk){
	writerStream.write(chunk)  // 读一节写一节
})

readerStream.on('end',function(){  // 无可读数据
	writerStream.end()             // 标记文件末尾
	writerStream.on('finish',function(){  // 所有数据已被写入
		console.log('复制完成')
	})
})

// 2. 以管道方式实现大文件复制
readerStream.pipe(writerStream);

链式流

将多个管道连接起来,实现链式操作

管道链式来压缩和解压文件:

var fs = require("fs");
var zlib = require('zlib');

// 压缩 input.txt 文件为 input.txt.gz
var reader = fs.createReadStream('input.txt')
var writer = fs.createWriteStream('input.txt.gz')

reader.pipe(zlib.createGzip()).pipe(writer);
  

// 解压 input.txt.gz 文件为 input.txt
var reader = fs.createReadStream('input.txt.gz')
var writer = fs.createWriteStream('input.txt')

reader.pipe(zlib.createGunzip()).pipe(writer);

path (路径)

是nodejs中提供的系统模块,不需安装,用于格式化或拼接转换路径,执行效果会因应用程序运行所在的操作系统不同,而有所差异。

常用方法:

方法 描述
path.normalize(path) 规范化给定的 path,解析 '..''.' 片段。
path.join([…paths]) 将所有给定的 path 片段连接到一起(使用平台特定的分隔符作为定界符),然后规范化生成的路径。如果路径片段不是字符串,则抛出 TypeError
path.dirname(path) 返回路径中文件夹部分
path.basename(path) 返回路径中文件部分(文件名和扩展名)
path.extname(path) 返回路径中扩展名部分
path.parse(path) 解析路径,返回一个对象,其属性表示 path 的有效元素
var path = require('path')

var p1 = "../../../hello/../a/./b/../c.html"
var p2 = path.normalize(p1)   
console.log(path.normalize(p1));  // ../../../a/c.html
console.log(path.dirname(p2))     // ../../../a
console.log(path.basename(p2))    // c.html
console.log(path.extname(p2))     // .html
console.log(path.parse(p2))       //  {  root: '',  dir: '..\\..\\..\\a',  base: 'c.html',  ext: '.html',  name: 'c'}


console.log(path.join('/目录1', '目录2', '目录3/目录4', '目录5'));  // '/目录1/目录2/目录3/目录4'

var pArr = ['/目录1', '目录2', '目录3/目录4', '目录5']
console.log(path.join(...pArr));  // '/目录1/目录2/目录3/目录4'

// path.join('目录1', {}, '目录2'); // 抛出 ' The "path" argument must be of type string. Received an instance of Object'

url (URL)

url :全球统一资源定位符,对网站资源的一种简洁表达形式,也称为网址

官方规定完整构成:协议://用户名:密码@主机名.名.域:端口号/目录名/文件名.扩展名?参数名=参数值&参数名2=参数值2#hash哈希地址

http 协议 URL常见结构:协议://主机名.名.域/目录名/文件名.扩展名?参数名=参数值&参数名2=参数值2#hash哈希地址

域名是指向IP地址的,需要解析到 IP 地址上,服务器与IP地址形成标识,域名只是人可以看懂的符号,而计算机并看不懂,所以需要 DNS 域名服务器来解析。

nodejs中提供了两套对url模块进行处理的API功能,二者处理后的结果有所不同:

  1. 旧版本传统的 API

  2. 实现了 WHATWG 标准的新 API

var url = require('url');
var uu = 'https://music.163.com:80/aaa/index.html?id=10#/discover/playlist'

// WHATWG 标准API 解析 URL 字符串
var wUrl = new url.URL(uu);
console.log(wUrl);


// 使用传统的 API 解析 URL 字符串:
var tUrl = url.parse(uu);
console.log(tUrl);

NodeJs 入门到放弃 — 常用模块及网络爬虫(二)

http (协议)

网络是信息传输、接收、共享的虚拟平台,而网络传输数据有一定的规则,称协议,HTTP就是其中之一,且使用最为频繁。

B/S开发模式

(Browser/Server,浏览器/服务器模式),浏览器(web客户端)使用HTTP协议就可以访问web服务器上的数据

客户端:发送请求,等待响应

服务器:处理请求,返回响应

定义、约束、交互特点

定义:HTTP 即 超文本传输协议,是一种网络传输协议,采用的是请求 / 响应方式传递数据,该协议规定了数据在服务器与浏览器之间,传输数据的格式与过程

约束:

  1. 约束了浏览器以何种格式两服务器发送数据

  2. 约束了服务器以何种格式接收客户端发送的数据

  3. 约束了服务器以何种格式响应数据给浏览器

  4. 约束了以何种格式接收服务器响应的数据

交互特点:一次请求对应一次响应,多次请求对应多次响应

http (模块)

http模块是nodejs中系统模块,用于网络通讯,可以作为客户端发送请求,亦可以作为服务器端处理响应,

限于篇幅,另总结了一篇博文,介绍了两者的用法,以及客户端向服务器端传参,详见:NodeJs 入门到放弃 — 网络服务器(三)

网络爬虫

网络爬虫(又称为网页蜘蛛,网络机器人),是一种按照一定的规则,自动地抓取万维网信息的程序或者脚本。

小案例

需求:写一个爬虫程序批量下载图片 http://www.nipic.com/photo/canyin/xican/index.html

思路:

  1. 打开对应网站查看内容,找图片地址,找规律

  2. 编写代码获取网站内html代码

  3. 通过正则表达式提取出图片地址

  4. 遍历图片地址数组,请求数据

  5. 将获取到的图片保存下来

var http = require('http')
var fs = require('fs')
var path = require('path')

http.get('http://www.jituwang.com/tuku/index.html',function(res){
  var data = ''  // 用于存放一节一节的HTML数据
  
  // 以流的方式读取数据
  res.on('data',function(chunk){
    data += chunk.toString()
  })

  res.on('end',function(){
    // 正则获取所有图片地址
    var reg = /<img src="(.+?)" alt=".+?"\/>/ig
    var result = ''
    var imgArr = []
    while(result = reg.exec(data)){
      imgArr.push(result[1])
    }
    // 根据数组中的图片地址 获取图片数据
    for (var i in imgArr) {
      setTimeout(function(i){
        getImg(imgArr[i])
      },1000*i,i)
    }
    // fs.writeFileSync('./b.txt',imgArr)   // 可写入文件,查看图片地址
  })
})

function getImg(url){
  http.get(url,function(res){
    res.pipe(fs.createWriteStream(path.join('./img',path.basename(url))))
  })
}

for循环请求数据时,为避免对其服务器造成压力,设置定时器,每隔一秒请求一次,将读到的数据,存储为与服务器上同名。利用上述path模块提供的方法,path.basename(url)

NodeJs 入门到放弃 — 常用模块及网络爬虫(二)

上集: NodeJs 入门到放弃 — 入门基本介绍(一)

续集: NodeJs 入门到放弃 — 网络服务器(三)


开心洋葱 , 版权所有丨如未注明 , 均为原创丨未经授权请勿修改 , 转载请注明NodeJs 入门到放弃 — 常用模块及网络爬虫(二)
喜欢 (0)

您必须 登录 才能发表评论!

加载中……