2009-05-14 21 views
66

httpでPythonでいくつかのファイルをダウンロードする必要があります。それを行うには「スマート」な方法でPythonを使ってファイルをダウンロードするには?

最も明白な方法は、ちょうどurllib2のを使用している:

import urllib2 
u = urllib2.urlopen('http://server.com/file.html') 
localFile = open('file.html', 'w') 
localFile.write(u.read()) 
localFile.close() 

しかし、私はいくつかの方法で厄介なURLに対処する必要があります、次のように言う:http://server.com/!Run.aspx/someoddtext/somemore?id=121&m=pdf。ブラウザを介してダウンロードすると、そのファイルは人間が判読可能な名前を持ちます。 accounts.pdf

これをPythonで処理する方法はありますか?そのため、ファイル名を知り、スクリプトにハードコードする必要はありませんか?

Content-Disposition: attachment; filename="the filename.ext" 

あなたはそのヘッダーをつかむことができる場合、あなたは適切なファイル名を取得することができます。そのような

+3

サーバー上のファイル名は適切ですか?おそらく、これらのファイルには何らかの意味があるので、自分で名前を付けることができなければなりません。名前に意味がない場合は、ランダムな一意の名前を自分で作成してください(おそらく?) –

+0

私はファイル名を読みやすく、意味のあるものにしたいと思っています。問題は、スクリプトがテキストファイルからダウンロードするURLを取得し、URLが技術者以外の人によって追加され削除されることです。 – kender

答えて

40

ダウンロードスクリプトは、ファイルに名前を付けるためにどのようなユーザーエージェントを伝えるヘッダをプッシュする傾向があります。

Content-Disposition-grabbingを提供するコードが少しあります。another threadがあります。

remotefile = urllib2.urlopen('http://example.com/somefile.zip') 
remotefile.info()['Content-Disposition'] 
+0

しかし、そうではないかもしれません。 –

+5

いいえ、プレーンファイルにリダイレクトされている可能性があります。しかし、それがほとんどのダウンロードスクリプトのようなものなら、彼らはコンテンツの処分を推進しています。ぜひチェックしてください。 – Oli

+0

普通のファイルにリダイレクトするのも簡単ですが、remotefile.urlで実際のURLにアクセスできますか? – kender

35

コメントにし、オリのanwser @基づき、私はこのような解決策を作った:

from os.path import basename 
from urlparse import urlsplit 

def url2name(url): 
    return basename(urlsplit(url)[2]) 

def download(url, localFileName = None): 
    localName = url2name(url) 
    req = urllib2.Request(url) 
    r = urllib2.urlopen(req) 
    if r.info().has_key('Content-Disposition'): 
     # If the response has Content-Disposition, we take file name from it 
     localName = r.info()['Content-Disposition'].split('filename=')[1] 
     if localName[0] == '"' or localName[0] == "'": 
      localName = localName[1:-1] 
    elif r.url != url: 
     # if we were redirected, the real file name we take from the final URL 
     localName = url2name(r.url) 
    if localFileName: 
     # we can force to save the file as specified name 
     localName = localFileName 
    f = open(localName, 'wb') 
    f.write(r.read()) 
    f.close() 

それがContent-処分からファイル名を取ります。 URLが存在しない場合は、URLのファイル名を使用します(リダイレクトが発生した場合は、最終的なURLが考慮されます)。上記の多くを組み合わせる

+9

これは役に立ちました。しかし、大きなファイルをメモリに保存せずに、「r」を「f」にコピーして見つけなければなりませんでした。 import shutil shutil.copyfileobj(r、f) – u0b34a0f6ae

+0

@ kaizer.se素晴らしい、これを指摘してくれてありがとう – kender

+4

うまくいきましたが、 'urllpl.unquote'を呼び出して' urlsplit(url)[2] 'をラップします。そうしないと、ファイル名はパーセントでエンコードされます。 'return basename(urllib.unquote(urlsplit(url)[2]))' – fjsj

23

が、ここではより多くのニシキヘビのソリューションです:

import urllib2 
import shutil 
import urlparse 
import os 

def download(url, fileName=None): 
    def getFileName(url,openUrl): 
     if 'Content-Disposition' in openUrl.info(): 
      # If the response has Content-Disposition, try to get filename from it 
      cd = dict(map(
       lambda x: x.strip().split('=') if '=' in x else (x.strip(),''), 
       openUrl.info()['Content-Disposition'].split(';'))) 
      if 'filename' in cd: 
       filename = cd['filename'].strip("\"'") 
       if filename: return filename 
     # if no filename was found above, parse it out of the final URL. 
     return os.path.basename(urlparse.urlsplit(openUrl.url)[2]) 

    r = urllib2.urlopen(urllib2.Request(url)) 
    try: 
     fileName = fileName or getFileName(url,r) 
     with open(fileName, 'wb') as f: 
      shutil.copyfileobj(r,f) 
    finally: 
     r.close() 
1

2ケンダー

if localName[0] == '"' or localName[0] == "'": 
    localName = localName[1:-1] 

それは安全ではありません - Webサーバはとして間違ってフォーマットされた名前を渡すことができます["file.ext]または[file.ext]]または空であってもローカル名[0]は例外を発生させます 正しいコードは次のようになります:

localName = localName.replace('"', '').replace("'", "") 
if localName == '': 
    localName = SOME_DEFAULT_FILE_NAME 
+2

さらに良い: 'local_name.strip( '\'" ') ' - これは始めと終わりであり、より簡潔である。 – koniiiik

0

wgetを使用する:

custom_file_name = "/custom/path/custom_name.ext" 
wget.download(url, custom_file_name) 

をurlretrieveを使用する:

urllib.urlretrieve(url, custom_file_name) 

urlretrieveも、ディレクトリ構造を作成していない場合が存在します。