トップページから巡回して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 件のコメント:
コメントを投稿