猿学—通过HTTP的HEADER完成各种骚操作

作为一名专业的切图工程师,我从来不care网页的header,最多关心Status Code是不是200。但是HEADER真的很重要啊,客户端从服务器端获取内容,首先就是通过HEADER进行各种沟通!HEADER可以帮助我们完成许多骚操作,提高网站的性能,用户的体验。好了让我们来feel一下。

初级骚操作

  • 多语言(Accept-Language)
  • 防盗链(Referer、Referered)
  • gzip,简单地说就是省流量(Accept-Encoding,Content-Encoding)

多语言

多语言就是一个网站可以实现多种语言的切换,这里不讨论建N个网站,一个网站也个语言。这里讨论如何智能返回用户所需的语言。

serverclient向server扔过去了Accept-Language接收对方的Accept-Language字段大概这样子zh,en-US;q=0.9,en;q=0.8开始处理,将字段变成带权重q的数组排序好大概长这样[{"name":"zh","q":1},{"name":"en-US","q":0.9},{"name":"en","q":0.8}]根据权重返回拥有的语言,有zh返回zh,没有zh就返回en-US万一我没有对方需要的语言包,怎么办?急,在线等!没办法了,只能给对方我们的官方(默认)语言发送,请接收您的ACCEPT语言已匹配这个网站挺上道的,虽然是国外网站,但知道我是中文我们没有你所在地区的语言包emmmm,这是火星文吗?

附赠多语言的简易实现版:

let languages = {

zh:{

title:"你好",

content:"同学"

},

en:{

title:"Hey",

content:"guy"

},

}

//设置默认语言,万一用户的语言我们不支持呢?

let defaultLanguage="zh"

let http = require('http');

function getLanguage(client_langs){

let finalLanguage=defaultLanguage

try{

if(client_langs){

//排序获取语言顺序

client_langs=client_langs.split(',').map(l=>{

let [name,q] = l.split(';');

q = q?Number(q.split('=')[1]):1

return {name,q}

}).sort((a,b)=>b.q-a.q);

//匹配服务器有的语言,并返回

for(let i = 0 ;i

let name= languages[i].name;

if(languages[name]){

finalLanguage=name;

break;

}

}

}

}catch(e){}

return languages[finalLanguage]

}

http.createServer(function (req,res) {

//获取客户端的语言

let client_langs = req.headers['Accept-Language'];

let lan=getLanguage(client_langs)

//将语言打印到客户端

res.end(`

${lan.title}

${lan.content}

`)

}).listen(3000);

防盗链

这个技术用的最多的应该就是对于图片的限制,只有本域名可以获取到,其他域名想都不要想。

serverclient在某网站上请求了一张图片通过Referer,Referered发现此网站域名不在我方白名单内此图片不提供给某网站此时po上了一张万用土支持正版请上我们网站

实现原理,此处我用iframe来做例子,其实原理很简单就是对比来源,要么和请求资源一致要么在白名单内,不然就拒绝。当然如果没有来源的情况下就直接放行,万一人家是单独打开的呢,不是盗链:

let http = require('http');

let fs = require('fs');

let url = require('url');

let path = require('path');

// 设置白名单

let whiteList = ['localhost:3000'];

http.createServer(function (req,res) {

//获取请求地址

let { pathname } = url.parse(req.url);

// 获取物理地址

let realPath = path.join(__dirname,pathname);

// 获取文件状态

fs.stat(realPath,function(err,statObj) {

if(err){

res.statusCode = 404;

res.end();

}else{

// 重点来了

let Referer = req.headers['Referer'] || req.headers['referred'];

//如果有来源

if(Referer){

//获取双方域名

let current = req.headers['host']

Referer = url.parse(Referer).host

console.log(current,Referer)

//如果域名相同活在白名单中,放行!

if (current === Referer || whiteList.includes(Referer)){

fs.createReadStream(realPath).pipe(res);

}else{

//不放行,此乃盗链!给你个眼神自行体会

fs.createReadStream(path.join(__dirname,'files/2.html')).pipe(res);

}

}else{

//没有来源,也放行。万一是单独打开的呢~

fs.createReadStream(realPath).pipe(res);

}

}

})

}).listen(3000);

gzip

现代浏览器很高级,已经可以接受压缩包了。佩服佩服。那么该如何传输压缩的网页呢?

serverclient向server扔过去了Accept-Encoding大概结构是这样的gzip, deflate, brget到了对方的用意,开始配置压缩如果支持压缩,先设置个头部Content-Encoding有很多种压缩方式,按照server优先支持的匹配在线压缩网页,成功后返回client欢欢喜喜省了流量,而且不影响体验

附赠建议代码,大家测试的时候,别忘了创建测试的html文件

let http = require('http');

//用于压缩文件所需的库

let fs = require('fs');

let path = require('path');

//压缩的库

let zlib = require('zlib');

http.createServer(function (req,res) {

//获取客户端接受的压缩方式

let rule = req.headers['Accept-Encoding'];

// 创建原文件可读流

let originStream=fs.createReadStream(path.join(__dirname, '1.html'));

if(rule){

// 啊啊啊!正则是个坎,我怕我是跨不过去了。

if(rule.match(/\bgzip\b/)){

//如果支持压缩!一定要设置头部!

res.setHeader('Content-Encoding','gzip');

originStream=originStream.pipe(zlib.createGzip())

} else if (rule.match(/\bdeflate\b/)){

res.setHeader('Content-Encoding', 'deflate');

originStream=originStream.pipe(zlib.createDeflate())

}

}

// 输出处理后的可读流

originStream.pipe(res)

}).listen(3000);

中级操作

初级操作大多只需要靠配置HEADER即可以实现,中级我们当然要难一点,大多需要client和server打配合。

  • client给server发送内容(Content-Type、Content-Length)
  • client从server获取内容(Range、Content-Range)
  • client爬虫,抓取网页

client给server发送内容

serverclient给你了一串数据,你给处理下没头没脑,谁知道你要做什么,请设置好HEADER好吧,告诉你Content-Type和Content-Length可以可以,数据的内容类型是长度是很必要的把数据传给你了,你看一下收到~监听收到的数据是一组Buffer接受完毕,合并Buffer根据Content-Type对数据进行处理格式化数据,end

Server代码

let http = require('http');

let server = http.createServer();

let arr=[]

server.on('request', (req, res)=>{

req.on('data',function (data) {

//把获取到的Buffer数据都放入熟组

arr.push(data);

});

req.on('end',function() {

// 请求结束了,好了可以开始处理断断续续收到的Buffer了

// 合并buffer

let r = Buffer.concat(arr).toString();

if (req.headers['content-type'] === 'x-www-form-urlencoded'){

let querystring = require('querystring');

r = querystring.parse(r); // a=1&b=2然后格式化

console.log("querystring",r);

} else if (req.headers['content-type'] === 'application/json'){

//听说是JSON格式的

console.log("json",JSON.parse(r));

} else{

//没有格式?那原来是啥就是啥吧。

console.log("no type",r);

}

arr=[]

res.end('结束了!');

});

})

server.listen(3000,()=>{

console.log(`server start`);

});

Client代码

// 设置请求地址的配置

let opts = {

host:'localhost',

port:3000,

path:'/',

// 头部设置很重要,头部设置很重要,头部设置很重要

headers:{

'Content-Type':'x-www-form-urlencoded',

//长度超过3就没有人理你了

"Content-Length":7

}

}

let http = require('http');

let client = http.request(opts,function (res) {

res.on('data',function (data) {

console.log(data);

})

});

client.end("a=1&b=2");

client从server获取部分内容

serverclient我想要资源的部分内容可以啊,告诉我范围我放在HEADER中的Range了,bytes=0-3Content-Range:bytes 0-3/7,请接受,此文件一共8字节,前3字节已经给你了好的,那么把接下来的给我吧,bytes=4-7给你给你都给你end

大家都发现了吧,这样的range获取数据,完全是断点续传的简陋版啊!不过这边有一个点容易犯错就是文件大小的计算,因为文件字节的位置是按照0开始算,所以range的全范围都是0~size-1/size-1,大家注意下。

server 端

let http = require('http');

let fs = require('fs');

let path = require('path');

// 当前要下载的文件的大小

let size = fs.statSync(path.join(__dirname, 'my.txt')).size;

let server = http.createServer(function (req, res) {

let range = req.headers['range']; //获取client请求访问的部分内容

if (range) {

let [, start, end] = range.match(/(\d*)-(\d*)/);

start = start ? Number(start) : 0;

end = end ? Number(end) : size - 1; // 10个字节 size 10 (0-9)

console.log(`bytes ${start}-${end}/${size - 1}`)

res.setHeader('Content-Range', `bytes ${start}-${end}/${size - 1}`);

fs.createReadStream(path.join(__dirname, 'my.txt'), { start, end }).pipe(res);

} else {

// 会把文件的内容写给客户端

fs.createReadStream(path.join(__dirname, 'my.txt')).pipe(res);

}

});

server.listen(3000);

client端

let opts = {

host:'localhost',

port:3000,

headers:{}

}

let http = require('http');

let start = 0;

let fs = require('fs');

function download() {

//分流下载,部分下载

opts.headers.Range = `bytes=${start}-${start+3}`;

start+=4;

let client = http.request(opts,function (res) {

let total = res.headers['content-range'].split('/')[1];

res.on('data',function (data) {

fs.appendFileSync('./download.txt',data);

});

res.on('end',function () {

//结束之后,1s之后再下载

setTimeout(() => {

console.log(start,total)

if (start <= total)

download();

}, 1000);

})

});

client.end();

}

download()

client抓取网页内容,简易爬虫

这一块的操作其实很简单,只要建一个请求获取到网页就可以了。

难点在于:如何将游游有用信息剥离网页,过滤掉无用信息。

我这里抓去了百度的娱乐版,百度还算良心,是utf8的,不然就要乱码了。

let http = require('http');

let opts = {

host:'news.baidu.com',

path:'/ent'

}

//创建一个请求,获取网站内容

let client = http.request(opts,function (r) {

let arr= [];

//资源不可能一次下载完成,因此每次获取到数据都要push到arr中

r.on('data',function (data) {

arr.push(data);

});

r.on('end',function() {

//合并资源

let result = Buffer.concat(arr).toString();

//对资源进行处理,可以是变成我这样的对象,之后不管做什么处理都很方便

let content = result.match(/

    (?:[\s\S]*?)/img).toString().match(/
  • (?:[\s\S]*?)/img);

    content=content.map((c)=>{

    let href=/


分享到:


相關文章: