忍者ブログ
     2008年11月14日 開始
[1] [2] [3]
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

これは引用だよ
http://tawasi.infogami.com/development/pysqlite_1

Development taoy NG (Next Generation)

Python + SQLite、面白い

いやぁ、Python + SQLite の組み合わせって、なかなか面白そうだねぇ。 Debian では、sqlite3 ってパッケージと、python-pysqlite2 ってパッケージを入れてやれば、それで動作する。

from pysqlite2 import dbapi2 as sqlite

って書いて、Python モジュールをロードしておけば、後は、

con = sqlite.connect("mytest.db")

って感じで接続できちゃう。でもって、

cur = con.cursor()
cur.execute("select * from table1;")

とかで、SQLite3 に対して SQL 文を発行してデータを取得できちゃう。それも Python のオブジェクトとして取得。なので、後はフツーに Python のオブジェクトとして扱える。例えば、

print cur.fetchall()

で、全データ一覧表示。簡単。

んで、execute メソッドの引数がリテラルである必要は当然ないので、

SELECT = "select * from table1 order by id"

なんて定義しておけば、

con.execute(SELECT)

で実行できてしまう。良く使う SELECT 文とか、INSERT 文の部品とか、イチイチ書いてると間違いやすいもんはこうしてあらかじめ決まった名前で登録しちゃえば良いんだな。そうすりゃ、プログラム中のどこでもそれを使えちゃう。

便利だ。

もしかしてもしかすると、これぐらいのことはフツーのプログラミング言語だったら当り前の事なのかもしれないけどさ、私が今迄に自分自身で書いてきたもんだと、こういうのがちょっと面倒だったのだった。喰い物については他人の過去を多少は断罪出来るかもしれんけど、プログラミングについてはまるっきりの悪食だった、ってことかもしれませんね。スマンこってす(誰にだ?)

で。

SQLite ってのが、クライアントサーバ型の RDBMS で _ ない _ お蔭で、ちょっと変わった接続を実現できる。

con = sqlite.connect(":memory:")

これで、メモリ上に SQLite のデータベースを新規に展開しちゃう。私が作ろうとしてるような、利用者が極端に少ないような Web アプリケーションだったら、ユーザがログインしてきたときに、ディスク上のファイルにあるデータをメモリ上にデータベースとして展開しちゃえる、ってことだよね。でもって、python のカーソルもやっぱりメモリ上に展開されるから、そりゃぁ動作も速いだろうよ。

もちろん、大規模で権限の複雑な、要は整合性が可能なかぎり完全である必要のあるような、トランザクションベースの Webアプリケーション、それも同時接続ユーザが数千人以上、なぁんてもんだったら、これをスケーラブルに実現するなんらかの方法と云うか手段を考慮する必要はあるけれど、コト、私がやろうと思ってることに関して云えば、これはけっこうお誂え向きのプラットフォームでございますのではないかしらん。

うーむ。

もちょっと、いろいろと試して悩んでみよう。

PR

「seraphyの日記」からの引用だよ
http://d.hatena.ne.jp/seraphy/20060927
 

そこで、さっそく試してみた。

Python2.5では、sqlite3というモジュールをインポートするだけで準備完了である。

基本的な使い方(DMLトランザクション処理)

SQLiteは、DerbyやHSQLDBと同様に「インメモリ・データベース」として動作することができる。

揮発性の、コネクションを閉じるとデータも消えてしまうものだが、テストプログラムや、スタンドアロンアプリケーションの中で一時データの集計などを行う場合には便利だと思われる。

この「インメモリ・データベース」で、基本的な使い方を実験してみた。

私自身がPython使いではないので無駄(もしかすれば間違いも)あるかもしれないが、

使ってみた感触では、意外と簡単というか、APIはよく出来ている。

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Python2.5以降のみ
# 13.13 sqlite3 -- DB-API 2.0 interface for SQLite databases および、
# http://www.python.org/dev/peps/pep-0249/
# を参照のこと。

# SQLiteの利用
import sqlite3

# テーブル内容の一覧表示
def dump(conn):
    cur = conn.cursor()
    try:
        cur.execute("select idx, val from testtbl order by idx")
        for row in cur:
            # カーソルは、fetchone,fetchallなどで取得できるほか、
            # それ自身をレコードのリストとして列挙可能である。
            print row
    finally:
        cur.close()


# インメモリデータベースとしてコネクションを作成する。
conn = sqlite3.connect(":memory:")

# 指定がないばあいは、最初のSQL文実行でトランザクションが開始する。(beginは必要ない。)
# Autocommitにするばあいは、isolation_levelをNoneにする。
try:
    # DDLを実行する。
    # executescript()はセミコロンで区切り複数のSQL文を流し込める。
    # (コネクション・オブジェクトに定義されているexecuteメソッドは、一時カーソルを作成して
    # 実行する、コンビニエンスメソッドである。)
    # SQLite3は、Integer/Real/Text/BLOB(Binary Large Object)のみサポート。
    # それ以外の型は、類推して、いずれかに割り当てられる。
    conn.executescript("""create table testtbl(
        idx integer primary key,
        val varchar2(512));""")

    # 「カーソル」は読み取りに限らず、SQLの実行のすべてを行う。
    # (トランザクションは、コネクションで制御する。)
    cur = conn.cursor()
    finished = False
    try:
        for n in range(1, 100):
            # 「?」はバインド変数で、引数をしてタプルなどの列挙可能なオブジェクトを渡す。
            # (タプルのタプルを渡すと、複数行の操作になる。)
            # SQLite3は「INTEGER PRIMARY KEY」のカラムを空にすると自動で割当てる。(ROWID)
            # http://www.sqlite.org/faq.html#q1
            cur.execute("insert into testtbl(val) values(?)", (str(n),))
        finished = True
        conn.commit()
    except:
        if not finished:
            # commitに失敗した場合は、トランザクションはロールバックされて無効になっている(はず)
            conn.rollback()
        raise
    finally:
        cur.close()

    # 内容の確認
    dump(conn)
except Exception, ex:
    print ex
finally:
    conn.close()

#
# インメモリデータベースをもう一度開いてみる。(テーブルが未定義と言われる = 消えている。)
conn2 = sqlite3.connect(":memory:")
try:
    dump(conn2)
except Exception, ex:
    print ex
finally:
    conn2.close()

マルチスレッドでのデータベースへの書き込みアクセス

次に、実際にファイルに書き出しながらマルチスレッドでアクセスを試してみた。

SQLiteはスレッドセーフであるが、connect単位で独立させる必要があるとのこと。

つまり、データベースファイルには複数スレッドからアクセスしてもよいが、そのコネクションは共有してはならない、ということ。

また、コネクション・オブジェクトそのものがトランザクションを管理しているようである。

このあたりはJDBCも同様であるから、とくに驚きはない。

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Python2.5以降のみ

import threading
import sqlite3

# データベースファイル名
# SQLiteのデータストアは単一のファイルである。
DBNAME = "threadingtest.db"

# データベースの準備
def init_db():
    conn = sqlite3.connect(DBNAME)
    try:
        # テーブルを作成する
        conn.executescript("""create table testtbl(
            idx integer primary key, tname varchar(128), val integer);""")
    except Exception, ex:
        # 作成に失敗したら、作成済みと想定して、既存データを削除する
        conn.execute("delete from testtbl")
        conn.commit()
    finally:
        conn.close()

# コネクションを操作するスレッド
class MyThread(threading.Thread):
    def run(self):
        # SQLiteは、コネクション単位でスレッドセーフである。
        # つまり、コネクションやカーソルをスレッドをまたいで使うことは出来ない。
        # ロックのタイムアウトは、60秒に設定。
        conn = sqlite3.connect(DBNAME, timeout=60000)
        # 1件づつ暗黙でコミットする。(Autocommit)
        conn.isolation_level = None
        try:
            cur = conn.cursor()
            try:
                for n in range(1, 10):
                    cur.execute(
                        "insert into testtbl(tname, val) values(?, ?)",
                        (self.getName(), n))
            finally:
                cur.close()
        except Exception, ex:
            # タイムアウト時間までにロックを獲得できなかった場合は例外が発生する。
            print "thread=%s /exception=%s" % (self.getName(), str(ex))
        finally:
            conn.close()

# メイン
def main():
    try:
        init_db()
        
        # スレッドを作成する
        threads = []
        for n in range(1, 10):
            t = MyThread()
            t.setName("MyThread:%d" % n)
            threads.append(t)

        # スレッドを開始する
        for t in threads:
            t.start()

        # すべてのスレッドの終了を待機する
        for t in threads:
            t.join()

        print "done."

    except Exception, ex:
        print ex

# 書き込み結果の表示
def verify_test():
    conn = sqlite3.connect(DBNAME)
    try:
        cur = conn.cursor()
        try:
            cur.execute("select count(*) from testtbl")
            cnt = cur.fetchone()[0]
            print "count=%d" % cnt
            cur.execute("""select tname, count(val) from testtbl
                group by tname order by tname""")
            for row in cur:
                print row
        finally:
            cur.close()
    except Exception, ex:
        print ex
    finally:
        conn.close()

# 実行
main()
verify_test()

実際に試した感触では、どうやら、トランザクションが開始されるとジャーナルファイルが作成され、コミットするとジャーナルファイルがデータベースファイルにマージされたあと削除され、このジャーナルが存在する期間は、別のスレッドはブロックされてしまうようなのだ。

SERIALIZEレベルの分離レベル、ということ?

いや、これはテーブルロックどころか、データベース全体をロックしているように見える。

まあ、これは見た感触であって、ただの推論だが。

結論

SQLiteは簡単に使えるうえに簡単に壊れたりすることはないようだが、

実感として、マルチスレッドでの書き込みが頻発する状況では、かなり遅いと考えてよさそうである。

しかし、主要な用途をクライアントサイドのスタンドアロンなアプリケーションのバッキングストアとして利用する分には、なんの問題もないだろう。

(BLOBのサイズ、データベースファイルの上限も、かなりでかい。まず、使い切ることはないだろう。)

そのうえで、共有しても壊れない、ファイルコピーで持ち運べる手軽さは魅力的である。

今後、Pythonでプログラムを書くときは、これを使う選択肢も覚えておくべきだろう。

C/C++バインドであるが、Windows版はDLL一個で動作するらしい。

さらに、msvcrt.dllという、VCのランタイムライブラリ以外に依存しない、というからまったく手軽なものである。

ぜひ、C/C++SQLiteを使う方法も習得してみたいと思わされた。

これは、まちがいなく優れたデータベースの1つであって、覚えて損はないだろう。

 

http://www.atmarkit.co.jp/bbs/phpBB/viewtopic.php?topic=14758&forum=12&8

 

ファイルのコピーと移動処理をjava.io.Fileクラス等を使用して、
読む→別ファイルに書くでコピー、書いた後に読み込みファイルを削除することによって
移動処理としていましたが、移動処理は、FileクラスのrenameToメソッドを使用しても
できるので、読み込み書き込みのコードを記述しなくて済むので、以下のようなコードで
実装するようになりました。

java.io.File file = new java.io.File( "c:\\abc.txt" ); // 移動元
java.io.File file2 = new java.io.File( "c:\\tmp\\abc.txt" ); // 移動先
boolean ret = file.renameTo( file2 );


そこで、コピー処理も同様にできないものかと相当の動作をするメソッドを探しましたが
見当たりませんでした。コピー処理に相当するメソッドは無いから、コピーする場合は
読む→別ファイルに書くという方法しかないのでしょうか?
また、皆さんがコピーと移動処理を実装する際は、どのように行われていますでしょうか

http://www.site-cooler.com/kwl/javascript/15.htm#15-4


大文字と小文字の区別(iオプション)


 JavaScriptが大文字小文字を区別するように正規表現でもデフォルトでは大文字と小文字を別として扱います。つまりアルファベットを記述したければa-zA-Zと二通りの指定をしなければ間違った指定になってしまいます。しかしJavaScriptもPerl同様にオプションをつけることによって大文字小文字を区別させないでマッチングすることが可能になります。


文字列.match(RegExpオブジェクト,"i") ;
/パターン/i


 [ sample ]



 [ source ]

<script language="JavaScript">
<!--
function func3(){

    str = "abc";

    if(str.match(/ABC/)){
        alert("オプション無し");
    }
    if(str.match(/ABC/i)){
        alert("オプション指定");
    }

}
//-->
</script>
<form>
<input type=button value="  ♂  " onClick="func3()">
</form>


 iオプションをつけてマッチングしています。これにより文字列が小文字の"abc"、検索対照が大文字の"ABC"となっていますがマッチすることが出来ます。

str.match(/ABC/i)



繰り返しのマッチング(gオプション)


 matchメソッドは [ 一致した文字(文字列)があった場合、一致した文字(文字列)を返します ] と説明しましたが、一致する文字(文字列)が複数あった場合はどうなるのでしょう・・・・ こうなった場合は一番はじめに見つかった文字列が返されることになります。しかしマッチした文字列を全部知りたいといった場合にちょっと困ってしまいます。そんなときにこのオプションを利用すればマッチングした文字(文字列)を全て配列として取得することが出来ます。


文字列.match(RegExpオブジェクト,"g") ;
/パターン/g


 [ sample ]


 [ source ]

<script language="JavaScript">
<!--
function func4(){

    str = "123456789";

    ret = str.match(/\w/g)

    alert("1番目:" + ret[0] + "\n" +
        "2番目:" + ret[1] + "\n" +
        "3番目:" + ret[2] + "\n" +
        "4番目:" + ret[3] + "\n" +
        "5番目:" + ret[4] + "\n" +
        "6番目:" + ret[5] + "\n" +
        "7番目:" + ret[6] + "\n" +
        "8番目:" + ret[7] + "\n" +
        "9番目:" + ret[8] + "\n");

}
//-->
</script>
<form>
<input type=button value="  ♂  " onClick="func4()">
</form>


 gオプションをつけてマッチングしています。これにより一回マッチングしても終了しないで全てに対してマッチングを行い、その結果を配列として戻すことになります。

ret = str.match(/\w/g)


 

#import urllib
import urllib2
import urlparse
import BeautifulSoup

url = 'http://mixi.jp'
url='http://hiw.oo.kawai-juku.ac.jp/nyushi/honshi/08/'
url='http://hiw.oo.kawai-juku.ac.jp/nyushi/honshi/07/t01.html'
soup = BeautifulSoup.BeautifulSoup(urllib2.urlopen(url))
base=url
#縲€url縺ョ蜿門セ・
for _a in soup.findAll('a'):
    url_fragments=_a.get('href')
    print 'url_fragments = ' + str(_a.get('href'))
    print '      urljoin = ' + urlparse.urljoin(base, url_fragments)

# 繝輔Ξ繝シ繝縺ョ蜿門セ・
for _a in soup.findAll('frame'):
    url_fragments=_a.get('src')
    print 'url_fragments = ' + str(_a.get('src'))
    print '      urljoin = ' + urlparse.urljoin(base, url_fragments)

===以下は訂正版===

# -*- coding: utf-8 -*-

import urllib
import BeautifulSoup
#import urllib
import urllib2
import urlparse
import BeautifulSoup

url = 'http://mixi.jp'
url='http://hiw.oo.kawai-juku.ac.jp/nyushi/honshi/08/'
url='http://hiw.oo.kawai-juku.ac.jp/nyushi/honshi/07/t01.html'
soup = BeautifulSoup.BeautifulSoup(urllib2.urlopen(url))
base_url='http://hiw.oo.kawai-juku.ac.jp/nyushi/honshi/07/t01.html'

base_url='http://hiw.oo.kawai-juku.ac.jp/nyushi/honshi/07/'
#print soup
#print soup.prettify
#縲€url縺ョ蜿門セ・
for _a in soup.findAll('frame'):
    url_fragments=_a.get('src')
    print 'url_fragments =' , url_fragments

    print 'url_fragments = ' , str(url_fragments)
    print '      urljoin = ' + urlparse.urljoin(base_url, str(url_fragments))


makedirs( path[, mode])
再帰的なディレクトリ作成関数です。 mkdir() に似ていますが、末端 (leaf) となるディレクトリを作成するために必要な中間の全てのディレクトリを作成します。末端ディレクトリがすでに存在する場合や、作成ができなかった場合には error 例外を送出します。mode の標準の値は 0777 (8進)です。 (Windows システムにのみ関係することですが、Universal Naming Convention パスは、`\\host\path' という書式のパスです) バージョン1.5.2 以降で新規追加された 仕様です。

 

http://www.yukun.info/blog/2008/07/python-string.html
 

  1. #!/usr/bin/python  
  2. # coding: UTF-8  
  3.  
  4. # 文字列の検索 | index(), reindex()の使い方  
  5.  
  6. s1 = 'Hello, Jan !' 
  7.  
  8. # 引数(パターン)が1文字の文字列  
  9. try:  
  10.     i = s1.index('l')  # 引数で与えられた文字列を先頭から探索した場合の出現位置を返す  
  11. except ValueError:  
  12.     i = None           # 存在しない場合は例外ValueErrorがなげれられる  
  13. print i, s1[i]  
  14.  
  15. try:  
  16.     i = s1.rindex('l'# 末尾から探索した場合の最初の出現位置を返す  
  17. except ValueError:  
  18.     i = None 
  19. print i, s1[i]  
  20. print 
  21.  
  22. # 引数(パターン)が2文字以上の文字列  
  23. str = 'lo' 
  24. try:  
  25.     i = s1.index(str)  
  26. except ValueError:  
  27.     i = None 
  28. print i, s1[i]  
  29.  
  30. try:  
  31.     i = s1.rindex(str)  
  32. except ValueError:  
  33.     i = None 
  34. print i, s1[i]  
  35. print 
  36.  
  37. # 引数で与えられた文字列(パターン)が存在しない場合  
  38. try:  
  39.     i = s1.index('Max')  
  40. except ValueError:  
  41.     i = None 
  42. print i 

実行結果

2 l
3 l

3 l
3 l

None

リファレンス


 

11.2.2 cgi モジュールを使う

先頭には "import cgi" と書いてください。"from cgi import *"と書いてはいけません -- このモジュールは以前のバージョンとの互換性を持たせるために内部で呼び出す名前を多数定義しており、それらがユーザの名前空間に存在する必要はありません。

新たにスクリプトを書く際には、以下の一行を付加するかどうか検討してください:

 

import cgitb; cgitb.enable()

これによって、特別な例外処理が有効にされ、エラーが発生した際にブラウザ上に詳細なレポートを出力するようになります。ユーザにスクリプトの内部を見せたくないのなら、以下のようにしてレポートをファイルに保存できます:

 

import cgitb; cgitb.enable(display=0, logdir="/tmp")

スクリプトを開発する際には、この機能はとても役に立ちます。 cgitb が生成する報告はバグを追跡するためにかかる時間を大きく減らせるような情報を提供してくれます。スクリプトをテストし終わり、正確に動作することを確認した後はいつでも cgitb の行を削除できます。

入力されたフォームデータを取得するには、 FieldStorage クラスを使うのが最良の方法です。このモジュールで定義されている他のクラスのほとんどは以前のバージョンとの互換性のためのものです。インスタンス生成は引数なしで必ず 1 度だけ行います。これにより、標準入力または環境変数からフォームの内容が読み出されます (どちらから読み出されるかは、CGI 標準に従って設定されている複数の環境変数の値によって決められます)。インスタンスが標準入力を使うかもしれないので、インスタンス生成を行うのは一度だけにしなければなりません。

FieldStorage のインスタンスは Python の辞書のようにインデクスを使った参照ができ、標準の辞書に対するメソッド has_key()keys() をサポートしています。組み込みの関数 len() もサポートされています。空の文字列を含むフォームのフィールドは無視され、辞書上にはありません; そういった値を保持するには、 FieldStorage のインスタンスを生成する時にオプションの keep_blank_values キーワード引数を true に設定してください。

例えば、以下のコード (Content-Type: ヘッダと空行はすでに出力された後とします) は name および addr フィールドが両方とも空の文字列に設定されていないか調べます:

 

form = cgi.FieldStorage()
if not (form.has_key("name") and form.has_key("addr")):
    print "<H1>Error</H1>"
    print "Please fill in the name and addr fields."
    return
print "<p>name:", form["name"].value
print "<p>addr:", form["addr"].value
...further form processing here...

ここで、"form[key]" で参照される各フィールドはそれ自体が FieldStorage (または MiniFieldStorage 。フォームのエンコードによって変わります) のインスタンスです。インスタンスの属性 value フィールドの文字列値になります。 getvalue() メソッドはこの文字列値を直接返します。 getvalue() は 2 つめの引数にオプションの値を与えることができ、リクエストされたキーが存在しない場合に返されるデフォルトの値になります。

入力されたフォームデータに同じ名前のフィールドが二つ以上あれば、 "form[key]" で得られるオブジェクトは FieldStorageMiniFieldStorage のインスタンスではなく、それらインスタンスからなるリストになります。同じく、この状況では、 "form.getvalue(key)" は文字列を要素とするリストを返します。もしこうした状況が起こりうると思われるなら (HTML のフォームに同じ名前をもったフィールドが複数含まれているのなら) 、組み込み関数 isinstance() を使って、返された値が単一のインスタンスかインスタンスのリストかを調べてください。例えば、以下のコードは任意の数のユーザ名フィールドを結合し、コンマで分割された文字列にします:

 

value = form.getvalue("username", "")
if isinstance(value, list):
    # Multiple username fields specified
    usernames = ",".join(value)
else:
    # Single or no username field specified
    usernames = value

フィールドがアップロードされたファイルを表す場合、value 属性または getvalue() メソッドを介してアクセスされた値はファイルの内容を全て文字列としてメモリ上に読み込みます。これは望ましくない機能かもしれません。アップロードされたファイルがあるかどうかは filename 属性および file 属性のいずれかで調べることができます。その後、以下のようにして file 属性から落ち着いてデータを読み出せます:

 

fileitem = form["userfile"]
if fileitem.file:
    # It's an uploaded file; count lines
    linecount = 0
    while 1:
        line = fileitem.file.readline()
        if not line: break
        linecount = linecount + 1

現在ドラフトとなっているファイルアップロードの標準仕様では、一つのフィールドから (再帰的な multipart/* エンコーディングを使うことで) 複数のファイルがアップロードされる可能性を受け入れています。この場合、アイテムは辞書形式の FieldStorage アイテムとなります。複数ファイルかどうかは type 属性が multipart/form-data (または multipart/* にマッチする他の MIME 型) になっているかどうか調べることで判別できます。この場合、トップレベルのフォームオブジェクトと同様にして再帰的に個別処理できます。

フォームが ``古い'' 形式で入力された場合 (クエリ文字列または application/x-www-form-urlencoded データの単一の部分の場合) アイテムは実際には MiniFieldStorage クラスのインスタンスになります。この場合、listfile 、および filename 属性は常に None になります。

 

http://www.uselesspython.com/tutorials/BatteriesIncluded.php
より

 

>>> import calendar
>>> dir(calendar)

>>> import calendar
>>> for thingy in dir(calendar):
.......print thingy
 

 

Excelワークシート関数を有効活用しよう
http://officetanaka.net/excel/vba/speed/s9.htm
Sub Test2()
    Dim i As Long, Ans As Long
    For i = 1 To 5000
        Ans = WorksheetFunction.Sum(Range("M1:M100"))
    Next i
End Sub
Sub Test2()
    Dim fc  As Variant, j As Long, Ans As Long
    For j = 1 To 30
        Ans = WorksheetFunction.VLookup("田中亨", Range("A1:B10001"), 2, False)
    Next j
End Sub

Sub Test2()
    Dim fc  As Variant, j As Long, Ans As Long
    For j = 1 To 30
        Ans = WorksheetFunction.VLookup("田中亨", Range("A1:B10001"), 2, False)
    Next j
End Sub

Sub Test3()
    Dim fc  As Variant, j As Long, Ans As Long
    For j = 1 To 30
        Set fc = Range("A1:A1001").Find(What:="田中亨")
        If Not fc Is Nothing Then Ans = fc.Offset(0, 1).Value
    Next j
End Sub

絶対Withステートメントを使うべきです
http://officetanaka.net/excel/vba/speed/s4.htm

オブジェクト型変数に格納

Sub Test3()
    Dim i As Integer, j As Integer, myCell As Range
    Set myCell = ActiveWorkbook.Worksheets(1).Range("A1")
    For i = 1 To 300
        For j = 1 To 10
                myCell.Value = myCell.Value + j
        Next j
    Next i
End Sub

BeautifulSoupでHTML解析

| | コメント(0) | トラックバック(0)

BeautifulSoupが素晴らしいので
他にあまり解説サイトもないし、
簡単に使えそうなものを羅列してみた。

ただしデフォルトエンコーディングを設定している事が前提。

BeautifulSoupに渡されたHTMLは、
utf-8に文字コードを変換され自動で綺麗に生成しなおされる。
prettify()を使用すると、綺麗なソースに生成しなおされる。
※BeautifulSoup内で処理される場合は、このソースが元になる。

壊れたタグを修復という訳ではなさそうだが
改行やインデントを作り直してくれる。
Webアプリケーションとしても、かなり使えそう。

# -*- coding: utf-8 -*-

import string, re, urllib, urllib2
from BeautifulSoup import BeautifulSoup

url = 'http://www.google.co.jp/search?hl=ja&num=100&q='
# url = 'http://search.yahoo.co.jp/search?p=' # Yahooの場合

# クライアントを生成
opener = urllib2.build_opener()

# PythonのUAがGoogleに弾かれているようなので変更
opener.addheaders = [('User-Agent', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)')]

q = 'Perl使いのPythonちゃん' # 検索クエリ
q = urllib.quote(q) # クエリをURLエンコード

# Google検索結果を取得
print url+q
html = opener.open(url+q).read()

# HTMLソースをBeautifulSoupに渡す
soup = BeautifulSoup(html)

# エンコーディングを自動で取得
enc = soup.originalEncoding
print "Encoding : %s" % enc

# ソースを出力
# ※unicode(text, 'utf-8')はutf-8の文字をunicodeに変換
# print unicode(soup.prettify(), enc)

# ※UnicodeEncodeError: 'cp932' codec can't encode character...は、
# Windowsの拡張文字でエラーが起こっているのかも。
# その場合は'mbcs'というCodecを使う
#print unicode(soup.prettify(), enc).encode('mbcs')

### タグへのアクセス方法 - 1 (DOMもどきな扱い方)
print u'DOMもどきな扱い方'

# contents[0]で、その階層の1番上の要素にアクセス
# .name で、要素のタグ名を取得(小文字)
print soup.contents[0].name
# 結果はu'html'となる

# 更に子となる要素へアクセス(結果 : u'head')
print soup.contents[0].contents[0].name

# 要素を変数に格納
head = soup.contents[0].contents[0]

# 親要素にアクセス(結果 : u'html')
print head.parent.name

# 自分を含む、それ以下の階層の次の要素にアクセス(結果 : u'meta')
print head.next.name

# 同階層の次の要素にアクセス(結果 : u'body')
print head.nextSibling.name

# 複合(結果 : u'table')
print head.nextSibling.contents[0].name
# 複合(結果 : u'blockquote')
print head.nextSibling.contents[0].nextSibling.name

### タグへのアクセス方法 - 2 (よりDOMっぽい扱い方)

print u'DOMもどきな扱い方'

# タグ名でアクセス
titleTag = soup.html.head.title

# そのまま出力するとタグごと表示
print u"%s" % titleTag

# string でinnerText
print u"%s" % titleTag.string

# 要素数を取得
print len(soup('font'))

# 一致したタグの一番目を返す(属性で絞込みもできる)
soup.find('a')

# 一致したタグをリストにして返す(属性で絞込みもできる)
print soup.findAll('a', href="/")

# 正規表現さえ使える
print soup.find('a', href=re.compile('^/'))

# リストとしてアクセスも可能
print soup('a', href=re.compile('^/'))[1]

# 要素の値を返す
print soup('a', href=re.compile('^/'))[0]['href']

# 覚書 : xmlを解析する場合はBeautifulStoneSoup(xml)を使う?(未確認)

DOMのような感じで、置き換えたりタグを生成したりも
できるようだが、現状では必要ないのでまた今度。

ようし、飽きた!
これらを駆使してサンプルでも作ってみる。

トラックバック(0)

このブログ記事を参照しているブログ一覧: BeautifulSoupでHTML解析

このブログ記事に対するトラックバックURL: http://mt.blog-slime.com/mt-tb.cgi/8

コメントする

サインインしてください。 (匿名でコメントする)

このブログ記事について

このページは、adminが2007年7月27日 14:00に書いたブログ記事です。

ひとつ前のブログ記事は「PythonでHTML解析(HTMLParserより優れたパーサ)」です。

次のブログ記事は「Perlで言う、join関数が使い難い。」です。

最近のコンテンツはインデックスページで見られます。過去に書かれたものはアーカイブのページで見られます。

Excel(エクセル) VBA入門:ワークシート関数を利用する


http://www.eurus.dti.ne.jp/~yoneyama/Excel/vba/vba_ws_kansu.html
 

VBAでセルに数式を入力するにはFormulaプロパティを使います。

  • 【例1】C1:C5に数式=SUM(A1:B1)を入力します。
    • Sub rei13_1()
        Worksheets("Sheet1").Range("C1:C5").Formula = "=SUM(A1:B1)"
      End Sub

 

【例3-2】VLOOKUP関数がエラーとなる場合(対処法1)

  • エラー処理へ分岐します。
    Sub rei13_8()
    Dim myR
    On Error GoTo ErrorHandler
      With Worksheets("Sheet1")
        myR = Application.WorksheetFunction.VLookup(.Range("C1"), .Range("A1:B7"), 2, False)
        .Range("D1").Value = myR
      End With
    Exit Sub
    ErrorHandler:
      Worksheets("Sheet1").Range("D1").Value = "該当無し"
    End Sub
     

http://www.python.jp/Zope/articles/tips/regex_howto/regex_howto_3
 

3.1. 正規表現のコンパイル

正規表現は RegexObject インスタンスにコンパイルされます。このインスタンスは、パターン・マッチや文字列交換のような、様々な働きをするメソッドを持ちます。

>>> import re
>>> p = re.compile('ab*')
>>> print p
<re.RegexObject instance at 80b4150>

re.compile() に引数 flags を指定することで、特別な設定や文法の変更を実現するのに使われます。使用可能な設定は後で見ていくことにして、ここでは例だけ挙げておきます。

>>> p = re.compile('ab*', re.IGNORECASE)

正規表現は re.compile() に文字列として渡されます。これは、正規表現は Python 本体には含まれていないので、正規表現を表すのに特化した文法が用意されていないからです。 (正規表現を全く必要としないアプリケーションも存在するので、正規表現を内包することによる言語仕様をふくらませる必要はありません) その代わり re モジュールは、string モジュールと同様、単なる C 拡張モジュールになっています。

3.2. バックスラッシュだらけ

既に述べたように、正規表現はバックスラッシュ文字("\")で、特殊な形式を表したり、特殊文字に特殊な意味を持たせないのに使います(訳注: 日本語フォントセットでは、バックスラッシュは円記号で表示されます)。この規則は、文字列リテラル中で Python がバックスラッシュを同じ目的で使用するのと衝突します。

LaTeX 文書で見られるような文字列 "\section" とマッチする正規表現を書きたいとしましょう。プログラムのソースコードの中で、何と書くか導くために、マッチさせたい文字列から考えます。次にバックスラッシュを前置して、バックスラッシュやメタ文字をエスケープしなければいけません。その結果、文字列 "\\section" を得ます。 re.compile() に渡されるべき文字列は、\\section でなければいけません。しかし、Python の文字列リテラルでこれを表現するには、両方のバックスラッシュを再度エスケープしなければならないのです。

文字列 段階
\section マッチさせるテキスト文字列
\\section re.compile 用にバックスラッシュをエスケープ
"\\\\section" 文字列リテラル用にバックスラッシュをエスケープ

つまり、リテラルのバックスラッシュにマッチさせるには、正規表現文字列として '\\\\' と書かなければいけません。なぜなら正規表現は "\\" でなくてはいけませんし、Python の文字列リテラル中で、各バックスラッシュは "\\" と書かれなければいけないからです。バックスラッシュが繰り返し現れる正規表現中では、これが多くのバックスラッシュの繰り返しを誘発することになり、その文字列を理解することが困難になります。

解決策は Python の生の文字列(raw string)表記を正規表現に使うことです。 "r" を前置した文字列リテラル内では、バックスラッシュは特別な扱いを受けません。ですから r"\n" は、"\" と "n"の 2 文字からなる文字列ですが、"\n" は 1 つの改行文字です。 Python のコードで正規表現を書くとき、この生の文字列表記がよく用いられます。

一般の文字列 生の文字列
"ab*" r"ab*"
"\\\\section" r"\\section"
"\\w+\\s+\\1" r"\w+\s+\1"

3.3. マッチングを行う

ここまでで、コンパイルされた正規表現を represent するオブジェクトを持っているのですが、これで何をすればいいのでしょう。 RegexObject インスタンスは、いくつかのメソッドと属性を持っています。最も重要なものだけをここで扱います。完全なリストを手に入れるには、ライブラリ・リファレンスをあたってください。

メソッド / 属性 目的
match 正規表現が、文字列の先頭でマッチするかどうか調べる
search 文字列を走査し、正規表現がマッチする位置を探す
split 正規表現がマッチする全ての位置で、文字列をリストに分割する
sub 正規表現がマッチする全ての部分文字列を見つけ出し、別の文字列に置換する
subn sub()と同じ。ただし置換の個数を制限することができる

全くマッチしなければ、これらのメソッドは None を返します。見つかれば MatchObject インスタンスを返します。このインスタンスは、マッチした部分文字列や、それがどこで始まり、どこで終わるか、などの情報をもっています。

re モジュールを使って、対話的に実験することで勉強していきましょう。 (Tkinter が使えるなら redemo.py を見るのもよいでしょう。これは Python ディストリビューションに含まれているデモです。正規表現を入力すると、マッチしたか失敗したかを表示してくれるのです。コンパイルされた正規表現をデバッグしようとするときには、redemo.py が非常に有用であると思われます)

最初に、Python インタプリタを起動して、re をインポートしてから、正規表現をコンパイルします。

Python 1.5.1 (#6, Jul 17 1998, 20:38:08)	[GCC 2.7.2.3] on linux2
Copyright 1991-1995 Stichting Mathematisch Centrum, Amsterdam
>>> import re
>>> p = re.compile('[a-z]+')
>>> p
<re.RegexObject instance at 80c3c28>

これで正規表現 [a-z]+ に対して、様々な文字列のマッチングを試みることができます。 + は「1 回または、それ以上の繰り返し」を意味するので、空の文字列はまったくマッチしないはずです。この場合 match() の戻り値 None はインタプリタに何の出力も表示させません。結果をはっきりさせたければ、明示的に match() の結果をを表示すこともできます。

>>> p.match("")
>>> print p.match("")
None

今度は、"tempo" のような、マッチする文字列を試してみましょう。この場合、match()MatchObject を返すので、後で使えるように、この結果を変数に保存しておきます。

>>> m = p.match('tempo')
>>> print m
<re.MatchObject instance at 80c4f68>

この MatchObject から、マッチした文字列に関する情報を得ることができます。 MatchObject インスタンスには、いくつかのメソッドと属性があります。そのうち特に重要なものは以下のとおりです。

メソッド / 属性 目的
group() 正規表現にマッチした文字列を返す
start() マッチした文字列が始まる位置を返す
end() マッチした文字列が終わる位置を返す
span() マッチした文字列の位置を (start, end) のタプルで返す

これらのメソッドを使ってみれば、どういうことか明確になるでしょう。

>>> m.group()
'tempo'
>>> m.start(), m.end()
(0, 5)
>>> m.span()
(0, 5)

group() は正規表現にマッチした部分文字列を返します。 start()end() は、マッチした部分の開始位置と終了位置を返します。 span() は開始位置と終了位置の両方を、タプルにして返します。 match メソッドは、正規表現が文字列の先頭でマッチするかどうかをチェックするだけなので、start() の戻り値は常に 0 です。しかし RegexObject インスタンスの search メソッドは、文字列全部に渡って調べるので、マッチの開始位置が 0 でない場合もあります。

>>> print p.match('::: message')
None
>>> m = p.search('::: message') ; print m
<re.MatchObject instance at 80c9650>
>>> m.group()
'message'
>>> m.span()
(4, 11)

実際のプログラムでは、MatchObject を変数に保存し、その値が None かどうかチェックするのが最も一般的なスタイルで、次のようなになります。

p = re.compile( ... )
m = p.match('string goes here')
if m:
    print 'Match found: ', m.group()
else:
    print 'No match'

3.4. モジュールレベルの関数

必ずしも RegexObjectを作ったり、そのメソッドを呼び出す必要はありません。 re モジュールは、match(), search(), sub(), などのトップレベル関数も提供します。これらの関数は、RegexObject メソッドと対応していて、第 1 引数が正規表現文字列であるような引数を取り、なおかつ None または MatchObject インスタンスを返します。

>>> print re.match(r'From\s+', 'Fromage amk')
None
>>> re.match(r'From\s+', 'From amk Thu May 14 19:12:10 1998')
<re.MatchObject instance at 80c5978>

この隠れ蓑の下で、これらの関数はあなたに代わって RegexObject を作り、それに対して適切なメソッドを呼び出すだけです。また、コンパイルされたオブジェクトをキャッシュに保存し、後で、同じ正規表現をを使うときの呼び出しが速くなります。

モジュールレベルの関数を使うべきでしょうか、RegexObject を作ってからメソッドを呼び出すべきでしょうか。どれだけ頻繁にその正規表現を使わうのか、あなたのコーディングスタイル次第です。もし正規表現がコードの中で、たった 1 箇所だけで使うのなら、モジュール関数がおそらく便利でしょう。一方、プログラムがたくさんの正規表現を包含していたり、同じ正規表現を別々の箇所で何度も使ったりするなら、全ての定義を始めの 1 箇所に集めて、正規表現をコンパイルする価値があるでしょう。標準ライブラリの xmllib.py を例にとってみましょう。

ref = re.compile( ... )
entityref = re.compile( ... )
charref = re.compile( ... )
starttagopen = re.compile( ... )

(私はふつう、1 回使うだけであっても、コンパイル済みオブジェクトを好みます。私と同じくらい、徹底するようになる人もいるようです)

3.5. コンパイル時のフラグ

コンパイル時にフラグを指定することで、正規表現が動作するときの振るまいを変更できます。 re モジュールのフラグには、IGNORECASE のようなロングネームと、Iのように1文字で表すショートネームが使えます。 (もし Perl のパターン修飾子に慣れているなら、ショートネームは Perl と同じ文字を使うと言えば分かりやすいかも知れません。 re.VERBOSE のショートネームは re.X です) 複数のフラグは、OR ビット演算で指定できます。たとえば、re.I | re.M は、IM の両方を指定することになります。

以下に使用できるフラグの表を示します。詳しい説明は後述します。

フラグ 意味
DOTALL, S . が改行も含めて、全ての文字とマッチするように指定する
IGNORECASE, I 大文字小文字を区別しない
LOCALE, L ロケールを考慮してマッチングを行う
MULTILINE, M 複数行にマッチング。これは ^$ に影響する
VERBOSE, X 冗長な正規表現(もっと、きれいで分かりやすくまとめられる表現)を有効にする。
I, IGNORECASE
大文字小文字を区別せずにマッチングを行う。文字クラスとリテラル文字列は大文字小文字を無視して、マッチングが行われる。例えば、[A-Z] は小文字にもマッチし、Spam は、"Spam"、"spam"、"spAM" にマッチします。このフラグ指定では、カレント・ロケールを考慮しません。
L, LOCALE
\w, \W, \b, \B をカレント・ロケールに依存して扱います。ロケールは、扱う自然言語の違いを考慮に入れるのを手助けする目的で、C 言語ライブラリに含まれる機能です。たとえば、フランス語を処理するときには、単語にマッチさせるのに \w+ と書けることを望むでしょう。しかし、\w は文字クラス [A-Za-z] のみとマッチし、"é" や "ç" とはマッチしません。システムが適切に設定されていて、フランス語が選択されていれば、特定の C 言語の関数が、プログラムに対して "é" も文字扱いするように教えるのです。正規表現をコンパイルするときに LOCALE フラグを設定すると、コンパイル済みオブジェクトが \w を扱うときに、こういう C 言語の関数を使うことになります。この場合処理は遅くなりますが、期待どおり \w+ がフランス語の単語にマッチします。

 

M, MULTILINE
通常、^ は行頭にのみ、$ は(もし存在すれば)newline 直前の行末にのみマッチします。このフラグを指定すると、^ は文字列の先頭と、文字列中で newline の直後に続く行頭にマッチします。同様に、メタ文字 $ は、文字列の最後と(newline 直前の)行末にマッチします。
S, DOTALL
特殊文字 "." が改行文字も含めて、あらゆる文字にマッチするようにします。このフラグを指定しないと、"." は改行文字以外のあらゆる文字にマッチします。
X, VERBOSE
このフラグを指定すると、柔軟な書式で、正規表現を読みやすく書くことができます。文字クラスやエスケープされていないバックスラッシュで生成された空白文字を除いて、正規表現中の空白文字は無視されます。これにより、正規表現を分かりやすくまとめたり、インデントしたりできるのです。また、正規表現中にコメントを入れることもできます。文字クラスや、エスケープされていないバックスラッシュによって生成されている場合を除き、"#" 以降がコメントになります。コメントは単に無視されます。

re.VERBOSE を使った正規表現の例を以下に示します。とても読みやすいと思いませんか?

charref = re.compile(r"""
  &#                            # 数値型の開始
  (?P<char>
        [0-9]+[^0-9]                # 10進法
        | 0[0-7]+[^0-7]             # 8進法
        | x[0-9a-fA-F]+[^0-9a-fA-F] # 16進法
)
""", re.VERBOSE)

冗長表現を指定しないと、この正規表現は次のようになります。

charref = re.compile("&#(?P<char>[0-9]+[^0-9]"
"|0[0-7]+[^0-7]"
"|x[0-9a-fA-F]+[^0-9a-fA-F])")

上の例では、Python が文字列リテラルを自動的に連結するのを利用して、正規表現を小さな部分に分割していますが、それでも re.VERBOSE を使った場合に比べて、分かりにくくなります。

http://www.builderau.com.au/program/python/soa/Build-a-basic-Web-scraper-in-Python/0,2000064084,339281476,00.htm

The Web holds a truly awe inspiring amount of information, which we're all usually happy enough to access through our Web browser. There are times, however, where your programs need to access it, and you don't want to worry about the details of the HTML mark-up.

There are thousands of HTML (or SGML, or XML) parsing libraries for hundreds of languages out there, but for this example we use a Python library called BeautifulSoup which takes care of almost all of the work for you. The BeautifulSoup library is an extremely helpful tool to have at your disposal, since it not only gives you functions to search and modify your parse tree, but it also handles the broken and malformed HTML you're likely to encounter on an average Web page.

You can download the library at its Web page. It also resides in some popular software repositories, such as the apt-get repository used in the Debian and Ubuntu distributions.

We'll write a Web scraper that prints all the displayed text contained within <p> tags. This is a very simple implementation that is easy to trip up, but it should be enough to demonstrate how using the library works.

First up, we need to retrieve the source of the page that we want to scrape. The following code will take an address given on the command line and put the contents into the variable html:

 

import urllib2,sys

address = sys.argv[1]

html = urllib2.urlopen(address).read()

 

Then we need to build a parse tree using BeautifulSoup:

 

from BeautifulSoup import BeautifulSoup

soup = BeautifulSoup(html)

 

At this point the code has already been cleaned up and converted to unicode by the BeautifulSoup library, you can print soup.prettify() to get a clean dump of the source code.

Instead, what we want is to print all of the text, without the tags, so we need to find out which parts of the parse tree are text. In BeautifulSoup there are two kinds of nodes in the parse tree, plain text is represented by the NavigableString class, whereas Tags hold mark-up. Tags are recursive structures, they can hold many children, each being either other Tags or NavigableStrings.

We want to write a recursive function that takes part of the tree: if it is a NavigableString print it out, otherwise, runs the function again on each subtree. Because we can iterate over a tag's children simply by referring to that tag this is easy.

 

from BeautifulSoup import NavigableString

def printText(tags):
	for tag in tags:
		if tag.__class__ == NavigableString:
			print tag,
		else:
			printText(tag)

 

Then we just need to run that function on all the <p> tags. We can use BeautifulSoup's in built parse tree searching functions to retrieve all of them:

 

printText(soup.findAll("p"))

 

That's it. You've got a fully functioning, if basic, HTML scraper. For more help with searching the parse tree, look up the BeautifulSoup documentation.

The full code for this example is as follows:

Listing A

 

from BeautifulSoup import BeautifulSoup,NavigableString
import urllib2,sys

address = sys.argv[1]

html = urllib2.urlopen(address).read()

soup = BeautifulSoup(html)

def printText(tags):
        for tag in tags:
                if tag.__class__ == NavigableString:
                        print tag,
                else:
                        printText(tag)
        print ""

printText(soup.findAll("p"))

print "".join(soup.findAll("p", text=re.compile(".")))


忍者ブログ [PR]
お天気情報
カレンダー
04 2024/05 06
S M T W T F S
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
リンク
フリーエリア
最新CM
最新TB
プロフィール
HN:
No Name Ninja
性別:
非公開
バーコード
ブログ内検索
P R
カウンター
ブログの評価 ブログレーダー