RSSからURLを引っ張るchoiceFotoUrlRandom

RSSからURLを取る版。やはりこっちのがいい。

#-*- coding: utf-8 -*-
from __future__ import with_statement
from xml.dom.minidom import parseString
import urllib2
import random

FOTOURL='http://f.hatena.ne.jp/takerunba/food/rss'
RDF_URI='http://www.w3.org/1999/02/22-rdf-syntax-ns#'

def choiceFotoUrlRandom():
        res = urllib2.urlopen(FOTOURL)
        rss = parseString(res.read())
        res.close()
        return random.choice(rss.getElementsByTagNameNS(RDF_URI,'li'))\
                .getAttributeNS(RDF_URI, 'resource')

if __name__ == '__main__':
        print choiceFotoUrlRandom()

はてなの動作がおかしい

投稿してリロードすると出なくなったり、一部の文字列が消えたりとよくわからない挙動をする。はてな記法回りがあやしいけど、よくわからん。

朝起きて落ち着いて考えると、自分の記法の書き方がまずかったのだろうと思い直す。
まあ、システムのせいにした方が気分がいいので、やっぱりダイアリーがおかしいことにする。

botを作ってみた

http://d.hatena.ne.jp//20081129/p1

よい題材だと思うので作るまでの過程を書いてみるです。
id:takerunbaさんはRubyPHPを勉強したいようなので、邪魔しないようにPythonで。RubyPHPも詳しくないし。
環境はUbuntu 8.10。標準でインストールされないものも使ってます。



その1 リポジトリを作る
あとで泣かないためにリポジトリを作る。なんでもいいけど、個人で使うなら導入が楽なものが良いです。
私はmercurialを使います。てなわけで、

$hg init quitter

コミットは適宜行います。

その2 設計する
設計と言っても、方針を決める程度です。
quitterの場合「一定時間毎に、あるWebページ上の特定のURLをtwitterに投稿する」が目的なので、以下の3つを作って組み合わせれば楽そうです。

  1. twitterに投稿する機能
  2. 一定時間毎に1の機能を動かす仕組み
  3. Webページを読んで、そこから目的のURLを切り出す機能
    1. Webページを読む機能
    2. URLを切り出す機能

その3 調査する
とりあえず、自分で何でもやろうとせず、似たようなものが既に無いか探します。
1のtwitter投稿機能はpython-twitterというものが使えそうです。
2は素朴な機能なので、標準ライブラリから探します。これが結構大変ですが、timeモジュールのsleep関数をループの中で使うとよさげです。
3はBeautifulSoupを使うのが定石でしょう。

その4 作る
まず簡単に一定時間毎にtwitterに投稿する機能を作ります。

python-twitterのドキュメントを眺めると、こうすると投稿できるみたい。

api = twitter.Api(username='twitter user', password='twitter pass') #ログイン
status = api.PostUpdate('I love python-twitter!') #ここで投稿
staus.text #戻り値には投稿されたテキストとかが入ってるみたい

タイマーの仕組みはこんな感じ

INTERVAL_TIME = 60.0
for i in range(0, 3):
        time.sleep(INTERVAL_TIME)
        #ここで投稿機能を呼ぶ

本番ではwhile Trueの無限ループを使うけど、まだ実験中なのでforループを使ってます。
INTERVAL_TIMEはとりあえず60(秒)にしたけど、これも本番では変えるつもりです。念のために書きますが、twitterAPIからのアクセスに制限をかけていますし、極端に頻繁なアクセスは迷惑、場合によっては業務妨害になります。注意しましょう。
さて、さっきの投稿手順をこのループに組み込めば、「定期的に投稿する」というbotの基礎は出来上がりです。

#-*- coding: utf-8 -*-
import time
import twitter

INTERVAL_TIME = 60.0
USERNAME = ''
PASSWORD = ''

def twitter_login():
        return twitter.Api(username=USERNAME, password=PASSWORD)

def post_twitter(post_status):
        twitter = twitter_login()
        twitter.PostUpdate(post_status)
        
for i in range(0, 3):
#while True:
        time.sleep(INTERVAL_TIME)
        post_twitter('TEST:hello twitter')

何となくApiインスタンスの作成を別にしてみました。将来USERNAMEとPASSWORDを別ファイルに書いたりするかもしれないし、他にもいろいろ考えて。無駄な細分化かもしれません。
これをquitter.pyという名前で保存しました。
あとはpost_twitter関数に渡す文字列を自動的に組み立てる関数があれば良いわけです。
てなわけで、WebページからURLを切り出す機能を作ります。

今回対象となるWebページは、はてなフォトライフなので、まずはそれを解析。
見てもらえば分かると思いますが、非常にわかりやすい作りです。
どうやらclass="fotolist"なul要素の下のa要素たちが取得したいURLを持っているようです。

BeautifulSoupを使うのは初めてなので、使用例を検索したところこんな感じらしい。

soup = BeautifulSoup(urllib2.urlopen(対象URL))
fotolist = soup.find('ul', {'class':'fotolist'})
fotoLinks = fotolist.findAll('a')

ページ取得後はDOMアクセスですな。簡単です。
ただ、これで取れたのはあくまでa要素なので、href属性からURLを取得します。ちなみにこのURLは相対です。なので、上の方を補って絶対パスにしてあげます。

FOTO_ROOT=http://f.hatena.ne.jp
urllist = []
for a in fotoLinks:
        urllist.append(FOTO_ROOT + a['href'])

さらにさらに、ここからランダムにURLを選びたいわけですね。Pythonのrandomモジュールには、リストや配列からランダムに要素を一つ取り出すというchoice関数というものがあります。

import random
random.choice(urllist)

さて、これらを組み立てましょう。

#-*- coding: utf-8 -*-
import urllib2
import random
from BeautifulSoup import BeautifulSoup


FOTO_ROOT = 'http://f.hatena.ne.jp' 
CONTENT_URL = 'http://f.hatena.ne.jp/目標'

def choiceFotoUrlRandom():
        soup = BeautifulSoup(urllib2.urlopen(CONTENT_URL))

        fotolist = soup.find('ul', {'class':'fotolist'})
        fotoLinks = fotolist.findAll('a')

        urllist = []
        for a in fotoLinks:
                urllist.append(FOTO_ROOT + a['href'])

        return random.choice(urllist)

if __name__ == '__main__':
        print choiceFotoUrlRandom()

私はこれをhatenafotoscraper.pyという名前で保存しました。
面倒な人はquitter.pyに混ぜても良いですが、別にしとくのをすすめます。

さて、quitter.pyも書き換えます。

#-*- coding: utf-8 -*-
import time
import twitter
from hatenafotoscraper import choiceFotoUrlRandom

INTERVAL_TIME = 3600.0
USERNAME = ''
PASSWORD = ''

def twitter_login():
        return twitter.Api(username=USERNAME, password=PASSWORD)

def post_twitter(post_status):
        twitter = twitter_login()
        twitter.PostUpdate(post_status)
        
#for i in range(0, 3):
while True:
        time.sleep(INTERVAL_TIME)
        post_twitter(choiceFotoUrlRandom())

やや荒っぽい作りですが、これで目的のものは完成です。実行すると、無限ループに入り、定期的に投稿を行います。
止める場合、C-cなどで落としてやります。

5 見直す
とりあえず動くものは出来ましたが、いろいろまずそうな点が見られます。作って終わりではなく、改良していきましょう。

例えば、投稿する度にApiインスタンスを作っていますが、無駄のような気がします。ログイン処理をしているのではなく、認証されたApiインスタンスを作っているのに、twitter_loginという名前は適切でしょうか?

今の作りだと、システム系の例外が起きたら普通に死ぬだけです。ただ死ぬのではなく、ログ機能を使ってメールで知らせてくれたら便利ですね。
他にログ関連では、投稿されたURLを時刻付きで記録するとよいかもしれません。

これくらい小さく単純であればいらないかもしれませんが、テストが無いというのはちょっと不安です。

他に、はてなフォトライフはページング処理されていますが、今のhatenafotoscraperは1ページ目しか見ません。これは改良すべきでしょう。
同じURLが頻繁に投稿されないよう、投稿したURLを保持しておいて、最近投稿されたかチェックする機構があると親切です。
そもそも、起動の度にURLを取りに行くのではなく、URLのリストを作るプログラムを別に動かした方が良いのかもしれません。
HTML経由でなくRSSを使った方が、ページ変更に強い作りになります。


botを作ったのは今回が初めてです。python-twitterも、BeautifulSoupも初めて使いましたが、全部作るのに大体2時間(途中夕食含む)です。ありものを組み合わせれば割と簡単に作れるわけです。
私はPythonを使いましたが、RubyPHPPerlでも同様だと思います。
あと、見直しに書いたように。URLはRSSから取るべきです。

Lignes de TempsはFlashで出来ている

起動すればわかるけど、Flash 8で作られている。
そのせいなのか、Windows版もMac版もUIが他から浮いている。
メニューがフランス語なので、うまく使えない。
これのネットアプリ版て無いのかな?下手するとニコ動になっちゃいそうだけど。

象徴の貧困について少し考える

社会の相当な部分を占める人々が今日、あらゆる感性的な体験を奪われており、世界人口の圧倒的多数を支配するようになったマーケティングによる感性の条件付けに完全に屈服しているからである。一方でそれに与せず今も実験を続けている層は、この条件付けによってうち沈んでいった者たちのことをもう諦めているといったありさまなのである。

実験というのは、感性的実験=芸術のこと。これについては後回し。

「泣ける」「萌え」「かわいい」といった、単語で全てを片付けられる感性は、おそらく貧困と言い切って構わないだろう。
ただ「泣ける」はともかくとして、「萌え」や「かわいい」という感性は本来、「人々」が発見した概念であり、これらの語が条件付けを破る契機にもなるのではないかと。
ケータイ小説という、まさにマーケティングによって生まれた形式であっても、かけらぐらいは内側から条件付けを食い破ろうという気概が含まれているのかもしれない。

問題は自家中毒的に、あるいは自縄自縛的に、「人々」が自らを条件付けし、自ら沈んでしまう場合が少なくないこと。
同調すること、何かに所属していることを実感することは、それが本質的に幻想だとしても快感なわけで。