搞科研的同学肯定离不开谷歌学术,谷歌学术搜索是文献搜索下载一大利器。之前实验室开发了一款学术应用,遗留了历史问题,就是没有解决文献搜索的功能,而这个任务最后落在我的身上。我采用的方案就是集成谷歌学术,但是国内的网络环境,你懂的,自然状态下根本就访问不了谷歌学术的,你得翻墙才能访问。你不能期望使用你开发的学术应用都能翻墙访问谷歌学术(虽然搞科研的人电脑翻墙软件肯定都准备好了!),所以呢我还要给谷歌学术搭建一个代理。不仅要集成谷歌学术一键搜索并下载,还要能导入和分享文献到自己开发的应用,获取文献的BIBTEX并导入到自己的学术应用中,就是要在代理中hack谷歌学术的原始响应,注入相关脚本。(关于BIBTEX可以去维基百科脑补一下。)
废话少说,放效果图过来先:
谷歌学术代理
谷歌学术代理默认采用的是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]的链接,见下图:
- 点击右上角我的著作引用情况右边的三角图标,选择设置
- 然后选择显示导入BibTeX的链接
- 点击保存后自动跳转到搜索结果,将会显示导入BibTeX(谷歌学术的原始显示)
修改谷歌学术的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实现:
- 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后的显示
- 修改后的点击事件
详细代码见Github上的google-scholar-proxy。
本文链接:http://blog.noobsky.com/2015/11/25/学术应用使用node-http-proxy集成谷歌学术/
–EOF–
今天的文章学术应用使用node-http-proxy集成谷歌学术分享到此就结束了,感谢您的阅读,如果确实帮到您,您可以动动手指转发给其他人。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/26141.html