2010年10月15日 星期五

Stanza - USER AGENT & OPDS Catalog

Stanza 是在 iPod Touch / iPhone / iPad 上,強大的免費電子書軟體,有興趣可以裝一下


然而,有些電子書的供應網站,有提供 OPDS Catalog,但是僅限於 Stanza 去閱讀,真是令人不方便,因此就先了解一下 Stanza 的 User Agent後,就可以去模擬一下,看到那些 OPDS Catalog 囉!


得知 Stanza 的 User Agent 的方式嘛,就是先寫個 CGI ,例如透過 PHP:


@index.php
<?php
file_put_contents( '/tmp/opds' , print_r( $_SERVER , true ) );
?>


接著使用 Stanza > 獲取書籍 > 分享 > 編輯 > 新增上述 CGI 位置,接著瀏覽一下就可以收集到 Stanza 使用的 User Agent 啦!


Stanza/3.0.3 iPhone OS/4.1/iPhone catalog/3.0.3


最後則是透過瀏覽器的一些 plugin ,例如 Firefox & User Agent Switcher,就可以新增一個 user agent,接著在瀏覽某些網頁就會看到神秘的 OPDS Catalog 囉!


2010年10月13日 星期三

opac.ndl.go.jp 日本書籍資料

研究上,如果要取得書籍資料當作測試,並不是每一家擁有資料者都那麼佛心的!特別是沒有任何關係者。慶幸的,国立国会図書館有提供這種服務!


http://opac.ndl.go.jp/ 網站上,可以透過關鍵字去查詢書籍,查詢完後,旁邊有個小按鈕 "Download" ,之後就可以把這次查詢的資料其 metadata 下載回來。其格式是 tab-delimited format (tsv) 並且最多讓你下載 200 筆資料。


以 Python 語法來處理下載下來的 tsv 檔案:


fd = codecs.open( target_file , 'rb' , 'Shift-JIS' )
rawitems = []
while True:
raw = fd.readline()
if raw is None or raw == '' :
    break
else:
    rawitems.append( raw.split( '\t' ) )
fd.close()

RE_ISBN = re.compile( '[0-9xX\-]{10,}' )

for rawdata in rawitems:

    ISBN = None
    TITLE = None
    PUBLISHER = None
    NOTE = None

    # ISBN
    if len(rawdata) < 17:
        continue
    ISBN = rawdata[16]
    ISBN = re.findall( RE_ISBN , ISBN )
    if len(ISBN) < 1 :
        continue
    ISBN = ISBN[0].replace('-', '')

    # TITLE
    if len(rawdata) < 2:
        continue
    TITLE = rawdata[1]

    # PUBLISHER
    if len(rawdata) < 9:
        continue
    PUBLISHER = rawdata[8]

    if len(rawdata) >= 16 :
        NOTE = rawdata[15]

    #
    # do something ...
    # ...
    
    return


[Python] 使用 cURL (PycURL)

以前常用 PHP cURL 惡搞,最近常寫 Python,終於又碰到了使用 cURL 的時刻了,自己隨意摸索一下筆記。


關於 PycURL 的使用,對照文件:



如此如此,這般這般,心得:



  • 所有 "CURLOPT_" 開頭的設定,在 Python 改用 "pycurl.",例如 CURLOPT_URL 改成 pycurl.URL

  • 以前用 PHP 時,可以 curl_setopt( $ch , CURLOPT_RETURNTRANSFER , true ); 的方式,就可以透過 $result = curl_exec( $ch ); 把網頁資料存在變數中,現在這邊則是要用 b = StringIO.StringIO()、c.setopt(pycurl.WRITEFUNCTION, b.write) 和 b.getvalue() 方式來取得


簡易的範例:


        import urllib
        import pycurl
        import StringIO

        url = 'target_url'
        c = pycurl.Curl()
        c.setopt( pycurl.URL , url )
        c.setopt( pycurl.FOLLOWLOCATION , True )

        c.setopt( pycurl.COOKIEFILE , '/tmp/pycurl' )
        c.setopt( pycurl.COOKIEJAR , '/tmp/pycurl' )
     
        b = StringIO.StringIO()
        c.setopt(pycurl.WRITEFUNCTION, b.write)

        c.perform()

        #print b.getvalue()
        r = b.getvalue()
        b.close()
        b = StringIO.StringIO()
        c.setopt(pycurl.WRITEFUNCTION, b.write)


        check = re.findall( re.compile( '(<form(.*?)</form>)' , flags=(re.IGNORECASE|re.DOTALL) ) , r );
        if len(check) < 1 :
                print "No FORM DATA"
                return

        r = check[0][1]
        out = {}
        for sub_info in re.findall( re.compile( '<input(.*?)(name=[\'"]{0,1}(.*?)[\'"]{0,1}[\s]+value=[\'"]{0,1}(.*?)[\'"]{0,1}[\s>]|value=[\'"]{0,1}(.*?)[\'"]{0,1}[\s]+name=[\'"]{0,1}(.*?)[\'"]{0,1}[\s>])' , flags=re.IGNORECASE ) , r ):
                if len(sub_info) != 6 :
                        continue
                if sub_info[2] != '':
                        out[ sub_info[2] ] = sub_info[3]
                elif sub_info[5] != '':
                        out[ sub_info[5] ] = sub_info[4]

        for key in out :
                print "\t",key,":\t",out[key]

        url = 'target_form_action_url'
        c.setopt( pycurl.URL , url )
        c.setopt( pycurl.FOLLOWLOCATION , True )
        c.setopt( pycurl.POST , True )
        c.setopt( pycurl.POSTFIELDS , urllib.urlencode(out) )

        c.perform()

        #print b.getvalue()
        r = b.getvalue()
        b.close()

        f = open( target_file , 'wb' )
        f.write( r )
        f.close()


這個範例是到某個網頁收集 form 表格中的 input 欄位資料,並且使用 cookie ,最後則是產生 POST 並把結果存在檔案中。


2010年10月11日 星期一

OPDS Catalog Validator

OPDS Catalog 是從 Atom 那邊擴充的,相關的驗證工具:



但如果想要驗證 OPDS Catalog 1.0 的規格,這些都還不夠用。而後終於找到一個可以驗證的方式:



使用方式也要稍作設定:



  • Address : 你的 opds url

  • Schemas

    • http://openpub.googlecode.com/svn/trunk/schemas/atom.rnc

    • http://openpub.googlecode.com/svn/trunk/schemas/opds_catalog.rnc



  • Preset:None

  • Parser:XML; load external entities

  • 勾選 'Be lax about HTTP Content-Type'


2010年10月8日 星期五

[Javascript] EPUB Reader 翻頁效果的原理

幾天前收到一個問題,關於坊間常見得 Javascript 版 EPUB Reader ,他們處理翻頁的效果是怎樣做的呢?


可以看看以下的 Demo:



Javascript EPUB Reader 的實作,原理就是將 EPUB 裡頭以XHTML、HTML單一檔案為單位,把整個 XHTML 弄成一個物件擺入指定的 DIV 裡頭,藉以顯示出來,就像用瀏覽器瀏覽 XHTML 檔案一樣。如此一來,只是透過 Javascript 去抽取該 XHTML 內的資料出來呈現。


至於翻頁的效果呢?假設要呈現的內容範圍是 640x480,以 ePub js 的原始碼看來,先產生一個 div-tmp ,動態把一個 XHTML 的資料,以 <p> 為單位地,慢慢地一個個加入到 div-tmp,並且動態地查看 div-tmp 的高度,若超過 480 時,則把前一個才加入的 <p> 內容,以空白切字,從後面慢慢地移出一個字出來,直到 div-tmp 的高度滿足 480 的限制,並且把移出來的內容另外建立一個新的 <p> 並擺在其後,以便下一頁的使用。而 rePublish 也是使用類似的概念,細節可以 rePublish - paginator.js 看到。


但是,以空白切字是有危險的,例如僅適用於英文字這種好切的語言,因此我嘗試使用 Monocle 發現他並沒有切字切不好的現象,再仔細一看,發現他的實作方式並非使用切字、填補的方式,並採用兩個 div 的處理機制。而後發現使採用 CSS Multi-column Layout Module (W3C Candidate Recommendation 17 December 2009)。


概念就是產生兩個 div ,一個是現在的這頁,另一個是下一頁用來顯示的,假設這兩頁是同一份的 XHTML 資料,且這個 XHTML 檔案不分頁顯示時,呈現共佔 640x800 大小,那第一頁就類似顯示 640x480,剩下的 640x(800-480) 之後的內容。然而,這樣還可能存在邊界關係,例如第一頁的最後一列,可能會整列因為視窗大小不夠顯示,而把整列的字都切了一半。實際測試,發現並沒這個問題,我用 Firebug 去觀察相關的 div style,整理一個小範例:


<html>
        <head>
                <title>Monocle Test</title>
                <style type="text/css">
                        #container
                        {   
                                width: 100%;
                                height: 100%;
                                position: relative;
                        }   
                        #show {
                                position: absolute;
                                top: 1em; bottom: 1em; left: 1em; right: 1em;
                                overflow: hidden;
                        }   
                        #content
                        {   
                                position: absolute;
                                top: 0pt; bottom: 0pt; min-width: 200%;
                                -moz-column-gap: 0pt; -moz-column-width: 1219px;
                        }   
                </style>
        </head>
        <body>
                <div id="container">
                        <div id="show">
                                <div id="content">
                                        <p>  詞曰:滾滾長江東逝水,浪花淘盡英雄。是非成敗轉頭空:青山仿舊在,幾度夕陽紅。白髮漁樵江渚上,慣看秋月春風。一壺
濁酒喜相逢:古今多少事,都付笑談中。</p>
                                        <p/>
                                        <p>  話說天下大勢,分久必合,合久必分:周末七國分爭,并入於秦。及秦滅之後,楚、漢分爭,又并入於漢。漢朝自高祖斬白蛇
而起義,一統天下。後來光武中興,傳至獻帝,遂分為三國。推其致亂之由,殆始於桓、靈二帝。桓帝禁錮善類,崇信宦官。及桓帝崩,靈帝即位,大將軍竇武、太傅陳蕃,共相>輔佐。時有宦官曹節等弄權,竇武、陳蕃謀誅之,作事不密,反為所害。中涓自此愈橫。</p>
                                        <p/>
                                        <p>  建寧二年四月望日,帝御溫德殿。方陞座,殿角狂風驟起,只見一條大青蛇,從梁上飛將下來,蟠於椅上。帝驚倒,左右急救
入宮,百官俱奔避。須臾,蛇不見了。忽然大雷大雨,加以冰雹,落到半夜方止,壞卻房屋無數。建寧四年二月,洛陽地震;又海水泛溢,沿海居民,盡被大浪捲入海中。光和元>年,雌雞化雄。六月朔,黑氣十餘丈,飛入溫德殿中。秋七月,有虹見於玉堂;五原山岸,盡皆崩裂。種種不祥,非止一端。</p>
            
                                </div>
                        </div>
                </div>
        </body>
</html>


可以使用 Firefox 瀏覽器瀏覽上述內容,並且試著把視窗的高度縮小,可以發現並不會造成最後一列字被切一半。原理就是使用 css 來處理。另外一提的,#content 裡的 -moz-column-width 是跟我的視窗大小有關,測試時要改一下囉。


而 Moncle 使用的技巧就是上面的東西,再加上一個 Display Screen 的移換,將上述的 CSS 更新:


<style type="text/css">
    #container
    {
        width: 100%;
        height: 100%;
        position: relative;
    }
    #show {
        position: absolute;
        top: 1em; bottom: 1em; left: 1em; right: 1em;
        overflow: hidden;
    }
    #content
    {
        position: absolute;
        top: 0pt; bottom: 0pt; min-width: 200%;
        -moz-column-gap: 0pt; -moz-column-width: 320px;
    }
</style>


呈現的樣貌:


CSS Pagination


因此關於切換頁面的部份,就只是移換要顯示的框框位置,上例就是左邊為第一頁,右邊是第二頁