Chgtaxihe's blog Chgtaxihe's blog
首页
练习
算法学习
读书笔记
小玩意

Chgtaxihe

首页
练习
算法学习
读书笔记
小玩意
  • 给blog添加新功能(1)

    • Upd: 懒加载
      • UPD:使用Cloudflare Worker
      Chgtaxihe
      2021-02-23
      随笔

      给blog添加新功能(1)

      # 前言

      UPD: 由于博客从Jekyll迁移到了VuePress,该功能暂时失效

      codeforces评测机炸了,闲来无事想给blog写个新功能,这个过程里遇到了不少坑,在此记录一下。

      另:本人基本没有JavaScript基础,只能根据w3cschool和别人的博客依样画葫芦来的,代码可能巨丑,请见谅。

      # 需求简述

      在网页中所有指向型如https://codeforces.com/contest/1338/submission/76667310的超链接下放添加一个可以快速查看源代码的小组件,实际效果如下。


      AC代码 (opens new window)


      # 客户端本地解析

      # XMLHttpRequest

      大体思路:通过客户端(浏览器)发送Get请求,得到网页源代码后解析。

      var xhr = new XMLHttpRequest();
      xhr.open('GET', url, true);
      xhr.onload = function(){
          if(xhr.status === 200){
              // do something
          }
      }
      xhr.send();
      
      1
      2
      3
      4
      5
      6
      7
      8

      结果遇到了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"
      
      1
      2
      3
      4
      5
      6
      7

      重新配置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
      
      1
      2
      3
      4
      5
      6
      7
      8
      9

      配置好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)
      
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32

      这里还有个小插曲:上述代码在本地正常运行,但是在服务器上失败,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";
      
      1
      2
      3

      另外,可以通过正则来配置跨域白名单,见链接 (opens new window)

      # 编写Js代码

      最终代码如下,用到了highlight.js (opens new window)

      <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>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36

      小问题:for循环中出现异步函数,引用值不正常。

      解决方案:参考博客 (opens new window)

      # 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>
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43

      # UPD:使用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))
      })
      
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41

      因为API变动了,顺手把js脚本也改了

      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]+)");
          $(document).ready(function(){
              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].innerText = links[i].innerText + "(展开)";
                          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);
              }
              function HTMLDecode(text) { 
                  var temp = document.createElement("div"); 
                  temp.innerHTML = text; 
                  var output = temp.innerText || temp.textContent; 
                  temp = null; 
                  return output; 
              } 
              $("a.expand_code").click(function(){
                  var dest = this.href;
                  var item = $('div.expand_div_' + this.id);
                  var state = item.html();
                  if(state == ''){
                      $.get('https://api.chgtaxihe.top/sources_fetch?url=' + window.btoa(dest), function(result){
                          s = hljs.highlightAuto(HTMLDecode(result)).value;
                          item.html('<pre>' + s + '</pre>');
                          item.slideToggle();
                      });
                  }
                  item.slideToggle();
                  return false;
              });
          });
      
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      上次更新: 2021/02/24, 03:37:30
      最近更新
      01
      深入理解Java虚拟机
      03-04
      02
      DNS工作原理
      02-27
      03
      改用VuePress啦
      02-23
      更多文章>
      Theme by Vdoing | Copyright © 2019-2021
      • 跟随系统
      • 浅色模式
      • 深色模式
      • 阅读模式