Node 爬虫

255 阅读3分钟

爬虫的原理

1.获取初始的URL。初始的URL地址可以由用户人为地指定,也可以由用户指定的某个或某几个初始爬取网页决定。

2.根据初始的URL爬取页面并获得新的URL。获得初始的URL地址之后,首先需要爬取对应URL地址中的网页,爬取了对应的URL地址中的网页后,将网页存储到原始数据库中,并且在爬取网页的同时,发现新的URL地址,同时将已爬取的URL地址存放到一个URL列表中,用于去重及判断爬取的进程。

3.将新的URL放到URL队列中。在第2步中,获取了下一个新的URL地址之后,会将新的URL地址放到URL队列中。

4.从URL队列中读取新的URL,并依据新的URL爬取网页,同时从新网页中获取新URL,并重复上述的爬取过程。

5.满足爬虫系统设置的停止条件时,停止爬取。在编写爬虫的时候,一般会设置相应的停止条件。如果没有设置停止条件,爬虫则会一直爬取下去,一直到无法获取新的URL地址为止,若设置了停止条件,爬虫则会在停止条件满足时停止爬取

实现爬虫的技术

开发网络爬虫的语言有很多,常见的语言有:Python、Java、PHP、Node.JS、C++、Go语言等。以下我们将分别介绍一下用这些语言写爬虫的特点:

● Python:爬虫框架非常丰富,并且多线程的处理能力较强,并且简单易学、代码简洁,优点很多。

● Java:适合开发大型爬虫项目。

● PHP:后端处理很强,代码很简洁,模块也较丰富,但是并发能力相对来说较弱。

● Node.JS:支持高并发与多线程处理。

● C++:运行速度快,适合开发大型爬虫项目,成本较高。

● Go语言:同样高并发能力非常强。

使用node进行爬虫

获取网页数据

1、http

const http=require('http')

http.get(url, (res) => {
  // 数据分段 只要接受数据就会触发data事件 chunk表示每次接受的数据分段
  let rawData=''
  res.on('data', (chunk) => {
    rawData + = chunk.toString('utf8')
  })
  
  //数据传输完毕
  res.on('end', () => {
  	console.log('获取到的网页数据:',rawData)
  })
}).on('error', (err) => {
  console.log('请求错误')
})

2、 request

const request = require("request");

request.get(url,  (err, response, body) => {
  if (!err && response.statusCode == 200) { 
    console.log('获取到的网页数据:',body)
  }
})

3、 superagent

const superagent= require('superagent');

superagent.get(url).end((err, res) => {
  if (err) {
    // 如果访问失败或者出错,会这行这里
    console.log(`数据抓取失败 - ${err}`)
  } else {
   // 访问成功,请求页面所返回的数据会包含在res
   // 抓取热点新闻数据
   console.log(`数据抓取成功 - ${res}`)
  }
});

4、axios


分析数据

使用第三方模块 cheerio

将网页数据转化成JQ的写法


// cheerio 使用试例

const cheerio = require('cheerio');
const $ = cheerio.load('<h2 class="title">Hello world</h2>');

$('h2.title').text('Hello there!');
$('h2').addClass('welcome');

$.html();

使用 eventproxy 重复异步协作


var ep = new EventProxy();

ep.all('tpl', 'data', function(tpl, data){//or ep.all(['tpl', 'data'], function(tpl, data{}))
    //在所有指定的事件触发后, 将会被调用执行
    //参数对应各自的事件名
});

fs.readFile('template.tpl', 'utf-8', function(err, content){
    ep.emit('tpl', content);
});

db.get('sql', function(err, content){
    ep.emit('data', result);
});

// 重复请求
ep.after('got_file', files.length, function(list){
    //在所有文件的异步执行结束后将被执行
    //所有文件的内容都存在list数组中
});
for(var i = 0; i<files.length; i++){
    fs.readFile(files[i], 'utf-8', function(err, content){
        //触发结果事件
        ep.emit('got_file', content);
    })
}

邮箱发送

发送邮箱—— nodemailer

"use strict";
const nodemailer = require("nodemailer");

// async..await is not allowed in global scope, must use a wrapper
async function main() {
  // Generate test SMTP service account from ethereal.email
  // Only needed if you don't have a real mail account for testing
  let testAccount = await nodemailer.createTestAccount();

  // create reusable transporter object using the default SMTP transport
  let transporter = nodemailer.createTransport({
    host: "smtp.ethereal.email",
    port: 587,
    secure: false, // true for 465, false for other ports
    auth: {
      user: testAccount.user, // 发送方账号
      pass: testAccount.pass, // 授权码 可自行百度各邮箱授权码获取
    },
  });

  // send mail with defined transport object
  let info = await transporter.sendMail({
    from: '"Fred Foo 👻" <foo@example.com>', // sender address
    to: "bar@example.com, baz@example.com", // list of receivers
    subject: "Hello ✔", // Subject line
    text: "Hello world?", // plain text body
    html: "<b>Hello world?</b>", // html body
  });

  console.log("Message sent: %s", info.messageId);
  // Message sent: <b658f8ca-6296-ccf4-8306-87d57a0b4321@example.com>

  // Preview only available when sending through an Ethereal account
  console.log("Preview URL: %s", nodemailer.getTestMessageUrl(info));
  // Preview URL: https://ethereal.email/message/WaQKMgKddxQDoou...
}

自定义模版——art-template

aui.github.io/art-templat…

var template = require('art-template');
const html=template(path.join(__dirname,'./index.html'),{ 
  user: '张三'age: '22'
})

定时发送——node-schedule

*    *    *    *    *    *
┬    ┬    ┬    ┬    ┬    ┬
│    │    │    │    │    │
│    │    │    │    │    └ day of week (0 - 7) (0 or 7 is Sun)
│    │    │    │    └───── month (1 - 12)
│    │    │    └────────── day of month (1 - 31)
│    │    └─────────────── hour (0 - 23)
│    └──────────────────── minute (0 - 59)
└───────────────────────── second (0 - 59, OPTIONAL)
const schedule = require('node-schedule');

const job = schedule.scheduleJob({hour: 16, minute: 11, second: 10}, function(){
  console.log('The answer to life, the universe, and everything!');
});

const job2 = schedule.scheduleJob([
  {hour: 16, minute: 11, second: 10},
  {hour: 17, minute: 11, second: 10}], 
function(){
  console.log('The answer to life, the universe, and everything!');
});

依赖汇总

eventproxy —— 重复异步操作处理

superagent —— 发送爬虫请求

cheerio —— 爬虫数据分析

art-template —— 模版数据

nodemailer —— 邮箱发送

node-schedule —— 定时任务