HTMLを正規表現で指定してRSSに変換するスクリプト
今でもブログを使わずにHTML直書きで日記を書いている方が居る。RSSリーダーで読みたい。そういうときに使えるサービスとしてPage2Feed APIがあるけど、自動処理よりは自分で指定した方が綺麗に抜き出せるので、そんなスクリプトを書いた。
著作権とかセキュリティとかが不安なので設置した物を公開するのは止めておきます。適当なサーバーに置いて使ってください。スクリプトは続きに。
こんな感じのページに対して、
こんな感じで設定して、リンクをRSSリーダーに登録すると、
こんな感じになる。
ETagをRSSの項目にすることができるので正規表現から漏れても更新されたことはわかる。
RSSのリンクの末尾にconf=onを付けると再設定可能。
#! /usr/bin/python # coding: utf-8 # 使用例 # Title: Google のサービス # URL: http://www.google.co.jp/intl/ja/options/ # RegExp: <li id=".*?"><a href=".*?">(.*?)<span></span></a><p>(.*?)\n # Title group: 1 # Description group: 2 import urllib2 import cgi import re import htmllib import sys import sqlite3 import time import hashlib MAX_PAGE_SIZE = 100*1024 CACHE_FILE = r"page2rss/cache" GOOGLE_API_KEY = "" class MyError(Exception): pass # main def main(): stor = cgi.FieldStorage() if len(stor)==0 or "conf" in stor and stor["conf"].value.decode("utf-8")=="on": printhtml(stor) return try: # 読み込み if "title" not in stor: raise MyError(u"ページタイトルが指定されていません") if "url" not in stor: raise MyError(u"URLが指定されていません") content,etag = loadpage(stor["url"].value.decode("utf-8")) # 解析 try: if "reg" in stor: item = [] r = re.compile(stor["reg"].value.decode("utf-8"),re.M|re.S) for m in re.finditer(r,content): t = {} if "gtitle" in stor: t["title"] = m.group(int(stor["gtitle"].value)) if "gdesc" in stor: t["desc"] = m.group(int(stor["gdesc" ].value)) else: t["desc"] = m.group(0) item += [t] if "etag" in stor and stor["etag"].value=="on" and etag!="": item += [{"title":"ETag","desc":etag}] except: raise MyError(u"ページの解析に失敗しました") # 出力 printrss(title=stor["title"].value.decode("utf-8"), link=stor["url"].value.decode("utf-8"), item=item) except MyError, e: printrss(title=u"エラー",desc=unicode(e)) return # ページの読み込み # ページ内容とETagを返す def loadpage(url): # テーブルが無ければ作成 # キャッシュを取得 conn = sqlite3.connect(CACHE_FILE) c = conn.cursor() c.execute("select * from sqlite_master where type='table' and name='cache'") if c.fetchone()==None: c.execute("create table cache (url text primary key, time real, content text, etag text)") conn.commit() c.execute("select * from cache where url=?",(url,)) cache = c.fetchone() conn.close() # 現在時刻 tm = time.time() update = False # 1時間以内のキャッシュがあればそれを返す if cache and tm<=cache[1]+3600: return cache[2],cache[3] # 読み込み try: request = urllib2.Request(url) if cache and cache[3]!="": request.add_header("If-None-Match",cache[3]) page = urllib2.urlopen(request) content = page.read(MAX_PAGE_SIZE) etag = page.info()["Etag"] if "ETag" in page.info() else "" modified = True except urllib2.HTTPError, e: if e.code==304: content,etag = cache[2],cache[3] modified = False else: raise MyError(u"ページの読み込みに失敗しました") except: raise MyError(u"ページの読み込みに失敗しました") # 文字コード変換 if modified: # 例外が発生しなければ正解でいいだろ…… charcode = ["ascii","shift_jis","euc_jp","utf_8","cp932"] content,tmp = None,content for i in range(8): for code in charcode: try: content = tmp.decode(code) except: pass if content!=None: break if content!=None: break # マルチバイト文字の途中で切れている可能性があるので tmp = tmp[:-1] if not content: raise MyError(u"文字コードの変換に失敗しました") # データベースに追加 conn = sqlite3.connect(CACHE_FILE) c = conn.cursor() if cache!=None: c.execute("delete from cache where url=?",(url,)) c.execute("insert into cache values (?,?,?,?)",(url,tm,content,etag)) conn.commit() conn.close() return content,etag # HTMLを出力 def printhtml(stor): value = lambda s: cgi.escape(stor[s].value,True) if s in stor else "" print r"""Content-Type: text/html; charset=utf-8; <html> <head><title>page2html</title></head>""" print ' <script type="text/javascript" src="https://www.google.com/jsapi?key=%s"></script>' % GOOGLE_API_KEY print r""" <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script> <script type="text/javascript"> <!-- $(function(){ function up() { s = document.location.href.split("?")[0]+"?"; s += "title="+encodeURIComponent($("#title").val())+"&"; s += "url="+encodeURIComponent($("#url").val())+"&"; s += "reg="+encodeURIComponent($("#reg").val())+"&"; s += "gtitle="+encodeURIComponent($("#gtitle").val())+"&"; s += "gdesc="+encodeURIComponent($("#gdesc").val())+"&"; if($("#etag").attr("checked")) s += "etag="+encodeURIComponent($("#etag").val())+"&"; $("#rss").attr("href",s).text(s); } $("#title").change(up).keyup(up); $("#url").change(up).keyup(up); $("#reg").change(up).keyup(up); $("#gtitle").change(up).keyup(up); $("#gdesc").change(up).keyup(up); $("#etag").change(up).keyup(up); up(); }); //--> </script> <body> <form method=get action=""> <dl> <dt><label for=title>Title:</label></dt>""" print ' <dd><input type=text size=64 name=title id=title value="%s" /></dd>' % value("title") print " <dt><label for=url>URL:</label></dt>" print ' <dd><input type=text size=64 name=url id=url value="%s" /></dd>' % value("url") print " <dt><label for=reg>RegExp:</label></dt>" print ' <dd><input type=text size=64 name=reg id=reg value="%s" /></dd>' % value("reg") print " <dt><label for=gtitle>Title group:</label></dt>" print ' <dd><input type=text size=4 name=gtitle id=gtitle value="%s" /></dd>' % value("gtitle") print " <dt><label for=gdesc>Description group:</label></dt>" print ' <dd><input type=text size=4 name=gdesc id=gdesc value="%s" /></dd>' % value("gdesc") print " <dt><label for=etag>ETag:</label></dt>" print ' <dd><input type=checkbox name=etag id=etag value=on %s /></dd>' % ("checked" if value("etag")=="on" else "") print r""" <br /> <dt><strong>RSS:</strong></dt> <dd><a id=rss href=""> </a></dd> </dl> <input type=submit value="Preview" /> </form> </body> </html>""" # RSSを出力 def printrss(title="",link="",desc="",item=None): if item==None: item = [] esc = lambda x: cgi.escape(x,True).encode("utf-8") print "Content-Type: application/rss+xml; charset=utf-8;" print "" print '<?xml version="1.0"?>' print '<rss version="2.0">' print ' <channel xml:base="%s">' % esc(link) print " <title>%s</title>" % esc(title) print " <link>%s</link>" % esc(link) print " <description>%s</description>" % esc(desc) for i in item: print " <item>" t = "" if "title" in i: t += " <title>%s</title>\n" % esc(i["title"]) t += " <link>%s</link>\n" % esc(link) if "desc" in i: t+= " <description>%s</description>\n" % esc(i["desc"]) print t[:-1] print ' <guid isPermaLink="false">%s</guid>' % hashlib.sha1(t).hexdigest() print " </item>" print " </channel>" print "</rss>" if __name__=="__main__": main()