2009年1月13日火曜日

download mass images 2

download mass images 1のつづき

トップページから巡回して4ジャンルから3枚ずつのリストの取得。

def parse(url):
print 'parse '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', href=re.compile(r'^/images/pub/\d+/\w+\.jpg$'))
return urljoin(url, a[-1].get('href'))

def images(url):
print 'image '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', {'class':'image_a'}, href=re.compile(r'^/backgrounds/\d+$'))
return map(lambda a:parse(urljoin(url, a.get('href'))), a[:3])

def pages(url):
print 'pages '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', href=re.compile(r'^/desktop/\w+\.php$'))
return map(lambda a:images(urljoin(url, a.get('href'))), a[:4])

print pages(sys.argv[1])

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

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


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

まずは、Queue。
Queue#getが呼ばれると待ちます。
中身が入るまで、死ぬまで待ち続けます。
中身が入ったら、嬉々として値を返します。
格納できる値はひとつずつです。
複数の値を入れたい場合は、タプルを使います。
これを利用して、Queueに呼び出してほしい関数と引数をタプルにして詰め込んでやります。
そして一定数のスレッドを、Queueから取り出しては実行する無限ループにします。
これでThreadPool完成です。
実装してみましょう。

import sys, re, urllib
from urlparse import urlparse, urljoin
from Queue import Queue
from threading import Thread, Lock
from BeautifulSoup import BeautifulSoup
THREAD_MAX = 6
class FFURLopener(urllib.FancyURLopener):
version = 'Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.9.0.5) Gecko/2008120122 Firefox/3.0.5'
urllib._urlopener = FFURLopener()
q = Queue()

def download(url):
print 'down '+url
urllib.urlretrieve(url, url[url.rfind('/')+1:])

def parse(url):
print 'parse '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', href=re.compile(r'^/images/pub/\d+/\w+\.jpg$'))
q.put((download, urljoin(url, a[-1].get('href'))))

def images(url):
print 'image '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', {'class':'image_a'}, href=re.compile(r'^/backgrounds/\d+$'))
map(lambda a:q.put((parse, urljoin(url, a.get('href')))), a[:3])

def pages(url):
print 'pages '+url
soup = BeautifulSoup(urllib.urlopen(url).read())
a = soup('a', href=re.compile(r'^/desktop/\w+\.php$'))
map(lambda a:q.put((images, urljoin(url, a.get('href')))), a[:4])

if __name__ == '__main__':
def loop():
while 1:
func, args = q.get()
try:
func(args)
except Exception, e:
print e
finally:
q.task_done()
for i in range(THREAD_MAX):
w = Thread(target=loop)
w.daemon = True
w.start()
pages(sys.argv[1])
q.join()

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

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

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

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

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

次へ続く

0 件のコメント: