Chrome插件模拟HTTP请求
准备工作
到 http:\/\/www.google.cn\/intl\/zh-CN\/chrome\/browser\/ 下载适合你的系统的Chrome并安装
Chrome脚本
新建一个文件夹,进入这个文件夹,新建三个文件 manifest.json
demo.html
demo.js
。
manifest.json
{
"manifest_version": 2,
"name": "模拟HTTP请求Demo",
"description": "以http://ssfw.xjtu.edu.cn/为例,讲解Chrome插件利用XMLHttpRequest模拟HTTP请求的方法",
"version": "1.0.0",
"browser_action": {
"default_popup": "demo.html"
},
"permissions": [
"https://cas.xjtu.edu.cn/*",
"http://ssfw.xjtu.edu.cn/*"
]
}
demo.html
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>用户名:</p>
<p><input type="text" id="username" autofocus></p>
<p>密码:</p>
<p><input type="password" id="password"></p>
<p><input type="submit" id="submit" value="开始"></p>
<ul id="output"></ul>
<script src="demo.js"></script>
</body>
</html>
demo.js
function output(str) {
var lines = str.split('\n');
for (var i = 0; i < lines.length; ++i) {
var li = document.createElement('li');
li.textContent = lines[i];
document.getElementById('output').appendChild(li);
}
}
var Request = {
newXHR: function(callback, errMsg) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
callback(xhr.responseText);
}
};
xhr.addEventListener("error", function () {
output(errMsg);
}, false);
return xhr;
},
get: function(url, callback, errMsg) {
var xhr = Request.newXHR(callback, errMsg);
xhr.open("GET", url, true);
xhr.send();
},
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('&');
var xhr = Request.newXHR(callback, errMsg);
xhr.open('POST', url, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(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(/ /g, ' ').replace(/[\s\x0B\xC2\xA0]+/g, ' ').trim();
},
username: '',
password: '',
main: function(username, password) {
Demo.username = username;
Demo.password = password;
Request.get('http://ssfw.xjtu.edu.cn/logout.portal', function(body) {
Request.get('https://cas.xjtu.edu.cn/logout', function(body) {
var matches
if (matches = Regex.match(/注销成功/g, body, '初始化失败')) {
output('初始化成功');
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 + '评教失败');
}
};
document.getElementById('submit').onclick = function() {
try {
Demo.main(document.getElementById('username').value, document.getElementById('password').value);
} catch (e) {
output(e.message);
}
};
运行插件
然后在Chrome中访问 chrome:\/\/extensions ,点击 加载已解压的扩展程序...
,选择刚才新建的那个文件夹即可。
现在浏览器右上角会有一个插件的图标,点击图标,弹出一个页面,即可使用。
代码说明
这个Chrome插件使用的XMLHttpRequest模拟HTTP请求、正则匹配,其他原理同Node.js。
初始化时执行了两个注销操作,目的是排除浏览器原有的Cookie的影响。
注意:由于js的异步非阻塞特性,多门科目评教共同进行,容易对服务器产生较大压力(某种意义上的DoS攻击),有时会出现评教失败现象。要解决这个问题可以重试几次提交,或者做成阻塞型,本示例不再继续讨论。