xml.etree.ElementTreeを使ってRSSから記事のタイトルを取り出す

先週のエントリで、RSSフィードから記事のタイトルを表示するスクリプトを書いた。ただ、このときにはタイトルを取り出すのに正規表現を使ったから、記事のタイトルだけじゃなくてブログのタイトルも取り出されてしまっていた。
そこで、今回は xml.etree.ElementTree を使って記事のタイトルだけを取り出してみた。といっても使い方はよくわかってないんだけど。

cf. http://docs.python.jp/2/library/xml.etree.elementtree.html

import sys
import urllib
import xml.etree.ElementTree

url = sys.argv[1]

src = urllib.urlopen(url)
doc = xml.etree.ElementTree.parse(src)

for title in doc.findall(".//item/title"):
    print title.text

xml.etree.ElementTree.parse はファイル名またはファイルオブジェクトを受け取ってDOMを返してくれる。findallはXPath(?)を受け取ってエレメントを返してくれる。・・・らしい。よくわからないけどこれで何とかなった。

実行例:

^o^ > python get_feed2.py https://blog.panicblanket.com/feed
リストのスタック系メソッド
os.walk関数を使ってファイル数を列挙する
urllibモジュールの超簡単なサンプル(その3)
urllibモジュールの超簡単なサンプル(その2)
urllibモジュールの超簡単なサンプル
変数に関数と同じ名前をつけてはいけない
== 演算子と is 演算子
randomモジュール
fileinputモジュール
Rubyで点数を集計するとき、あなたはどうしてますか? をPythonで

リストのスタック系メソッド

Python でリストをスタックとして使おうとしたときにはちょっとやりにくい。popはあるけどpushがないし、shiftもunshiftもない。なのでちょっとメモ。Ruby のスタック系メソッドとの比較で書いておく。

Ruby Python
pop pop()
push(item) append(item)
shift pop(0)
unshift(item) insert(0, item)

実行例:

>>> l = ['a', 'b', 'c', 'd', 'e']
>>> l.pop()
'e'
>>> l
['a', 'b', 'c', 'd']
>>> l.append('E')
>>> l
['a', 'b', 'c', 'd', 'E']
>>> l.pop(0)
'a'
>>> l
['b', 'c', 'd', 'E']
>>> l.insert(0, 'A')
>>> l
['A', 'b', 'c', 'd', 'E']

os.walk関数を使ってファイル数を列挙する

osモジュールの wolk 関数はちょっと面白い。ディレクトリを指定して呼び出すと、中に含まれるディレクトリ名、サブディレクトリのリスト、ファイルのリストを返すジェネレータを返してくれる。典型的にはこうやって使うみたい。

import os

for base_dir, dirs, files in os.walk("."):
    何らかの処理

そこで、ディレクトリに含まれるファイル数を列挙するスクリプトを書いてみた。

import os
import sys

for base_dir, dirs, files in os.walk(sys.argv[1]):
    num = len(files)
    print "%8d %-30s" % (num, base_dir)

実行結果:

^o^ > python count_files.py .
      83  .
       8  .\.git
      10  .\.git\hooks
       1  .\.git\info
       1  .\.git\logs
       0  .\.git\logs\refs
       1  .\.git\logs\refs\heads
       0  .\.git\logs\refs\remotes
       1  .\.git\logs\refs\remotes\origin
       1  .\.git\logs\refs\remotes\u
       0  .\.git\objects
       2  .\.git\objects\00
       1  .\.git\objects\01
       3  .\.git\objects\02
       3  .\.git\objects\03
       2  .\.git\objects\04
       1  .\.git\objects\07
       1  .\.git\objects\0c
       2  .\.git\objects\0d
       1  .\.git\objects\0e
       1  .\.git\objects\0f
       3  .\.git\objects\10
       1  .\.git\objects\13
       2  .\.git\objects\14
       3  .\.git\objects\16
       2  .\.git\objects\17
       1  .\.git\objects\19
       2  .\.git\objects\1a
       3  .\.git\objects\1b
       2  .\.git\objects\1c
       1  .\.git\objects\1f
       2  .\.git\objects\21
       5  .\.git\objects\22
       5  .\.git\objects\23
       1  .\.git\objects\25
       1  .\.git\objects\29
       2  .\.git\objects\2c
       1  .\.git\objects\2e
       2  .\.git\objects\2f
       2  .\.git\objects\30
       1  .\.git\objects\33
       1  .\.git\objects\35
       1  .\.git\objects\36
       1  .\.git\objects\37
       1  .\.git\objects\39
       3  .\.git\objects\3b
       1  .\.git\objects\3c
       2  .\.git\objects\40
       1  .\.git\objects\41
       1  .\.git\objects\42
       3  .\.git\objects\43
       1  .\.git\objects\44
       1  .\.git\objects\49
       4  .\.git\objects\4a
       2  .\.git\objects\4b
       2  .\.git\objects\4d
       1  .\.git\objects\4e
       3  .\.git\objects\4f
       1  .\.git\objects\51
       2  .\.git\objects\52
       2  .\.git\objects\53
       2  .\.git\objects\54
       1  .\.git\objects\55
       1  .\.git\objects\56
       3  .\.git\objects\57
       2  .\.git\objects\58
       1  .\.git\objects\59
       2  .\.git\objects\5a
       3  .\.git\objects\5d
       2  .\.git\objects\5e
       2  .\.git\objects\5f
       1  .\.git\objects\61
       1  .\.git\objects\62
       1  .\.git\objects\66
       1  .\.git\objects\67
       3  .\.git\objects\68
       1  .\.git\objects\69
       3  .\.git\objects\6a
       2  .\.git\objects\6b
       2  .\.git\objects\6c
       1  .\.git\objects\6d
       1  .\.git\objects\6f
       1  .\.git\objects\70
       1  .\.git\objects\71
       1  .\.git\objects\72
       1  .\.git\objects\75
       1  .\.git\objects\76
       1  .\.git\objects\77
       1  .\.git\objects\78
       1  .\.git\objects\79
       1  .\.git\objects\7b
       1  .\.git\objects\7c
       2  .\.git\objects\7d
       1  .\.git\objects\7f
       2  .\.git\objects\80
       1  .\.git\objects\85
       2  .\.git\objects\86
       1  .\.git\objects\87
       1  .\.git\objects\88
       1  .\.git\objects\89
       1  .\.git\objects\8b
       2  .\.git\objects\8d
       1  .\.git\objects\8e
       1  .\.git\objects\8f
       1  .\.git\objects\91
       2  .\.git\objects\93
       1  .\.git\objects\94
       3  .\.git\objects\95
       1  .\.git\objects\96
       3  .\.git\objects\97
       1  .\.git\objects\98
       2  .\.git\objects\9a
       3  .\.git\objects\9b
       1  .\.git\objects\9c
       3  .\.git\objects\a0
       2  .\.git\objects\a3
       1  .\.git\objects\a6
       1  .\.git\objects\a7
       1  .\.git\objects\a9
       1  .\.git\objects\ac
       1  .\.git\objects\ad
       1  .\.git\objects\ae
       1  .\.git\objects\b0
       2  .\.git\objects\b1
       1  .\.git\objects\b3
       3  .\.git\objects\b4
       2  .\.git\objects\b7
       1  .\.git\objects\b9
       3  .\.git\objects\ba
       1  .\.git\objects\bb
       1  .\.git\objects\bd
       1  .\.git\objects\c1
       1  .\.git\objects\c2
       1  .\.git\objects\c4
       2  .\.git\objects\c6
       2  .\.git\objects\c8
       1  .\.git\objects\c9
       2  .\.git\objects\cb
       1  .\.git\objects\cd
       2  .\.git\objects\ce
       3  .\.git\objects\cf
       2  .\.git\objects\d0
       1  .\.git\objects\d3
       4  .\.git\objects\d4
       1  .\.git\objects\d7
       3  .\.git\objects\d8
       2  .\.git\objects\d9
       2  .\.git\objects\da
       1  .\.git\objects\db
       3  .\.git\objects\dc
       5  .\.git\objects\dd
       1  .\.git\objects\e0
       1  .\.git\objects\e2
       1  .\.git\objects\e3
       1  .\.git\objects\e6
       1  .\.git\objects\e7
       2  .\.git\objects\e9
       1  .\.git\objects\ea
       1  .\.git\objects\ec
       5  .\.git\objects\ed
       3  .\.git\objects\ee
       1  .\.git\objects\ef
       1  .\.git\objects\f0
       1  .\.git\objects\f1
       2  .\.git\objects\f2
       1  .\.git\objects\f3
       1  .\.git\objects\f4
       2  .\.git\objects\f5
       1  .\.git\objects\f8
       1  .\.git\objects\fc
       1  .\.git\objects\fd
       1  .\.git\objects\fe
       0  .\.git\objects\info
       0  .\.git\objects\pack
       0  .\.git\refs
       1  .\.git\refs\heads
       0  .\.git\refs\remotes
       1  .\.git\refs\remotes\origin
       1  .\.git\refs\remotes\u
       0  .\.git\refs\tags
      10  .\projecteuler

git のリポジトリのファイルが多いな。

urllibモジュールの超簡単なサンプル(その3)

RSSフィードを取得して記事のタイトルを表示する。

import sys
import re
import urllib

url = sys.argv[1]

src = urllib.urlopen(url).read()

for title in re.findall(r"", src):
    print title.decode("utf-8")

実行例:

^o^ > python get_feed.py https://blog.panicblanket.com/feed
blog.PanicBlanket.com
urllibモジュールの超簡単なサンプル(その2)
urllibモジュールの超簡単なサンプル
変数に関数と同じ名前をつけてはいけない
== 演算子と is 演算子
randomモジュール
fileinputモジュール
Rubyで点数を集計するとき、あなたはどうしてますか? をPythonで
Pythonで偽と見なされる値
エラトステネスの篩
ファイルの文字数、ワード数、行数を数える

タイトルの抜き出しを正規表現でやってるので、ブログのタイトルまで表示されてるけど、とりあえずはこんなものだろう。ちゃんとやるならXML解析をやるべきだろうな。

urllibモジュールの超簡単なサンプル(その2)

URL を指定してヘッダーを得る。

import urllib
import sys

url = sys.argv[1]

r = urllib.urlopen(url)
info = r.info()

for key in info.keys():
    print key, ":", info[key]

実行結果:

^o^ > python httphead.py https://blog.panicblanket.com/
date : Sun, 03 Feb 2013 00:12:51 GMT
connection : close
content-type : text/html; charset=UTF-8
x-pingback : https://blog.panicblanket.com/xmlrpc.php
server : Apache/2.2.23

urllibモジュールの超簡単なサンプル

コマンドラインから入力された url を data.html に保存する。

import urllib
import sys

url = sys.argv[1]

filename = "data.html"

urllib.urlretrieve(url, filename)

実行結果の data.html(の冒頭部分)

<!DOCTYPE html>
<!--[if IE 6]>
<html id="ie6" lang="ja">
<![endif]-->
<!--[if IE 7]>
<html id="ie7" lang="ja">
<![endif]-->
<!--[if IE 8]>
<html id="ie8" lang="ja">
<![endif]-->
<!--[if !(IE 6) | !(IE 7) | !(IE 8)  ]><!-->
<html lang="ja">
<!--<![endif]-->
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width" />
<title>変数に関数と同じ名前をつけてはいけない | blog.PanicBlanket.com</title>
<link rel="profile" href="http://gmpg.org/xfn/11" />
<link rel="stylesheet" type="text/css" media="all" href="https://blog.panicblanket.com/wp-content/themes/twentyeleven/style.css" />
<link rel="pingback" href="https://blog.panicblanket.com/xmlrpc.php" />
<!--[if lt IE 9]>
<script src="https://blog.panicblanket.com/wp-content/themes/twentyeleven/js/html5.js" type="text/javascript"></script>
<![endif]-->
<link rel="alternate" type="application/rss+xml" title="blog.PanicBlanket.com » フィード" href="https://blog.panicblanket.com/feed" />
<link rel="alternate" type="application/rss+xml" title="blog.PanicBlanket.com » コメントフィード" href="https://blog.panicblanket.com/comments/feed" />
<link rel="alternate" type="application/rss+xml" title="blog.PanicBlanket.com » 変数に関数と同じ名前をつけてはいけない のコメントのフィード" href="https://blog.panicblanket.com/archives/605/feed" />
<link rel='stylesheet' id='crayon_style-css'  href='https://blog.panicblanket.com/wp-content/plugins/crayon-syntax-highlighter/css/crayon_style.css?ver=2.1.1' type='text/css' media='all' />
<link rel='stylesheet' id='crayon_global_style-css'  href='https://blog.panicblanket.com/wp-content/plugins/crayon-syntax-highlighter/css/global_style.css?ver=2.1.1' type='text/css' media='all' />
<link rel='stylesheet' id='crayon-theme-classic-css'  href='https://blog.panicblanket.com/wp-content/plugins/crayon-syntax-highlighter/themes/classic/classic.css?ver=2.1.1' type='text/css' media='all' />
<link rel='stylesheet' id='crayon-font-monaco-css'  href='https://blog.panicblanket.com/wp-content/plugins/crayon-syntax-highlighter/fonts/monaco.css?ver=2.1.1' type='text/css' media='all' />
<link rel='stylesheet' id='wp-syntax-css-css'  href='https://blog.panicblanket.com/wp-content/plugins/wp-syntax/wp-syntax.css?ver=3.5.1' type='text/css' media='all' />
<link rel='stylesheet' id='thickbox-css'  href='https://blog.panicblanket.com/wp-includes/js/thickbox/thickbox.css?ver=20121105' type='text/css' media='all' />
<link rel='stylesheet' id='amazonjs-css'  href='https://blog.panicblanket.com/wp-content/plugins/amazonjs/amazonjs.css?ver=0.1beta3c' type='text/css' media='all' />
<script type='text/javascript' src='https://blog.panicblanket.com/wp-includes/js/jquery/jquery.js?ver=1.8.3'></script>
<script type='text/javascript'>
/* <![CDATA[ */
var CrayonSyntaxSettings = {"version":"2.1.1","is_admin":"0","ajaxurl":"https:\/\/blog.panicblanket.com\/wp-admin\/admin-ajax.php","prefix":"crayon-","setting":"crayon-setting","selected":"crayon-setting-selected","changed":"crayon-setting-changed","special":"crayon-setting-special","orig_value":"data-orig-value","debug":""};
var CrayonSyntaxStrings = {"copy":"\u8cbc\u308a\u4ed8\u3051\u306b\u30b3\u30d4\u30fc\u3001%s\u306b%s\u3092\u62bc\u3057\u3066\u3001","minimize":"Click To Expand Code"};
/* ]]> */
</script>
<script type='text/javascript' src='https://blog.panicblanket.com/wp-content/plugins/crayon-syntax-highlighter/js/util.js?ver=2.1.1'></script>
<script type='text/javascript' src='https://blog.panicblanket.com/wp-content/plugins/crayon-syntax-highlighter/js/crayon.js?ver=2.1.1'></script>
<script type='text/javascript' src='https://blog.panicblanket.com/wp-includes/js/comment-reply.min.js?ver=3.5.1'></script>
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="https://blog.panicblanket.com/xmlrpc.php?rsd" />
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="https://blog.panicblanket.com/wp-includes/wlwmanifest.xml" /> 
<link rel='prev' title='== 演算子と is 演算子' href='https://blog.panicblanket.com/archives/600' />
<meta name="generator" content="WordPress 3.5.1" />
<link rel='canonical' href='https://blog.panicblanket.com/archives/605' />
<link rel='shortlink' href='https://blog.panicblanket.com/?p=605' />
	<style type="text/css">.recentcomments a{display:inline !important;padding:0 !important;margin:0 !important;}</style>
<style type="text/css" id="custom-background-css">
body.custom-background { background-color: #e2e2e2; }
</style>
</head>

変数に関数と同じ名前をつけてはいけない

Project Eulerのある問題を解いてるときにはまったのでメモ。
整数と文字列からスコアを計算してその合計を求める問題なんだけど、途中でこういうコードを書いた。

def name_score(num, str):
    v = sum(map((lambda c: ord(c) - 64), str))
    return num * v

print name_score(1, "FOO")

names = ["ANDY", "BILL", "CHARLIE"]

n = 0
sum = 0
for name in names:
    n += 1
    score = name_score(n, name)
    sum += score

print sum

name_score がスコアを計算する関数で、6行目と16行目(forブロックの中)の2箇所で呼び出している(6行目はちゃんと動くかの確認用)。ところが、これを実行するとこうなる。

^o^ > python a.py
36
Traceback (most recent call last):
  File "a.py", line 16, in 
    score = name_score(n, name)
  File "a.py", line 2, in name_score
    v = sum(map((lambda c: ord(c) - 64), str))
TypeError: 'int' object is not callable

1回目の呼び出し(6行目)ではちゃんとスコアが計算されているのに、2回目(forブロックの中)ではエラーになって、’int’オブジェクトは callable じゃないと怒られる。
これで小一時間ほども悩んだけど、問題は13行目のsumという変数と2行目で使っているsum関数の名前が同じことだった。

Pythonでは、定義した関数をほかの変数に代入することができる。

>>> def hello(name):
...     print "Hello,", name
...
>>> hello("Andy")
Hello, Andy
>>> greeting = hello
>>> greeting("Bill")
Hello, Bill

それはいいんだけど、逆に関数名にほかの値、たとえば整数を代入することもできてしまう。

>>> hello = 1
>>> hello("Charlie")
Traceback (most recent call last):
  File "", line 1, in 
TypeError: 'int' object is not callable

hello には 1 を代入してしまったので、関数呼び出しのつもりがエラーになってしまっている。上で紹介したコードでも13行目で sum に 0 を代入しているので、同じことが起こったわけだ。
結局、変数名を変えたら期待通りに動いてくれた。

def name_score(num, str):
    v = sum(map((lambda c: ord(c) - 64), str))
    return num * v

print name_score(1, "FOO")

names = ["ANDY", "BILL", "CHARLIE"]

n = 0
sum_of_score = 0
for name in names:
    n += 1
    score = name_score(n, name)
    sum_of_score += score

print sum_of_score
^o^ > python b.py
36
282

Python では関数もオブジェクトだからかもしれないけど、普通の変数名と関数名の間に区別がないようだ。

今日の結論:変数に関数と同じ名前をつけてはいけない。

== 演算子と is 演算子

どっちも等しいかどうかの比較演算子だけど、簡単に言うと、== は値として等しいか(同値性)で、is はオブジェクトとして等しいか(同一性)の判定をする。だから次のようにaとbに別々に代入した場合では、値が等しくてもオブジェクトとしては等しくないので is での比較の結果は False になる。

>>> a = (1,2,3)
>>> b = (1,2,3)
>>> a == b
True
>>> a is b
False

id関数でオブジェクトのIDをみても、べつのオブジェクトだということがわかる。

>>> id(a)
5089160
>>> id(b)
4602696

一方で、bにaを代入した場合には、オブジェクトとしても等しいので、== でも is でも True になる。

>>> a = (1,2,3)
>>> b = a
>>> a == b
True
>>> a is b
True

ちなみに否定の場合は is not。

>>> a is b
True
>>> a is not b
False

randomモジュール

CodeIQの問題を解くのに random モジュールを調べたのでメモ。

cf. http://docs.python.jp/2.7/library/random.html

使った関数

random.randrange は指定された範囲の整数からランダムな整数を返す。

>>> import random
>>> random.randrange(1, 100)
51

random.sample は母集団のシーケンスから選ばれたリストをかえす。リストの長さを指定する。

>>> random.sample(range(1,101), 10)
[54, 3, 5, 58, 24, 18, 47, 57, 62, 40]

使ってない関数からいくつか

random.randint は整数a,bを指定しa≦N≦bであるような整数Nを返す。random.randrangeよりもこっちを使ったほうがよかったかも。

>>> random.randint(1,100)
20

random.choice は空でないシーケンスから要素をひとつ返す。

>>> random.choice(range(10))
8

random.shuffle はシーケンスxを直接かき混ぜる。

>>> x = range(10)
>>> x
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
>>> random.shuffle(x)
>>> x
[6, 2, 3, 8, 5, 4, 1, 7, 9, 0]

random.random は値域[0.0,1.0)からランダムな浮動小数点数を返す。

>>> random.random()
0.7121347944194496

とりあえずこんなとこかな。

fileinputモジュール

コマンドライン引数で指定したファイルや標準入力からの入力を簡単に処理できるモジュール。
fileinput.input で各ファイルから1行ずつ読み込んでくれる。

次の例は、ファイルの各行をファイル名と行番号つきで出力する。

import fileinput

for line in fileinput.input():
    print fileinput.filename(), fileinput.filelineno(), ":", line.rstrip("\n")

実行例:

^o^ > python file_input.py fib.py file_read.py
fib.py 1 : def fib():
fib.py 2 :     a = 1
fib.py 3 :     b = 1
fib.py 4 :     while True:
fib.py 5 :         yield a
fib.py 6 :         a, b = b, a+b
fib.py 7 :
fib.py 8 :
fib.py 9 : i = fib()
fib.py 10 :
fib.py 11 : for c in range(10):
fib.py 12 :     print i.next()
fib.py 13 :
file_read.py 1 : import sys
file_read.py 2 :
file_read.py 3 : filename = sys.argv[1]
file_read.py 4 : print filename
file_read.py 5 : print "--"
file_read.py 6 :
file_read.py 7 : file = open(filename, "r")
file_read.py 8 : contents = file.read()
file_read.py 9 : print contents
file_read.py 10 : file.close()
file_read.py 11 :

標準入力へリダイレクトした場合:

^o^ > python file_input.py < fib.py
<stdin> 1 : def fib():
<stdin> 2 :     a = 1
<stdin> 3 :     b = 1
<stdin> 4 :     while True:
<stdin> 5 :         yield a
<stdin> 6 :         a, b = b, a+b
<stdin> 7 :
<stdin> 8 :
<stdin> 9 : i = fib()
<stdin> 10 :
<stdin> 11 : for c in range(10):
<stdin> 12 :     print i.next()
<stdin> 13 :

パイプを使った場合:

^o^ > type fib.py | python file_input.py
<stdin> 1 : def fib():
<stdin> 2 :     a = 1
<stdin> 3 :     b = 1
<stdin> 4 :     while True:
<stdin> 5 :         yield a
<stdin> 6 :         a, b = b, a+b
<stdin> 7 :
<stdin> 8 :
<stdin> 9 : i = fib()
<stdin> 10 :
<stdin> 11 : for c in range(10):
<stdin> 12 :     print i.next()
<stdin> 13 :