学术应用使用node-http-proxy集成谷歌学术「建议收藏」

学术应用使用node-http-proxy集成谷歌学术「建议收藏」搞科研的同学肯定离不开谷歌学术,谷歌学术搜索是文献搜索下载一大利器。之前实验室开发了一款学术应用,遗留了历史问题,就是没有解决文献搜索的功能,而这个任务最后落在我的身上。我采用的方案就是集成谷歌学术,但是国内的网络环境,你懂的,自然状态下根本就访问不了谷歌学术的,你得翻墙才能访问。你不能期望使用你开发的学术应用都能翻墙访问谷歌学术(虽然搞科研的人电脑翻墙软件肯定都准备好了!),所以呢我还要给谷歌学术

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE使用 1年只要46元 售后保障 童叟无欺

搞科研的同学肯定离不开谷歌学术,谷歌学术搜索是文献搜索下载一大利器。之前实验室开发了一款学术应用,遗留了历史问题,就是没有解决文献搜索的功能,而这个任务最后落在我的身上。我采用的方案就是集成谷歌学术,但是国内的网络环境,你懂的,自然状态下根本就访问不了谷歌学术的,你得翻墙才能访问。你不能期望使用你开发的学术应用都能翻墙访问谷歌学术(虽然搞科研的人电脑翻墙软件肯定都准备好了!),所以呢我还要给谷歌学术搭建一个代理。不仅要集成谷歌学术一键搜索并下载,还要能导入和分享文献到自己开发的应用,获取文献的BIBTEX并导入到自己的学术应用中,就是要在代理中hack谷歌学术的原始响应,注入相关脚本。(关于BIBTEX可以去维基百科脑补一下。)
废话少说,放效果图过来先:
学术应用使用node-http-proxy集成谷歌学术「建议收藏」
学术应用使用node-http-proxy集成谷歌学术「建议收藏」

谷歌学术代理

谷歌学术代理默认采用的是node-http-proxy模块实现的,node-http-proxy的使用见博客用node-http-proxy搭建谷歌代理node-http-proxy的Github地址。有人读到这已经急,Talk is cheap, Show me the code! 好好,博主这就废话少说,放码过来。新鲜热乎的代码如下:

/** * Created by Jaye on 15/7/11. */
var config = require('./config');
var http = require('http');
var https = require('https');
var httpProxy = require('http-proxy');
var url = require('url');
var cookie = require('./cookie');
var util = require('util');
var modifyHtml = require('./modify_html');
var querystring = require('querystring');
var PROXY_PORT = config.proxyPort;
var proxy, server;
var cookieArr = [];
var hl = 'zh-CN';
var injected = "";
// 创建代理服务器
proxy = httpProxy.createProxy();
//处理error
proxy.on('error', function (err,req,res) { 

res.writeHead(500, {
'Content-Type': 'text/plain'
});
res.end('Something went wrong. And we are reporting a custom error message.' + err.message);
});
server = http.createServer(function (req, res) { 

//载入需要注入的内容
injected = fs.readFileSync('./inject.html', 'utf8');
//解析语言,没有设置默认为zh-CN,重构时可以用url_auth来代替
var query = querystring.parse(url.parse(req.url).query);
if(query){
hl = query.hl?query.hl:'zh-CN';
}
//todo:加入权限认证,调用url_auth中的urlAuth函数进行判断,如果auth为true放行,否则拦截 
// var finalUrl = req.url,
var finalUrl = 'https://scholar.google.com',
finalAgent = null,
parsedUrl = url.parse(finalUrl);
if (parsedUrl.protocol === 'https:') {
finalAgent = https.globalAgent;
} else {
finalAgent = http.globalAgent;
}
//
proxy.web(req, res, {
target: finalUrl,
agent: finalAgent,
headers: { host: parsedUrl.hostname,
},
prependPath:false,
xfwd:true,
hostRewrite:config.proxyHost+':'+config.proxyPort,//设置重定向地址,
protocolRewrite: 'http'//设置重定向协议
});
});
proxy.on('proxyReq',function(proxyReq,req,res){ 

//如果不去掉这个头字段,浏览器报330错误,无法解码
if(proxyReq._headers){
if(proxyReq._headers['accept-encoding']){
proxyReq._headers['accept-encoding'] = '';
}
}
});
/** * [在响应返回到客户端时,重写html并注入js脚本] * @param {[type]} proxyRes [description] * @param {[type]} request [description] * @param {[type]} response [description] * @return {[type]} [description] */
proxy.on('proxyRes',function(proxyRes,request,response){ 

if(proxyRes.headers && proxyRes.headers[ 'set-cookie' ]){
cookieArr =  cookie.parseGoogleCookies(proxyRes.headers['set-cookie']); 
proxyRes.headers['set-cookie']=cookieArr;
} 
//inject js,rewrite html body
if( proxyRes.headers &&
proxyRes.headers[ 'content-type' ] &&
proxyRes.headers[ 'content-type' ].match( 'text/html' ) ) {
var _end = response.end,
chunks,
_writeHead = response.writeHead,
_write = response.write;
response.writeHead = function(){ 

if( proxyRes.headers && proxyRes.headers[ 'content-length' ] ){
response.setHeader(
'content-length',
parseInt( proxyRes.headers[ 'content-length' ], 10 ) + injected.length
);
}
//不设置可能出现少量乱码
response.setHeader( 'transfer-encoding', proxyRes.headers['transfer-encoding'] );
// Disable cache for all http as well
response.setHeader( 'cache-control', 'no-cache' );
_writeHead.apply( this, arguments );
};
response.write = function( data ) { 

if( chunks ) {
chunks += data;
} else {
chunks = data;
}
};
response.end = function() { 

if( chunks && chunks.toString ) {
_end.apply( this, [ modifyHtml( chunks.toString(),hl , injected) ] );
}else {
_end.apply( this, arguments );
}    
};
}
});
console.log('listening on port ' + PROXY_PORT);
server.listen(PROXY_PORT);

代理的功能跟谷歌代理的动能类似,这里不再赘述。谷歌代理见博文用node-http-proxy搭建谷歌代理,下面我主要讲修改cookie来启用谷歌学术的设置功能,有设置功能的谷歌学术才够高大上!谷歌学术的设置是通过cookie来做的,需要完整的功能必须解决cookie的问题。我下面的例子是设置显示导入链接。谷歌学术的默认设置是隐藏导入链接的,需要到设置里勾选显示导入[BibTex、EndNote、RefMan、RefWorks]的链接,见下图:

  • 点击右上角我的著作引用情况右边的三角图标,选择设置
    学术应用使用node-http-proxy集成谷歌学术「建议收藏」
  • 然后选择显示导入BibTeX的链接
    学术应用使用node-http-proxy集成谷歌学术「建议收藏」
  • 点击保存后自动跳转到搜索结果,将会显示导入BibTeX(谷歌学术的原始显示)
    学术应用使用node-http-proxy集成谷歌学术「建议收藏」
    修改谷歌学术的cookie技术原理挺简单的,我们在proxyRes返回之前对set-cookie进行解析并修改相关字段即可,对应以上代码:
if(proxyRes.headers && proxyRes.headers[ 'set-cookie' ]){ cookieArr = cookie.parseGoogleCookies(proxyRes.headers['set-cookie']); proxyRes.headers['set-cookie']=cookieArr; } 

我们再看看parseGoogleCookies函数做了什么事情,代码如下:


/** * 解析google scholar返回的cookie **/
parseGoogleCookies: function (cookies) {
// console.log(cookies);
var cookieArr = [];
if (cookies && cookies.length > 0) {
for (var i = 0; i < cookies.length; i++) {
var cookieItem = cookieUtil.parse(cookies[i]);
if (cookieItem.domain) {
// delete cookieItem.domain;
cookieItem.domain = proxyHost;
}
if (cookieItem.path) {
cookieItem.path = '/';
}
var tempArr = [];
for (var key in cookieItem) {
if (key === 'expires' || key === 'path') {
tempArr.push(key+'='+cookieItem[key]);
} else {
tempArr.push(key+'='+cookieItem[key]);
}
}
cookieArr.push(tempArr.join('; '));
};
}
return cookieArr;
}

如果不够熟悉http cookie,请到谷歌先脑补一下相关的知识,这里推荐一篇博客全面解读HTTP Cookie。这里选取有助于理解上面程序的两点。谷歌cookie的用途:Cookie也被用来记忆用户自定义的一些功能。用户在设置自定义特征的时候,仅仅是保存在用户的浏览器中,在下一次访问的时候服务器会根据用户本地的cookie来表现用户的设置。例如google将搜索设置(使用语言、每页的条数,以及打开搜索结果的方式等等)保存在一个COOKIE里。cookie的Domain and Path
的作用:定义Cookie的生效作用域,只有当域名和路径同时满足的时候,浏览器才会将Cookie发送给Server。如果没有设置Domain和Path的话,他们会被默认为当前请求页面对应值。下面简单讲解一下cookie的http实现:

以访问http://blog.noobsky.com为例

  • Step1.客户端发起http请求到Server
GET / HTTP/1.1
Host: blog.noobsky.com
(这里是省去了User-Agent,Accept等字段)
  • Step2. 服务器返回http response,其中可以包含Cookie设置
HTTP/1.1 200 OK
Content-type: text/html
Set-Cookie: name1=value1
Set-Cookie: name2=value2; Expires=Wed, 06 Jun 2066 18:18:18 GMT
(content of page)
  • Step3. 后续访问blog.noobsky.com的相关页面
GET /archives HTTP/1.1
Host: blog.noobsky.com
Cookie: name1=value1; name2=value2
Accept: */*

第一次访问服务器的适合,服务器返回的头字段里通过set-cookie设置cookie,后面再次访问服务器时,客户端会自动的带上相关的cookie字段。这里需要注意的是要想客户端自动带上相关的cookie字段,cookie的domain和path字段必须要跟你访问的server服务。比如你在第一次访问blog.noobsky.com的时候通过技术手段把服务器response中的set-cookie中的domain域(默认值为blog.noobsky.com)修改为coolshell.cn的话,你下次再访问blog.noobsky.com的相关页面时,将不会带上相关的cookie,因为cookie的domain不匹配。同理通过访问代理然后代理访问scholar.google.com时,scholar.google.com服务器返回的response的set-cookie中的domain域的值是scholar.google.com,下次再访问代理时,因为domain域不匹配,将不会自动带上相关的cookie,所以我们在proxyRes返回给客户端之前,需要hack掉相关的cookie域,我们只需把cookie的domain域修改为proxyHost(代理主机),path域设置为'/'

if (cookieItem.domain) {
cookieItem.domain = proxyHost;
}
if (cookieItem.path) {
cookieItem.path = '/';
}

下次再访问代理主机时,因为domain域匹配,就会自动带上相关的cookie,谷歌学术就能实现记忆用户自定义的功能啦,你就可以随心所欲使用谷歌学术的设置功能!

除了能使用设置功能我们还需要把谷歌学术的默认显示导入BibTeX更改为导入我的网站,并修改默认的点击事件。默认的点击事件是跳转另一页面显示相应地BibTeX,我需要的点击事件是获取BibTeX数据,并推送给我的学术应用,cool!方法很简单:利用cheerio库修改response的html中导入BibTeX,然后注入相关的js脚本,js脚本修改默认点击事件!
* 通过重写response.end函数hack返回的html页面

response.end = function() { 

if( chunks && chunks.toString ) {
_end.apply( this, [ modifyHtml( chunks.toString(),hl , injected) ] );
}else {
_end.apply( this, arguments );
}    
};
  • 修改html的函数如下:
function(str,lang,inject){
$ = cheerio.load(str);
var str = config.zhStr;
if(lang.toLowerCase()=='en'){
str = config.enStr;
}
//修改显示文本
$(".gs_nta.gs_nph").each(function(i,elem){
$(this).text(str);
});
str = $.html();
// Add or script to the page,注入脚本
if( str.indexOf( '</body>' ) > -1 ) {
str = str.replace( '</body>', inject + '</body>' );
} else if ( str.indexOf( '</html>' ) > -1 ){
str = str.replace( '</html>', inject + '</html>' );
} else {
str = str + inject;
}
return str;
}
  • 注入的js脚本
<script type="text/javascript"> $(document).ready(function () { 
 $(".gs_nta.gs_nph").each(function () { 
 $(this).bind("click", getData); }); function getData() { 
 var pdfUrl; if ($(this).parent().parent().prev(".gs_ggs.gs_fl")) { pdfUrl = $(this).parent().parent().prev(".gs_ggs.gs_fl").children("div.gs_md_wp.gs_ttss").children("a").attr("href"); } var path = $(this).attr("href"); $.ajax({ type: "get", url: path, async: false, success: function (data) { 
 alert("获取BibTex成功!" + data + "pdfUrl:" + pdfUrl); }, error: function () { 
 alert("获取BibTex失败!"); } }); return false; } }); </script>

这里需要注意的是,推送BibTeX到自己的学术应用中存在跨域问题,这里采用JSONP的方式。激动人心的时刻来了,我们来看一下最终的效果

  • hack原始html后的显示
    学术应用使用node-http-proxy集成谷歌学术「建议收藏」
  • 修改后的点击事件
    学术应用使用node-http-proxy集成谷歌学术「建议收藏」
    详细代码见Github上的google-scholar-proxy

本文链接:http://blog.noobsky.com/2015/11/25/学术应用使用node-http-proxy集成谷歌学术/

–EOF–

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/183863.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • js有几种数据类型,基本数据类型有哪些_js中简单数据类型

    js有几种数据类型,基本数据类型有哪些_js中简单数据类型js中数据类型分为原始数据类型与引用数据类型原始数据类型:number//数值类型:值只能为数字vara=12string//字符串类型:值都是以引号包裹的vara=’你好’boolean//布尔类型:值只存在真假vara=truevara=falsenull//空类型vara=nullundefined//未定义类型:定义了变量a,但是没有a赋值varaconsole.log(a

  • clion激活码(注册激活)

    (clion激活码)这是一篇idea技术相关文章,由全栈君为大家提供,主要知识点是关于2021JetBrains全家桶永久激活码的内容https://javaforall.cn/100143.htmlIntelliJ2021最新激活注册码,破解教程可免费永久激活,亲测有效,上面是详细链接哦~08G05E7DZH-eyJsaWNlb…

  • Linux进程调度_linux进程的查看和调度

    Linux进程调度_linux进程的查看和调度进程调度含义进程调度决定了将哪个进程进行执行,以及执行的时间。操作系统进行合理的进程调度,使得资源得到最大化的利用。在单片机上,常常使用的方式是:系统初始化—-&gt;while(1){}。(当然,单片机也可以跑类似FreeRTOS,也可以有进程切换)在带操作系统的CPU上跑的逻辑是,允许多个进程(其实就是程序)”同时”跑。比如,你可以在操作鼠标的同时,进行音乐播放,文字…

  • matlab中的振铃现象是啥,振铃现象产生的原因

    matlab中的振铃现象是啥,振铃现象产生的原因振铃现象是怎么回事?是什么?如何减小和抑制上冲及振铃?下面就由小编告诉大家和抑制方法吧!由于任何传输线都不可避免地存在着引线电阻、引线电感和杂散电容,因此,一个标准的脉冲信号在经过较长的传输线后,极易产生上冲和振铃现象。大量的实验表明,阴线电阻可使脉冲的平均振幅减小;而杂散电容和引线电感的存在,则是产生上冲和振铃的根本原因。在脉冲前沿上升时间相同的条件下,阴线电感越大,上冲及振铃现象就越严重;杂散…

  • 正视自己,金无足赤——BCH不断发现与修正缺陷

    正视自己,金无足赤——BCH不断发现与修正缺陷

  • 手机JAVA编程技术[通俗易懂]

    JAVA手机编程技术林天峰(温州职业技术学院计算机系浙江

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号