Node.js模拟HTTP请求

准备工作

https:\/\/nodejs.org\/en\/download\/ 下载适合你的系统的 Node.js 并安装(最好选择安装器安装,因为内置了npm包管理工具

目录结构

  1. 新建一个文件夹作为我们的项目目录

  2. 在项目目录下新建两个文件 package.jsondemo.js 即可

安装依赖包

打开 package.json 写入如下内容

{
  "name": "http-request-simulation",
  "version": "1.0.0",
  "description": "HTTP Request Simulation in Node.js",
  "main": "demo.js",
  "dependencies": {
    "request": "^2.79.0"
  },
  "devDependencies": {},
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/ganlvtech/http-request-simulation/"
  },
  "keywords": [
    "http",
    "request",
    "simulation",
    "demo"
  ],
  "author": "Ganlv",
  "license": "AGPL-3.0"
}

然后在项目目录下打开命令行窗口,执行 npm install 等待所有依赖包自动安装成功即可

Node.js脚本

打开 demo.js 写入如下内容

var output = console.log;
var request = require('request').defaults({ jar: true });
var Request = {
    get: function(url, callback, errMsg) {
        request.get(url, function(error, response, body) {
            if (error) {
                console.error(errMsg);
            } else {
                callback(body);
            }
        });
    },
    post: function(url, form, callback, errMsg) {
        var params = [];
        for (var i in form) {
            params.push({
                name: i,
                value: form[i]
            });
        }
        Request.postParams(url, params, callback, errMsg);
    },
    postParams: function(url, params, callback, errMsg) {
        var body = [];
        for (var i = 0; i < params.length; ++i) {
            body.push(encodeURIComponent(params[i].name) + '=' + encodeURIComponent(params[i].value));
        }
        body = body.join('&');
        request({
                method: 'POST',
                url: url,
                headers: {
                    'content-type': 'application/x-www-form-urlencoded'
                },
                body: body
            },
            function(error, response, body) {
                if (error) {
                    console.error(errMsg);
                } else {
                    callback(body);
                }
            });
    },
};
var Regex = {
    match: function(regexp, str, errMsg) {
        var matches;
        if ((matches = regexp.exec(str)) === null) {
            output(errMsg);
        }
        return matches;
    },
    matchAll: function(regexp, str) {
        var result = [];
        var matches;
        while ((matches = regexp.exec(str)) != null) {
            result.push(matches);
        }
        return result;
    }
};
var Demo = {
    htmlToText: function(html) {
        return html.replace(/<[\s\S]*?>/g, ' ').replace(/&nbsp;/g, ' ').replace(/[\s\x0B\xC2\xA0]+/g, ' ').trim();
    },
    username: '',
    password: '',
    main: function(username, password) {
        Demo.username = username;
        Demo.password = password;
        Demo.loadCas();
    },
    loadCas: function() {
        Request.get('https://cas.xjtu.edu.cn/login?service=http%3A%2F%2Fssfw.xjtu.edu.cn%2Findex.portal', function(body) {
            var matches
            if (matches = Regex.match(/name="lt" value="(.*?)"[\s\S]*?name="execution" value="(.*?)"[\s\S]*?name="_eventId" value="(.*?)"/g, body, '获取登录凭证失败')) {
                var lt = matches[1];
                var execution = matches[2];
                var _eventId = matches[3];
                output('获取登录凭证成功');
                Demo.loginCas(lt, execution, _eventId);
            }
        }, '读取CAS登录页面失败');
    },
    loginCas: function(lt, execution, _eventId) {
        Request.post('https://cas.xjtu.edu.cn/login?service=http%3A%2F%2Fssfw.xjtu.edu.cn%2Findex.portal', {
            username: Demo.username,
            password: Demo.password,
            code: '',
            lt: lt,
            execution: execution,
            _eventId: _eventId
        }, function(body) {
            var matches
            if (matches = Regex.match(/url=(.*?)"/g, body, '用户名或密码错误')) {
                var ssfwUrl = matches[1];
                output('登录CAS成功');
                Demo.loginSsfw(ssfwUrl);
            }
        }, '登录CAS失败');
    },
    loginSsfw: function(ssfwUrl) {
        Request.get(ssfwUrl, function(body) {
            var matches
            if (matches = Regex.match(/<li style="color:#fff;font-weight:bold;">(.*?)<\/li>/g, body, '获取师生服务首页失败')) {
                var hello = matches[1];
                output('登录师生服务系统成功');
                output(hello);
                Demo.listCourse();
            }
        }, '登录师生服务系统失败');
    },
    listCourse: function() {
        Request.get('http://ssfw.xjtu.edu.cn/index.portal?.p=Znxjb20ud2lzY29tLnBvcnRhbC5zaXRlLmltcGwuRnJhZ21lbnRXaW5kb3d8ZjExNjF8dmlld3xub3JtYWx8YWN0aW9uPXF1ZXJ5', function(body) {
            var matches
            if (matches = Regex.match(/<tbody>([\s\S]*?)<\/tbody>/g, body, '解析课程列表失败')) {
                var table = matches[1];
                output('读取课程列表成功');
                var courses = Regex.matchAll(/<tr[\s\S]*?>([\s\S]*?)<a href="(.*?)">(.*?)<\/a>/g, table);
                for (var i = 0; i < courses.length; ++i) {
                    var course = courses[i];
                    var desc = Demo.htmlToText(course[1]).replace(/ /, '\t');
                    var action = course[3];
                    if (action !== '评教') {
                        output('================================================================================');
                        output(desc);
                    } else {
                        var url = 'http://ssfw.xjtu.edu.cn/index.portal' + course[2];
                        try {
                            Demo.loadCourse(url, desc);
                        } catch (e) {
                            output(e.message);
                        }
                    }
                }
            }
        }, '读取课程列表失败');
    },
    loadCourse: function(url, desc) {
        var msgPrefix = '================================================================================\n' + desc + '\n';
        Request.get(url, function(body) {
            var matches;
            if (matches = Regex.match(/post" action="(.*?)"/, body, msgPrefix + '获取提交网址失败')) {
                var url = 'http://ssfw.xjtu.edu.cn/index.portal' + matches[1];
                var params = [];
                var errMsg = msgPrefix + '解析课程失败';
                params.push({ name: 'wid_pgjxb', value: Regex.match(/wid_pgjxb" value="(.*?)"/g, body, errMsg)[1] });
                params.push({ name: 'wid_pgyj', value: Regex.match(/wid_pgyj" value="(.*?)"/g, body, errMsg)[1] });
                params.push({ name: 'type', value: '2' });
                params.push({ name: 'sfytj', value: Regex.match(/sfytj" value="(.*?)"/g, body, errMsg)[1] });
                params.push({ name: 'pjType', value: Regex.match(/pjType" value="(.*?)"/g, body, errMsg)[1] });
                params.push({ name: 'wid_pjzts', value: Regex.match(/wid_pjzts" value="(.*?)"/g, body, errMsg)[1] });
                params.push({ name: 'status', value: Regex.match(/status" value="(.*?)"/g, body, errMsg)[1] });
                params.push({ name: 'ztpj', value: '很好' });
                params.push({ name: 'sfmxpj', value: Regex.match(/sfmxpj" value="(.*?)"/g, body, errMsg)[1] });
                var trs = Regex.matchAll(/教师评价([\s\S]*?)<\/tr>/g, body);
                for (var i = 0; i < trs.length; ++i) {
                    var tr = trs[i][1];
                    params.push({ name: 'zbbm', value: Regex.match(/zbbm" type="hidden" value="(.*?)"/g, tr)[1] }, errMsg);
                    var matches = Regex.match(/(wid_.*?)" type="hidden" value="(.*?)"/g, tr, errMsg);
                    params.push({ name: matches[1], value: matches[2] });
                    matches = Regex.match(/(qz_.*?)" type="hidden" value="(.*?)"/g, tr, errMsg);
                    params.push({ name: matches[1], value: matches[2] });
                    var scoreMatches = Regex.matchAll(/(pfdj_.*?)"  value="(.*?)"/g, tr);
                    var j = parseInt(Math.random() + 0.5);
                    params.push({ name: scoreMatches[j][1], value: scoreMatches[j][2] });
                }
                params.push({ name: 'pgyj', value: '很好' });
                params.push({ name: 'actionType', value: '2' });
                Demo.evalCourse(url, desc, params);
            }
        }, '读取课程信息失败');
    },
    evalCourse: function(url, desc, params) {
        var msgPrefix = '================================================================================\n' + desc + '\n';
        Request.postParams(url, params, function(body) {
            output(msgPrefix + '评教成功');
        }, msgPrefix + '评教失败');
    }
};
try {
    Demo.main('username', 'password');
} catch (e) {
    console.error(e.message);
}

把最后改成自己的用户名密码,然后保存

在该文件夹下打开命令行,输入 node demo.js ,回车。

代码解释

代码中主要使用了 request.js 包发送HTTP请求,在新建request对象时设置 jar:true 参数即可自动管理cookie,使用了JavaScript内置的 RegExp 正则表达式提取内容,具体说明请参考相关文档。

由于js的异步非阻塞特性,多门科目评教共同进行,容易对服务器产生较大压力(某种意义上的DoS攻击),有时会出现评教失败现象。要解决这个问题可以重试几次提交,或者做成阻塞型,本示例不再继续讨论。

results matching ""

    No results matching ""