• node.js入门(express和superagent的使用)爬博客园和知乎数据,并实时显示到前端


    先来看一下结果:http://39.105.101.122:81/html/home.html

    用到的东西:前端(H5盒子模型+vue+jquery)后端node.js(express+superagent),这里没有用到数据库和其他存储数据的文件。前端发送ajax请求,后端用express接收请求,然后通过superagent去请求博客园或者知乎的网站,拿到数据之后看情况对数据进行处理,然后再将数据返回给前端。

    之前做过快半年的前端实习,主要是做H5方面的。毕业之后在新的公司里面主要是写java的爬虫,用的是htmlunit。这个东西相当于是用代码模拟浏览器来访问网站,可以模拟点击按钮的操作,用起来还是非常方便的。后来就想学一下node.js,看看用它是怎么来写后端,怎么写爬虫的。

    首先是下载并安装node.js,新建一个文件夹作为一个新项目,进入这个文件夹,按住Shift键然后点击鼠标右键,点击“在此处打开命令窗口”。这样省去了切换目录的麻烦。然后在命令窗口里面输入npm init,回车,看情况填写信息,也可以一路回车默认。然后安装这里用到的几个模块:express、superagent、querystring、cheerio、eventproxy,比如在命令窗口输入npm install express --save,然后回车就可以在当前项目内安装express模块。安装完这几个模块之后,在当前目录下新建一个js文件:server.js,里面存放后台的代码,然后在当前目录下新建一个文件夹webapp,里面存放前端的东西:html、css、js、图片、还有其他用到的vue、jquery。

    express的作用是可以创建一个后台的服务器,接收来自前端的请求,并且发送数据给前端。

    用法如下:

    var express = require('express');
    var app = express();
    app.use(express.static('webapp'));
    
    var server = app.listen(8081, function () {
    
      var host = server.address().address
      var port = server.address().port
    
      console.log("应用实例,访问地址为 http://%s:%s", host, port)
    
    })
    app.use(express.static('webapp'));加上这句代码,浏览器里面就可以访问webapp这个文件夹里面的内容了。在命令窗口里面输入node server.js然后回车,在浏览器里面输入http://localhost:8081/webapp里面的内容就可以访问了。
    接下来看一下前后端通信的代码是怎样的:
    前端的请求如下所示:
    $.ajax({
        url: 'http://你的ip地址:8081/getData',
        type: 'POST',
        data: {
            num: $this.num,
        },
        success: function(data){
            var tempData = data.ret;
            for( var i = 0; i < tempData.length; i++ ){
                var tempItem = {
                    img: tempData[i].itemCover,
                    hasImg: tempData[i].itemCover==undefined?false:true,
                    text: tempData[i].itemText,
                    num: "139",
                    title: tempData[i].itemTitle,
                    url: tempData[i].itemLink
                }
                $this.items.push(tempItem);
            }
            $this.num++;
            $this.working = false;
            $this.loading = false;
            // localStorage.items=JSON.stringify($this.items);
            // localStorage.num=$this.num;
        }
    })

    server.js里面对应接收这个请求的方法如下所示:

    app.post('/getData', function (req, res) {
        req.on('data', function(data) {
            var currentData = ""+data;
            var tempData = qs.parse(currentData);
            var index = tempData.num;
            var postData = [];
            var tempData = {
                value: "s"
            };
            postData.push(tempData);
            res.header("Access-Control-Allow-Origin", "*");
            res.json({
                success: 1,
                ret: postData
            })
        });
    })

    使用req.on('data',function(data){...})就可以接收到前端post过来的数据,然后用querystring的parse方法可以将数据解析成json格式,这样就能读取相应的数据。然后用res.json({...})就可以将数据返回给前端。res.header("Access-Control-Allow-Origin", "*")这句代码可以解决跨域的问题,比如说如果没有这句代码的话,在浏览器里面用localhost访问页面,前端的ajax请求是不会成功的,因为他会提示跨域的错误,加上这句代码之后就可以访问成功。

    接下来看一下怎样用superagent来爬数据:

    superagent.get("https://www.cnblogs.com/").end(function(err,pre){
        var $ = cheerio.load(pre.text);
        for( var i = 0; i < $(".post_item_body").length; i++ ){
           ...
        }
    })

    用superagent来请求博客园首页的网址,然后pre.text就是博客园首页的html内容了,但是由于它是字符串,所以这里需要用到cheerio.load方法。然后这个$就和jquery里面的$一样了,可以直接用jquery里面的方法来获取页面的元素。在这里呢,推荐看https://www.cnblogs.com/coco1s/p/4954063.html这篇博客,这里面详细的描述了怎样用superagent来爬取博客园的数据。

    爬取博客园的数据是成功了,但是我还想试一下其他的网站,平时看知乎比较多,就爬知乎吧。但是我发现用之前爬取博客园的方法去爬知乎的话,返回的是一个登陆的页面。所以这里就需要把cookie添加进去了。

    superagent.get("https://www.zhihu.com/")
    .set('Cookie': '...')
    .end(function(err,obj){
        if(err){return;
        }
        var tempData = {
            value: obj.text,
        };
        res.header("Access-Control-Allow-Origin", "*");
        res.json({
            success: 1,
            ret: tempData
        })
    })

    这样的话,首页是进去了,但是在浏览器里面下滑的话还会有很多条目出来的,要想爬到这部分后来加载出来的条目,就需要分析网页的请求了。在浏览器里面打开知乎,然后按F12进入控制台,点击network,然后在网页里面下滑,就会发现network里面有很多请求,其中有一条请求是这样的:

    可以看到这条请求返回的数据是很多的,它应该就是我们需要的请求了。点击上面的Headers,然后找到请求头,如下所示:

    将请求头里面的这些信息塞到superagent的set函数里面,并且将Headers下面的Request URL替换之前的url,如下所示:

    superagent.get("https://www.zhihu.com/api/v3/feed/topstory?action_feed=True&limit=10&session_token=a28a2fdc4537d7d14cbd46bcaf16912a&action=down&after_id=9&desktop=true"")
    .set({
        'Cookie': '_zap=67cf877b-b4c4-4798-9e23-d3ef6553d2f9; q_c1=fae7f35742874cd2b0356a5c321d085f|1501321325000|1501321325000; aliyungf_tc=AQAAAPsawBFU6QgABtBbcXShs11WbLol; q_c1=fae7f35742874cd2b0356a5c321d085f|1512744706000|1501321325000; _xsrf=51039ae2b79b7ba81f1da8eba41f2b62; r_cap_id="MDc0MTM0MWZlNWY2NDc2Mzk1YTdhOWE0MWFkZDQzYjA=|1512744706|b5b2a49f82637c6408347fe8941ea3315f5f5be3"; cap_id="NjI5ZDM3YTIzY2ZjNDhhOWJlZWM2MjIxNjQ0MjNmOTE=|1512744706|210d6f4e2c44900b25afebff79b79d9aac13dda0"; d_c0="ABBCbcS6zQyPTk28d91f51hdLtLQ1xQPe3s=|1512744707"; l_n_c=1; z_c0=Mi4xRDVobkFRQUFBQUFBRUVKdHhMck5EQmNBQUFCaEFsVk5JUFVYV3dBaC1ZVktoUjFoRExNMEI1Mkp5a1BWSHpUaERn|1512744736|1d19657b025b92339fecc550c2fc8606f0f5ba48; __utma=155987696.856620421.1512833230.1512833230.1512833230.1; __utmc=155987696; __utmz=155987696.1512833230.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _xsrf=51039ae2b79b7ba81f1da8eba41f2b62',
        'Host': 'www.zhihu.com',
        'Referer': 'https://www.zhihu.com/',
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
        'x-api-version': '3.0.53',
        'x-udid': 'ABBCbcS6zQyPTk28d91f51hdLtLQ1xQPe3s=',
        'Connection': 'keep-alive',
        'authorization': 'Bearer Mi4xRDVobkFRQUFBQUFBRUVKdHhMck5EQmNBQUFCaEFsVk5JUFVYV3dBaC1ZVktoUjFoRExNMEI1Mkp5a1BWSHpUaERn|1512744736|1d19657b025b92339fecc550c2fc8606f0f5ba48'
    })
    .end(function(err,obj){
        if(err){
            return;
        }
        var tempData = {
            value: obj.text,
        };
        res.header("Access-Control-Allow-Origin", "*");
        res.json({
            success: 1,
            ret: tempData
        })
    })

    其实看代码可以发现我并没有把accept、Accept-Encoding、Accept-Language这三项加进去,因为我发现把请求头里面所有的信息塞进去之后,会返回错误信息,然后我试着删除掉一些请求信息,结果发现把这三项删除之后就可以请求成功。不清楚原因是什么。

    这样的请求返回的结果和浏览器里面看到的返回的结果是一样的,中文字符是是经过转义的,使用JSON.parse函数可以将这个经过转义的字符串变为可用的json对象。然后分析这个json对象,从里面取我们需要的数据。

    完整的server.js代码如下所示:

    var express = require('express');
    var superagent = require('superagent');
    var qs = require("querystring");
    var cheerio = require('cheerio');
    var eventproxy = require('eventproxy');
    var ep = new eventproxy();
    var app = express();
    
    app.use(express.static('webapp'));
    
    app.post('/getData', function (req, res) {
        req.on('data', function(data) {
            var currentData = ""+data;
            var tempData = qs.parse(currentData);
            var index = tempData.num;
            console.log(tempData.num);
            var urlArr = [];
            for( var i = index*2-1; i <= index*2; i++ ){
                 var url = "http://www.cnblogs.com/?CategoryId=808&CategoryType=%22SiteHome%22&ItemListActionName=%22PostList%22&PageIndex="+i+"&ParentCategoryId=0";
                urlArr.push(url);
            }
            var postData = [];
            var startTime = new Date().getTime();
            for( var j = 0; j < urlArr.length; j++ ){
                superagent.get(urlArr[j]).end(function(err,pre){
                    var $ = cheerio.load(pre.text);
                    for( var i = 0; i < $(".post_item_body").length; i++ ){
                        var titleElement = $(".post_item_body>h3>a").eq(i);
                        var title = titleElement.text();
                        var sourceUrl = titleElement.attr("href");
                        var textElement = $(".post_item_summary").eq(i);
                        var text = textElement.text();
                        var Element = $(".post_item_summary").eq(i);
                        var coverElement = Element.find('img');
                        var cover = "";
                        if( coverElement!=undefined&&coverElement!=null ){
                            if( coverElement.attr("src")==undefined ){
                                cover = undefined;
                            }else{
                                cover = "https:"+coverElement.attr("src");
                            }
                        }
                        var tempData = {
                            itemTitle: title,
                            itemLink: sourceUrl,
                            itemText: text,
                            itemCover: cover
                        }
                        postData.push(tempData);
                    }
                    ep.emit("dataEvent");
                })
            }
            ep.after('dataEvent',2,function(){
                res.header("Access-Control-Allow-Origin", "*");
                res.json({
                    success: 1,
                    ret: postData
                })
                var endTime = new Date().getTime();
                var time = endTime-startTime;
                console.log(time);
            });
        });
    })
    
    app.post('/getZhihuData', function (req, res) {
        req.on('data', function(data) {
            var currentData = ""+data;
            var tempData = qs.parse(currentData);
            var index = tempData.num;
            console.log(tempData.num);
            
            var postData = [];
            var startTime = new Date().getTime();
            var afterId = index*10-1;
            var url = "https://www.zhihu.com/api/v3/feed/topstory?action_feed=True&limit=10&session_token=a28a2fdc4537d7d14cbd46bcaf16912a&action=down&after_id="+afterId+"&desktop=true";
            superagent.get(url)
            .set({
                'Cookie': '_zap=67cf877b-b4c4-4798-9e23-d3ef6553d2f9; q_c1=fae7f35742874cd2b0356a5c321d085f|1501321325000|1501321325000; aliyungf_tc=AQAAAPsawBFU6QgABtBbcXShs11WbLol; q_c1=fae7f35742874cd2b0356a5c321d085f|1512744706000|1501321325000; _xsrf=51039ae2b79b7ba81f1da8eba41f2b62; r_cap_id="MDc0MTM0MWZlNWY2NDc2Mzk1YTdhOWE0MWFkZDQzYjA=|1512744706|b5b2a49f82637c6408347fe8941ea3315f5f5be3"; cap_id="NjI5ZDM3YTIzY2ZjNDhhOWJlZWM2MjIxNjQ0MjNmOTE=|1512744706|210d6f4e2c44900b25afebff79b79d9aac13dda0"; d_c0="ABBCbcS6zQyPTk28d91f51hdLtLQ1xQPe3s=|1512744707"; l_n_c=1; z_c0=Mi4xRDVobkFRQUFBQUFBRUVKdHhMck5EQmNBQUFCaEFsVk5JUFVYV3dBaC1ZVktoUjFoRExNMEI1Mkp5a1BWSHpUaERn|1512744736|1d19657b025b92339fecc550c2fc8606f0f5ba48; __utma=155987696.856620421.1512833230.1512833230.1512833230.1; __utmc=155987696; __utmz=155987696.1512833230.1.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); _xsrf=51039ae2b79b7ba81f1da8eba41f2b62',
                'Host': 'www.zhihu.com',
                'Referer': 'https://www.zhihu.com/',
                'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
                'x-api-version': '3.0.53',
                'x-udid': 'ABBCbcS6zQyPTk28d91f51hdLtLQ1xQPe3s=',
                'Connection': 'keep-alive',
                'authorization': 'Bearer Mi4xRDVobkFRQUFBQUFBRUVKdHhMck5EQmNBQUFCaEFsVk5JUFVYV3dBaC1ZVktoUjFoRExNMEI1Mkp5a1BWSHpUaERn|1512744736|1d19657b025b92339fecc550c2fc8606f0f5ba48'
            })
            .end(function(err,obj){
                if(err){
                    var X = JSON.parse(err);
                    var Y = JSON.stringify(X);
                    console.log(Y);
                    console.log("err");
                    return;
                }
                var tempData = {
                    value: obj.text,
                };
                res.header("Access-Control-Allow-Origin", "*");
                res.json({
                    success: 1,
                    ret: tempData
                })
            })
            
        });
    })
    
    var server = app.listen(8081, function () {
    
      var host = server.address().address
      var port = server.address().port
    
      console.log("应用实例,访问地址为 http://%s:%s", host, port)
    
    })

    在这里面还用到了eventproxy这个东西,因为node.js可以异步执行,所以在一个循环里面使用的superagent也是异步执行的,它不会等当前循环执行完之后再执行下一个循环,所以在这种情况下,我们不知道这个for循环里面的所有的superagent什么时候可以执行完。所以就有了eventproxy这个东西。它相当于是一个监听器,在一个循环的末尾使用ep.emit("dataEvent");这个函数,然后就可以触发ep.after函数,在触发的次数达到设定的次数之后,after函数里面的代码就会执行。

    这里前端的细节就不再赘述了,主要实现的功能就是前端请求数据的时候,后端就去爬取数据并返回给前端,没有用到数据库或者是存储数据的文件。前端也实现了上划加载,loading动画的功能是我之前写过的一个loading动画,突然想到可以用到这里就搬来了。loading动画可以看这里:http://www.cnblogs.com/huizit1/p/5470587.html

    前端除了vue和jquery就没有使用其他的插件或者库了。完整的代码在这里:https://github.com/swang23/NodeWebParser

    项目代码克隆到本地之后,要想跑起来需要在home.js里面把ajax请求的url里面的ip地址换成自己电脑的ip地址(命令窗口里面:ipconfig回车查看ipv4地址),然后node server.js,在浏览器里面输入http://localhost:8081/html/home.html即可查看结果。

  • 相关阅读:
    vb移动窗体的代码
    vb得到一个进程的启动参数?
    UTF8方式读写文件的模块
    JavaScript中Window.event详解
    vb设置窗体不可移动
    一拖二
    实习第一天
    사랑해
    决定考研
    Eclipse快捷键
  • 原文地址:https://www.cnblogs.com/huizit1/p/8025816.html
Copyright © 2020-2023  润新知