« iPhoneのsafariで文字化けしているサイトを読めるようにする | トップページ | 第3回 MongoDB勉強会に行ってきた »

2010年8月 8日 (日)

webページをproxyしてくれるwebアプリのソース

悪用されそうで、自重してサービスを止めていたのですが、同じようなツール
を作ってすでに公開(Google App Engineを使って無料で自宅用プロキシを運用)
されているので、私もサービスを再開し、ソースを公開することにしました。
下記で公開中です。
google code webappproxy
メインのプログラム1本とHTML1本の小さなプログラムなので全部掲載します。
guess_charsetに関しては、たばさの:urlfetchとmemcacheを使ってみるテスト
のソースを流用させていただきました。
Python歴3ヶ月程度なのでPythonのほうは滅茶苦茶。

hello.py

 import os
import re
import urllib
import logging
import urlparse
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from google.appengine.api import urlfetch
from google.appengine.ext.webapp import template

def guess_charset(data):
   f = lambda d, enc: d.decode(enc) and enc 
 
   try: return f(data, 'utf-8') 
   except: pass 
   try: return f(data, 'shift-jis') 
   except: pass 
   try: return f(data, 'euc-jp') 
   except: pass 
   try: return f(data, 'iso2022-jp') 
   except: pass 
   return None 

class MainPage(webapp.RequestHandler):
  def get(self):
    path = os.path.join(os.path.dirname(__file__), 'main.html')
    self.response.out.write(template.render(path, {}))

class NotePage(webapp.RequestHandler):
  def get(self):
    url=""
    if(self.request.get("url")):
      if re.compile("^\s*http://").match(self.request.get("url")):
        url = self.request.get("url").strip()
      else:
        url = "http://"+self.request.get("url").strip()
    else:
      if 'host' in self.request.cookies:
        url = self.request.cookies['host'] + self.request.path
    if url!="" :
      result = urlfetch.fetch(url)
      enc=guess_charset(result.content)
      if enc == None or enc == "shift_jis":
        enc="cp932"
      dec_content = result.content.decode(enc)
      enc_content = dec_content.encode("utf_8")
      result.content = re.sub("charset=([a-zA-Z0-9_]*)","charset=UTF-8",enc_content,1)
      if result.status_code == 200:
        for k,v in result.headers.iteritems():
          self.response.headers.add_header(k, v)
        parseString=urlparse.urlparse(url)
        cookie_val = 'host' + '=' + parseString[0]+"://"+parseString[1]
        self.response.headers.add_header('Set-Cookie',cookie_val)
        cookie_val = 'path' + '=' + re.sub("/[^/]*$","",parseString[2])
        self.response.headers.add_header('Set-Cookie',cookie_val)
        self.response.out.write(result.content)
    else:
      self.response.out.write("")

application = webapp.WSGIApplication(
                                     [('/main', MainPage),(r'/[^?]*',NotePage)],
                                     debug=True)
def main():
  run_wsgi_app(application)

if __name__ == "__main__":
  main()

/mainでアクセスされたら、MainPageのgetを呼び出し、main.htmlを返して
います。
それ以外のアドレスはすべてNotePageのgetを呼び出します。
こうすることには 訳があり、後ほど説明します。
main.html画面にurlを入力し、displayボタンを押下すると、/?url=指定したurl
の形式でアクセスされます。
指定されたurlからurl_fetchで内容をごっそり持ってきて、utf-8に変換します。
また、クッキーに指定されたURLのHostをセットしておきます。
こうすることで、表示した画面のパスのみで記述されたリンクをクリックされた
際に、 クッキーに保存しているHost名にそのパスをくっつけることで、再び
コンテンツを 取得することができるようになります。
/main以外のアドレスをすべてNotePageのgetを呼び出すようにしたのもそういう
訳です。

main.html

 <html>
  <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <title>title</title>
 <link rel="shortcut icon" href="favicon.ico">
 <script type="text/javascript">
 function parseCooke(){
      cookies = document.cookie.split(";");
      obj = {};
 for(var i=0;i<cookies.length;i++){
        pair = cookies[i].split("=");
        obj[pair[0].replace(/^\s+/,"")] = pair[1];
 }
 return obj;
 }
 function loadData()
 {
 document.getElementById('ifm').src = "/?url="+escape(document.getElementById('url').value);
 }
 function modLink()
 {
 var doc;
 var ifm = document.getElementById("ifm");
 if (document.all) {
 // IE
        doc  = ifm.contentWindow.document;
 } else {
 // Mozilla
        doc = ifm.contentDocument;
 }
 var aElems=doc.getElementsByTagName("a");
 var cookie_info = parseCooke();
 var nHost = cookie_info.host;
 var path = cookie_info.path;
 var reg = RegExp(nHost);
 var reg2 = RegExp("http://"+location.host);
 for(var i=0;i<aElems.length;i++)
 {
 var aElem = aElems[i];
 if (document.all) {
 if(aElem.target!=null){
            aElem.target=null;
 }
 }
 else if(aElem.target){
 delete(aElem.target);
 }
 if(aElem.href.match(reg)){
          aElem.href= aElem.href.replace(nHost,"http://"+location.host);
 }
 else if(!aElem.href.match(reg2)){
          aElem.href="http://"+location.host+"?url="+escape(aElem.href);
 }else{
 if(!aElem.getAttributeNode("href").nodeValue.match(/^[\/#]/)){
            aElem.href =  path + "/" + aElem.getAttributeNode("href").nodeValue;
 }
 }
        aElem.onclick=function(ev){
 var elem = null;
 if(ifm.contentWindow.event){
            elem=ifm.contentWindow.event.srcElement;
 }else
 {
            elem=ev.target;
 }
          ifm.src = elem.href;return false;
 };
 }
 var imgElems=doc.getElementsByTagName("img");
 for(var i=0;i<imgElems.length;i++)
 {
 var imgElem = imgElems[i];
 if(!imgElem.getAttributeNode("src").nodeValue.match(/^http:/)){
 if(imgElem.getAttributeNode("src").nodeValue.match(/^\//)){
            imgElem.src = nHost + imgElem.getAttributeNode("src").nodeValue;
 }else{
            imgElem.src = nHost + "/" + path + "/" + imgElem.getAttributeNode("src").nodeValue;
 }
 }
 }
 }
 </script>
 </head>
<body>
  <form id="note_form" target="#" onsubmit="loadData(); return false;">
    URL:<input type="text" name="url" id="url" size="150"/>
    <input type="submit" value="display"/>
  </form>
  <iframe id="ifm" frameborder='0' style="width:100%;height:100%;border:none;" onload="modLink();" >
  </iframe>
</body>
</html>

main.htmlでは、displayボタンが押された時に/?url="URLのテキストボックスに
書かれた値" の形式でwebappproxyのURLを呼び出し、応答をiframeに書き出し
ています。
その際、iframeに書き出されたaタグのリンクをすべて抽出し、srcが同一ホストの
場合は、ホスト名を削除し(クッキーにホストが保存されているため),パスだけに
します。
違うホスト名の場合は/?url=違うホストのURLにします。
上記のようにすることで、proxyして表示されたリンクをたどっていっても、
proxyされ続けるようにしています。

|

« iPhoneのsafariで文字化けしているサイトを読めるようにする | トップページ | 第3回 MongoDB勉強会に行ってきた »

Google App Engine」カテゴリの記事

コメント

コメントを書く



(ウェブ上には掲載しません)




トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/68673/49096148

この記事へのトラックバック一覧です: webページをproxyしてくれるwebアプリのソース:

« iPhoneのsafariで文字化けしているサイトを読めるようにする | トップページ | 第3回 MongoDB勉強会に行ってきた »