nodejs 爬虫

最近在学习nodes,然后就动手跟着教程做了第一个nodejs的小demo—爬虫,我选取的是菜鸟教程网站首页(http://www.runoob.com)进行爬取。

但是在跟着教程做的过程中发现输出的网页HTML出现了乱码现象,百度了一下才知道是网站的代码是进行了压缩的,于是我便在代码里面引用了var zlib = require(“zlib”);进行处理,最后得到正确的html页面代码:

ar http=require('http');
var zlib = require("zlib");
var url='http://www.runoob.com'
// 该方法和 Http.request()的不同在于,该方法只以 GET 方式请求,并且会自动调用 req.end()来结束请求。
http.get(url,function(res){
     var html = []
    // 当response有data事件触发时,调用回调函数
    res.on('data',function(data){
        html.push(data)
    })
    // 在end事件上将html打印出来
    res.on('end',function(){
        // console.log(html)
         var buffer = Buffer.concat(html);
         zlib.gunzip(buffer, function(err, decoded) {
            console.log(decoded.toString());
        })
    })
}).on('error',function(){
    // 在最后绑定一个error事件
    console.log("获取数据出错!")
})

此外,数据是gzip压缩后的数据,所以建议不要直接使用字符串拼接,而是采用Buffer的concat方法。之后使用自带的zlib进行gunzip即可。
这个写法只是针对这一个问题而已,实际应用中需要自己去判断返回内容的encoding。当然了,第三方的工具,比如request和楼下的superAgent会更加方便一些。

2、使用cheerio
cheerio是nodejs的抓取页面模块,为服务器特别定制的,快速、灵活、实施的jQuery核心实现。适合各种Web爬虫程序。
使用时要现将抓取到的HTML文件给load进去,然后调用cheerio的方法,cheerio的用法和jq类似,很好上手。

之后将获取到的HTML切了,取得需要的信息放在数组中,最后将打印取出就好了。

3、利用promise对爬虫代码进行优化
(1)http和HTTPS的区别:
这里写图片描述
总之,https协议是在http之上加入了SSL/TLS握手以及数据加密传输,SSL/TLS是他们的最大区别。
在nodejs 中的HTTPS模块是用于处理加密访问的,http和HTTPS两者的使用是相同的。
4、node模块加载:
(1)Node.js的模块分为两类,一类为原生(核心)模块,*一类为文件模块,其中文件模块又分为三类*。

这三类文件模块以后缀来区分,Node.js会根据后缀名来决定加载方法。
.js。通过fs模块同步读取js文件并编译执行。
.node。通过C/C++进行编写的Addon。通过dlopen方法进行加载。
.json。读取文件,调用JSON.parse解析加载。

原生模块在Node.js源代码编译的时候编译进了二进制执行文件,加载的速度最快。另一类文件模块是动态加载的,加载速度比原生模块慢。但是Node.js对原生模块和文件模块都进行了缓存,于是在第二次require时,是不会有重复开销的。其中原生模块都被定义在lib这个目录下面,文件模块则不定性。由于通过命令行加载启动的文件几乎都为文件模块。我们从Node.js如何加载文件模块开始谈起。加载文件模块的工作,主要由原生模块module来实现和完成,该原生模块在启动时已经被加载,进程直接调用到runMain静态方法。

require方法中的文件查找策略

由于Node.js中存在4类模块(原生模块和3种文件模块),尽管require方法极其简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同。

这里写图片描述

从文件模块缓存中加载
尽管原生模块与文件模块的优先级不同,但是都不会优先于从文件模块的缓存中加载已经存在的模块。

从原生模块加载
原生模块的优先级仅次于文件模块缓存的优先级。require方法在解析文件名之后,优先检查模块是否在原生模块列表中。以http模块为例,尽管在目录下存在一个http/http.js/http.node/http.json文件,require(“http”)都不会从这些文件中加载,而是从原生模块中加载。

原生模块也有一个缓存区,同样也是优先从缓存区加载。如果缓存区没有被加载过,则调用原生模块的加载方式进行加载和执行。

从文件加载
当文件模块缓存中不存在,而且不是原生模块的时候,Node.js会解析require方法传入的参数,并从文件系统中加载实际的文件,加载过程中的包装和编译细节在前一节中已经介绍过,这里我们将详细描述查找文件模块的过程,其中,也有一些细节值得知晓。

require方法接受以下几种参数的传递:

http、fs、path等,原生模块。
./mod或../mod,相对路径的文件模块。
/pathtomodule/mod,绝对路径的文件模块。
mod,非原生模块的文件模块。

简而言之,加载的优先顺序为:文件模块的缓存中加载已经存在的模块>原生模块缓存区加载>原生模块加载>文件模块加载

(2)commonjs(模块加载机制被称为CommonJS规范,主要为了JS在后端的表现制定的,他是不适合前端的)

  • 分析前后端js的不同

    服务器端JS :相同的代码需要多次执行, CPU和内存资源是瓶颈 ,加载时从磁盘中加载
    浏览器端JS:代码需要从一个服务器端分发到多个客户端执行,带宽是瓶颈, 加载时需要通过网络加载


在这个规范下,每个.js文件都是一个模块,它们内部各自使用的变量名和函数名都互不冲突,例如,hello.js和main.js都申明了全局变量var s = ‘xxx’,但互不影响。
一个模块想要对外暴露变量(函数也是变量),可以用module.exports = variable;,一个模块要引用其他模块暴露的变量,用var ref = require(‘module_name’);就拿到了引用模块的变量。

实现“模块”功能的奥妙就在于JavaScript是一种函数式编程语言,它支持闭包。如果我们把一段JavaScript代码用一个函数包装起来,这段代码的所有“全局”变量就变成了函数内部的局部变量。

CommonJS定义的模块分为:{模块引用(require)} {模块定义(exports)} {模块标识(module)}

require()用来引入外部模块;exports对象用于导出当前模块的方法或变量,唯一的导出口;module对象就代表模块本身。

  • AMD(即Asynchronous Module Definition,中文名是异步模块定义的意思。它是一个在浏览器端模块化开发的规范)
    由于不是JavaScript原生支持,使用AMD规范进行页面开发需要用到对应的库函数,也就是大名鼎鼎RequireJS,实际上AMD 是 RequireJS 在推广过程中对模块定义的规范化的产出

requireJS主要解决两个问题:

1、多个js文件可能有依赖关系,被依赖的文件需要早于依赖它的文件加载到浏览器
2、js加载的时候浏览器会停止页面渲染,加载文件越多,页面失去响应时间越长
- CMD(Common Module Definition,大名远扬的玉伯写了seajs,就是遵循他提出的CMD规范,CMD有个浏览器的实现SeaJS,SeaJS要解决的问题和requireJS一样,只不过在模块定义方式和模块加载(可以说运行、解析)时机上有所不同 语法 ,Sea.js 推崇一个模块一个文件,遵循统一的写法 )

CMD推崇
一个文件一个模块,所以经常就用文件名作为模块id
CMD推崇依赖就近,所以一般不在define的参数中写依赖,在factory中写
factory是一个函数,有三个参数,function(require, exports, module)
require 是一个方法,接受 模块标识 作为唯一参数,用来获取其他模块提供的接口:require(id)
exports 是一个对象,用来向外提供模块接口
module 是一个对象,上面存储了与当前模块相关联的一些属性和方法

AMD与CMD区别

关于这两个的区别网上可以搜出一堆文章,简单总结一下

最明显的区别就是在模块定义时对依赖的处理不同

1、AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块
2、CMD推崇就近依赖,只有在用到某个模块的时候再去require
这种区别各有优劣,只是语法上的差距,而且requireJS和SeaJS都支持对方的写法

AMD和CMD最大的区别是对依赖模块的执行时机处理不同,注意不是加载的时机或者方式不同

很多人说requireJS是异步加载模块,SeaJS是同步加载模块,这么理解实际上是不准确的,其实加载模块都是异步的,只不过AMD依赖前置,js可以方便知道依赖模块是谁,立即加载,而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略

为什么我们说两个的区别是依赖模块执行时机不同,为什么很多人认为ADM是异步的,CMD是同步的(除了名字的原因。。。)

同样都是异步加载模块,AMD在加载模块完成后就会执行改模块,所有模块都加载执行完后会进入require的回调函数,执行主逻辑,这样的效果就是依赖模块的执行顺序和书写顺序不一定一致,看网络速度,哪个先下载下来,哪个先执行,但是主逻辑一定在所有依赖加载完成后才执行

CMD加载完某个依赖模块后并不执行,只是下载而已,在所有依赖模块加载完成后进入主逻辑,遇到require语句的时候才执行对应的模块,这样模块的执行顺序和书写顺序是完全一致的

这也是很多人说AMD用户体验好,因为没有延迟,依赖模块提前执行了,CMD性能好,因为只有用户需要的时候才执行的原因

最后转载一篇关于模块化发展的文章:链接

5、nodejs 文件系统(fs)
Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync()。
异步的方法函数最后一个参数为回调函数,回调函数的第一个参数包含了错误信息(error)。
建议大家是用异步方法,比起同步,异步方法性能更高,速度更快,而且没有阻塞。

6、buffer(二进制缓存区,可以用于处理tcp/图像/文件/网络等传输信息)
Buffer在nodejs中用来处理二进制的数组(js字符串是用utf-8存储的,处理二进制的能力是很弱的,而网络层对不同资源的请求,响应等基本以二进制来进行交互),它便创建一个专门存储二进制的缓存区,并提供了一些方法对这些缓存区的数据做进一步的处理。
buffer在nodejs里可全局访问,不需要使用require()来引入和加载它。

buffer是一个对象,也是一个构造函数,具有自己的属性和方法。通过它new出来的实例实际上是代表了V8引擎分配的一段内存,基本上是一个数组,成员也都是整数值。(一个 Buffer 类似于一个整数数组,但它对应于 V8 堆内存之外的一块原始内存。)

Buffer的实例化分为三种方法:
1.通过字符串的形式实例化,eg:new Buffer(‘hello world’);
2。通过定义buffer中size的大小实例化,eg:var buf=new Buffer(8)//定义一个缓冲区长度8的;
3.通过数组的方式实例化,eg:var buf=new Buffer([1,2,3,4])。

new Buff(‘hello 慕课网’); //默认是UTF-8编码格式
new Buff(‘hello’,’base64’); //指定编码格式
var buf = new Buffer(8);//超出长度时,超出部分不会被缓存,只存储8位
var buf=new Buffer([1,2,3,4]);console.log(buf[2])
3//说明可以使用数组的方式来取得内容
(1)buffer的一些静态方法和属性:
poolSize:内存载体的容量
isBuffer:是否为buffer类型对象
compare:用来判断两个buffer对象的相对位置
isEncoding:判断nodejs是否支持某种编码
concat:将几个buffer对象连接创建一个新的buffer对象
byteLength:获得指定编码下字符串所占的字节数
这里写图片描述

7、stream(流,Buffer用来保存原始数据,流是用来暂存和移动数据的,二者经常结合起来用。流是以buffer的形式存在,Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。)
所有的 Stream 对象都是 EventEmitter 的实例,也就是说stream是基于事件机制工作的,因此流有异步的特征,支持事件监听,可以监听流任何阶段的变化。
(1)管道方法 pipe()
Stream 有四种流类型:
Readable - 可读操作。(它有两种模式:流动模式(resume,这时候数据从底层系统读出,并提供给上层的应用程序)和暂停模式(pause))
Writable - 可写操作。(用来消费数据)
Duplex - 可读可写操作.(双工流,例如:web socket等)
Transform - 操作被写入数据,然后读出结果。(转换流,也是双工的,但是并不保存数据,只负责处理流经它的数据,是一个中间控制器(如水管中部的阀门))

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
爬虫(Web Crawler)是一种自动化程序,用于从互联网上收集信息。其主要功能是访问网页、提取数据并存储,以便后续分析或展示。爬虫通常由搜索引擎、数据挖掘工具、监测系统等应用于网络数据抓取的场景。 爬虫的工作流程包括以下几个关键步骤: URL收集: 爬虫从一个或多个初始URL开始,递归或迭代地发现新的URL,构建一个URL队列。这些URL可以通过链接分析、站点地图、搜索引擎等方式获取。 请求网页: 爬虫使用HTTP或其他协议向目标URL发起请求,获取网页的HTML内容。这通常通过HTTP请求库实现,如Python中的Requests库。 解析内容: 爬虫对获取的HTML进行解析,提取有用的信息。常用的解析工具有正则表达式、XPath、Beautiful Soup等。这些工具帮助爬虫定位和提取目标数据,如文本、图片、链接等。 数据存储: 爬虫将提取的数据存储到数据库、文件或其他存储介质中,以备后续分析或展示。常用的存储形式包括关系型数据库、NoSQL数据库、JSON文件等。 遵守规则: 为避免对网站造成过大负担或触发反爬虫机制,爬虫需要遵守网站的robots.txt协议,限制访问频率和深度,并模拟人类访问行为,如设置User-Agent。 反爬虫应对: 由于爬虫的存在,一些网站采取了反爬虫措施,如验证码、IP封锁等。爬虫工程师需要设计相应的策略来应对这些挑战。 爬虫在各个领域都有广泛的应用,包括搜索引擎索引、数据挖掘、价格监测、新闻聚合等。然而,使用爬虫需要遵守法律和伦理规范,尊重网站的使用政策,并确保对被访问网站的服务器负责。
Node.js是一个基于Chrome V8引擎的JavaScript运行环境,它可以让你使用JavaScript来开发服务器端的应用程序。而爬虫是一种自动化程序,用于从互联网上获取数据。在Node.js中,你可以使用一些库来实现爬虫功能,例如`axios`、`cheerio`和`puppeteer`等。 下面是一个使用Node.js实现爬取静态页面的简单示例: 1. 首先,你需要安装Node.js和npm(Node.js的包管理器)。 2. 在你的项目目录下,打开终端并运行`npm init`命令来初始化一个新的Node.js项目,并按照提示填写相关信息。 3. 安装所需的库,例如`axios`和`cheerio`,可以使用以下命令: ``` npm install axios cheerio ``` 4. 创建一个新的JavaScript文件,例如`crawler.js`,并在文件中编写以下代码: ```javascript const axios = require('axios'); const cheerio = require('cheerio'); // 定义要爬取的页面URL const url = 'https://example.com'; // 发起HTTP请求获取页面内容 axios.get(url) .then(response => { // 使用cheerio解析页面内容 const $ = cheerio.load(response.data); // 在页面中查找需要的数据并进行处理 const title = $('h1').text(); console.log('页面标题:', title); }) .catch(error => { console.error('请求页面失败:', error); }); ``` 5. 运行该脚本,使用以下命令: ``` node crawler.js ``` 以上示例代码使用`axios`库发送HTTP请求获取页面内容,然后使用`cheerio`库解析页面内容。你可以根据需要在代码中添加更多的逻辑来处理页面数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值