2009年1月13日火曜日

download mass images 2

download mass images 1のつづき

トップページから巡回して4ジャンルから3枚ずつのリストの取得。
  1. def parse(url):  
  2.     print 'parse '+url  
  3.     soup = BeautifulSoup(urllib.urlopen(url).read())  
  4.     a = soup('a', href=re.compile(r'^/images/pub/\d+/\w+\.jpg$'))  
  5.     return urljoin(url, a[-1].get('href'))  
  6.   
  7. def images(url):  
  8.     print 'image '+url  
  9.     soup = BeautifulSoup(urllib.urlopen(url).read())  
  10.     a = soup('a', {'class':'image_a'}, href=re.compile(r'^/backgrounds/\d+$'))  
  11.     return map(lambda a:parse(urljoin(url, a.get('href'))), a[:3])  
  12.   
  13. def pages(url):  
  14.     print 'pages '+url  
  15.     soup = BeautifulSoup(urllib.urlopen(url).read())  
  16.     a = soup('a', href=re.compile(r'^/desktop/\w+\.php$'))  
  17.     return map(lambda a:images(urljoin(url, a.get('href'))), a[:4])  
  18.   
  19. print pages(sys.argv[1])  

前回の設定を引き継ぎ、上記のようなコードを書けば一覧が取得できます。
% python wally.py http://www.backgroundsarchive.com/desktop/|xargs -n1 -P6 wget
このコマンドがが動いて、取得してくるように変更することは難しいことではないでしょう。

しかし、このコマンドは遅いですね。
ダウンロードリストを生成する時間がかかりすぎです。
Python内でもスレッドを使いたい。


そして本題
これが書きたかっただけですが、前置き長い。

まずは、Queue。
Queue#getが呼ばれると待ちます。
中身が入るまで、死ぬまで待ち続けます。
中身が入ったら、嬉々として値を返します。
格納できる値はひとつずつです。
複数の値を入れたい場合は、タプルを使います。
これを利用して、Queueに呼び出してほしい関数と引数をタプルにして詰め込んでやります。
そして一定数のスレッドを、Queueから取り出しては実行する無限ループにします。
これでThreadPool完成です。
実装してみましょう。
  1. import sys, re, urllib  
  2. from urlparse import urlparse, urljoin  
  3. from Queue import Queue  
  4. from threading import Thread, Lock  
  5. from BeautifulSoup import BeautifulSoup  
  6. THREAD_MAX = 6  
  7. class FFURLopener(urllib.FancyURLopener):  
  8.     version = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5'  
  9. urllib._urlopener = FFURLopener()  
  10. q = Queue()  
  11.   
  12. def download(url):  
  13.     print 'down  '+url  
  14.     urllib.urlretrieve(url, url[url.rfind('/')+1:])  
  15.   
  16. def parse(url):  
  17.     print 'parse '+url  
  18.     soup = BeautifulSoup(urllib.urlopen(url).read())  
  19.     a = soup('a', href=re.compile(r'^/images/pub/\d+/\w+\.jpg$'))  
  20.     q.put((download, urljoin(url, a[-1].get('href'))))  
  21.   
  22. def images(url):  
  23.     print 'image '+url  
  24.     soup = BeautifulSoup(urllib.urlopen(url).read())  
  25.     a = soup('a', {'class':'image_a'}, href=re.compile(r'^/backgrounds/\d+$'))  
  26.     map(lambda a:q.put((parse, urljoin(url, a.get('href')))), a[:3])  
  27.   
  28. def pages(url):  
  29.     print 'pages '+url  
  30.     soup = BeautifulSoup(urllib.urlopen(url).read())  
  31.     a = soup('a', href=re.compile(r'^/desktop/\w+\.php$'))  
  32.     map(lambda a:q.put((images, urljoin(url, a.get('href')))), a[:4])  
  33.   
  34. if __name__ == '__main__':  
  35.     def loop():  
  36.         while 1:  
  37.             func, args = q.get()  
  38.             try:  
  39.                 func(args)  
  40.             except Exception, e:  
  41.                 print e  
  42.             finally:  
  43.                 q.task_done()  
  44.     for i in range(THREAD_MAX):  
  45.         w = Thread(target=loop)  
  46.         w.daemon = True  
  47.         w.start()  
  48.     pages(sys.argv[1])  
  49.     q.join()  

これで4ジャンルから3枚ずつ最大サイズを取得します。

問題がいくつかあります。
1. タプル
2. 出力の乱れ

まずタプル。
読みづらいですね。
私はたいてい括弧を読み飛ばしてしまい、悩みます。
あと、引数の数の変更にも対応できません。

q.put((func, [a1], {}))

func, args, kwargs = q.get()
func(*args, **kwargs)
にすれば引数に対応できますが、煩雑です。
ここは、ラッパーを書きましょう。

次へ続く

0 件のコメント: