美文网首页
2018-08-13 node 爬虫

2018-08-13 node 爬虫

作者: c44fce2e629a | 来源:发表于2018-08-19 19:27 被阅读0次

    这次分享下node爬虫,通过实践学习下后端的一些知识。

    访问页面,获取页面内容

    首先我们要像浏览器一样,可以发送一个页面请求,并且可以解析页面内容。
    superagent 是一个客户端HTTP请求库,可以用来模拟浏览器发送请求。
    cheerio是一个转换工具。通过这个工具,我们可以用类似jquery的方式查询和处理获得的页面内容。

    superagent.get('xxx') // 首先访问文章列表页,获取各个章节的url
        .charset('gbk') // 文章内容是中文,这里设定字符集
        .end((err, sres) => {
          // 常规的错误处理
          if (err) {
            return next(err);
          }
          
          let $ = cheerio.load(sres.text, {decodeEntities: false}); // 通过cheerio实现jquery接口
          
          let items = [];
           
          $('#content p').each((idx, element) => {
            let $element = $(element);
            items.push({
              title: $element.html(),
              href: $element.href,
            });
          });
          fs.appendFile('./test.txt', 'abc',  (err)=> {  //将获得的文章列表输出到文件里
            if(!err) console.log('追加内容完成');
          });
          
        });
    

    这样就获得了所有章节的url。
    现在开始获取章节里的文章内容。原理和获取文章列表一致,首先通过循环发送请求获取内容。
    结果并没有获得所有的章节,总是有些章节丢失。
    这下要打些日志看看到底哪里有问题。

    日志

    通过日志记录请求文章内容时的具体状态,方便排查问题。这里使用的是winston,记录下请求发送的时间以及返回状态。

    日志级别

    下面罗列了6种日志级别,和每种日志的使用场景。

    参考文献: https://blog.csdn.net/qq_31332467/article/details/77198158
    Verbose: 开发调试过程中一些详细信息,不应该编译进产品中,只在开发阶段使用。(参考api文档的描述:Verbose should never be compiled into anapplication except during development)
    Debug: 用于调试的信息,编译进产品,但可以在运行时关闭。(参考api文档描述:Debug logs are compiled in but stripped a truntime)
    Info:例如一些运行时的状态信息,这些状态信息在出现问题的时候能提供帮助。
    Warn:警告系统出现了异常,即将出现错误。
    Error:系统已经出现了错误。

    日志设置

    
    const levels = {  //这个是日志级别。在winston里通过设置level可以设置哪些级别的日志可以输出。如果设置成info,则低于2的warn,error也会被输出。
      error: 0, 
      warn: 1, 
      info: 2, 
      verbose: 3, 
      debug: 4, 
      silly: 5 
    };
    
    var winston = require("winston")
    const logger = winston.createLogger({
      level: 'info',
      format: winston.format.json(), // 日志信息的格式
      transports: [
        //
        // - 将所有info,warn,error日志输出到combined.log
        // - 将所有error日志输出到error.log
        //
        new winston.transports.File({ filename: 'error.log', level: 'error' }),
        new winston.transports.File({ filename: 'combined.log' })
      ]
    });
    
    

    关于日志格式,可以自定义。可以参考下面的代码。

    const { createLogger, format, transports } = require('winston');
    const { combine, timestamp, label, printf } = format;
    
    const myFormat = printf(info => {
      return `${info.timestamp} [${info.label}] ${info.level}: ${info.message}`;
    });
    
    const logger = createLogger({
      format: combine(
        label({ label: 'right meow!' }),
        timestamp(),
        myFormat
      ),
      transports: [new transports.Console()]
    });
    // 输入的日志样式
    2018-08-15T07:16:05.923Z [right meow!] info: 297开始请求页面
    
    

    使用方式

    // 打日志,以下两种方式都可以
    logger.log({
      level: 'info',
      message: 'Hello distributed log files!'
    });
    
    logger.info('Hello again distributed logs');
    

    结果分析

    通过分析日志,发现并不是每个页面请求成功的回调都执行了。下面这句话也许就是原因。

    在Node中,长时间的CPU占用会导致后续的异步I/O发不出调用,已完成的I/O的回调函数也会得不到及时执行。 -- 深入浅出Node.js

    通过async控制并发

    既然并发太多会有问题,那么就控制下并发数量。 使用async这个库,通过里面的mapLimit方法控制同时发送的请求数目。

    async.mapLimit(
        urls, // url数组
        5, // 设置同时发送的请求数目上限
        function(url, callback) {
            fetch(url, callback); // 在fetch方法里,当页面返回结束后,调用callback函数,表明这个请求已经结束,这样就可以发送下一个请求。
        },
        (err, results) => {
            if (err) throw err;
            console.log(results);
        }
    );
    

    这样就可以获得所有章节,可以慢慢看。


    在上面提到了,Node服务每秒只能处理若干请求,即使内存,CPU和网络都没有饱和。

    参考文献

    Squeeze the juice out of Node— an exploration of how Node.js handles HTTP connections

    相关文章

      网友评论

          本文标题:2018-08-13 node 爬虫

          本文链接:https://www.haomeiwen.com/subject/acwybftx.html