Blog小功能-点击链接自动展开Codeforces代码
前言
codeforces评测机炸了,闲来无事想给blog写个新功能,这个过程里遇到了不少坑,在此记录一下。
另:本人基本没有JavaScript基础,只能根据w3cschool和别人的博客依样画葫芦来的,代码可能巨丑,请见谅。
需求简述
在网页中所有指向型如https://codeforces.com/contest/1338/submission/76667310的超链接下放添加一个可以快速查看源代码的小组件,实际效果如下。
由于博客园不允许js代码,请到这里查看
客户端本地解析
XMLHttpRequest
大体思路:通过客户端(浏览器)发送Get请求,得到网页源代码后解析。
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.onload = function(){
if(xhr.status === 200){
// do something
}
}
xhr.send();
结果遇到了CORS跨域的问题,此路不通
Jsonp
据说jsonp可以解决跨域问题,其原理是src标签可以载入任意地方资源,而不要求同源。
但是经过实测,text/html类型的数据并不能通过这种方法跨域,此路不同。
远程服务器解析
思来想去,还是用python解析比较可行,于是上flask(一个挺顺手的web框架),并搭建uwsgi(过程见Centos8配置uwsgi)
先上个demo
from flask import Flask
app = Flask(__name__)
@app.route('/')
def home():
return "Hello world"
重新配置uwsgi,添加callable
[uwsgi]
socket = 127.0.0.1:3031
chdir = /dic
wsgi-file = api.py
callable = app
processes = 4
threads = 2
stats=%(chdir)uwsgi/uwsgi.status
pidfile=%(chdir)uwsgi/uwsgi.pid
配置好nginx后打开127.0.0.1,看到"Hello world",说明配置正确。
最终服务端代码
from flask import Flask
from flask import abort
import requests
import re
from bs4 import BeautifulSoup
app = Flask(__name__)
def get_respond(url): # 获取网页源码
pass
def extract(content): # 解析html
pass
@app.route('/')
def home():
abort(403)
@app.route('/codeforces_sources/<path:url>')
def codeforces_sources(url):
url = 'https://%s' % url
html = get_respond(url)
if html is None: # 访问失败
abort(500)
code = extract(html)
if code is None: # 提取失败
abort(404)
return code.string
if __name__ == "__main__":
app.run(debug = True)
这里还有个小插曲:上述代码在本地正常运行,但是在服务器上失败,uwsgi报错ModuleNotFound,检查后原因是beautifulsoup4模块安装时使用了pip --user参数,指定pythonHome或把模块安装到默认路径下即可(最懒的方法:使用sudo pip install beautifulsoup4)
修改nginx跨域配置
简单的三行代码,添加到location下即可
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept";
add_header Access-Control-Allow-Methods "GET";
另外,可以通过正则来配置跨域白名单,见链接
编写Js代码
最终代码如下,用到了highlight.js
<script>
var links = document.getElementsByTagName("a");
var pattern1 = new RegExp("https://(codeforces.com/problemset/submission/[0-9]+/[0-9]+)");
var pattern2 = new RegExp("https://(codeforces.com/contest/[0-9]+/submission/[0-9]+)");
for (var i=0; i<links.length; i++){
(function(i){
var href = links[i].href;
console.log(href);
var url = '';
if(pattern1.test(href)){
url = pattern1.exec(href)[1].trim();
}else if(pattern2.test(href)){
url = pattern2.exec(href)[1].trim();
}
if (url != ''){
var innerText = links[i].innerText
var parent = links[i].parentNode;
var frame = document.createElement("div");
if(parent.lastChild == links[i]){
parent.appendChild(frame, links[i]);
}else{
parent.insertBefore(frame, links[i].nextSibling);
}
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.chgtaxihe.top/codeforces_sources/' + url, true);
xhr.onload = function(){
if(xhr.status === 200){
s = hljs.highlightAuto(xhr.responseText).value;
frame.innerHTML = '<details><summary>展开' + '</summary><div><pre>' + s + '</pre></div></details>';
}
}
xhr.send();
}
})(i);
}
</script>
小问题:for循环中出现异步函数,引用值不正常。
解决方案:参考博客
Upd: 懒加载
js代码更改如下,需要用到jquery
<script>
var links = document.getElementsByTagName("a");
var pattern1 = new RegExp("https://(codeforces.com/problemset/submission/[0-9]+/[0-9]+)");
var pattern2 = new RegExp("https://(codeforces.com/contest/[0-9]+/submission/[0-9]+)");
for (var i=0; i<links.length; i++){
(function(i){
var href = links[i].href;
var url = '';
if(pattern1.test(href)){
url = pattern1.exec(href)[1].trim();
}else if(pattern2.test(href)){
url = pattern2.exec(href)[1].trim();
}
if (url != ''){
var innerText = links[i].innerText
var parent = links[i].parentNode;
links[i].setAttribute('class', 'expand_code');
links[i].id = i;
var frame = document.createElement("div");
frame.setAttribute("class", 'expand_div_' + i.toString());
if(parent.lastChild == links[i]){
parent.appendChild(frame, links[i]);
}else{
parent.insertBefore(frame, links[i].nextSibling);
}
}
})(i);
}
$("a.expand_code").click(function(){
var dest = this.href.replace("https:\/\/",'');
var item = $('div.expand_div_' + this.id);
var state = item.html();
if(state == ''){
$.get('https://api.chgtaxihe.top/codeforces_sources/' + dest, function(result){
s = hljs.highlightAuto(result).value;
item.html('<pre>' + s + '</pre>');
item.slideToggle();
});
}
item.slideToggle();
return false;
});
</script>
UPD:使用Cloudflare Worker
Cloudflare的Worker真香
async function handleRequest(request) {
// Make the headers mutable by re-constructing the Request.
const url = request.url
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, HEAD, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type',
}
// Function to parse query strings
function getParameterByName(name) {
name = name.replace(/[\[\]]/g, '\\$&')
name = name.replace(/\//g, '')
var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
results = regex.exec(url)
if (!results) return null
else if (!results[2]) return ''
else if (results[2]) {
results[2] = results[2].replace(/\//g, '')
}
return decodeURIComponent(results[2].replace(/\+/g, ' '));
}
if (getParameterByName("url") === null){
return new Response("Oh no! Something goes wrong!", {status: 500, headers: corsHeaders});
}
var dest_url = atob(getParameterByName("url"));
request = new Request(request)
request.headers.set('Referer', 'https://codeforces.com')
let response = await fetch(dest_url, request)
// Make the headers mutable by re-constructing the Response.
var content = await response.text();
var reg = /<pre id="program-source-text".+?>([.\s\S\n]+?)<\/pre>/;
response = new Response(reg.exec(content)[1], {headers: corsHeaders,})
return response
}
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request))
})
因为API变动了,顺手把js脚本也改了(这里就不贴出来了)

浙公网安备 33010602011771号