33430


以下のような、urlの内容を保存する関数に、pathを引数にとってそこに保存するような機能をつけたいと思っています。

[code]

    def download(url):
        name = url.split("/")[-1]
        print(name)
        with open(name, "wb") as f:
            f.write(requests.get(url).content)
    
[/code]

以下のように書き換えましたが、この機能をこれから先に作る関数にもつけたい場合、どうするのが効率的でしょうか?  
同じようなものを何度も書かずに済む方法をお聞きしたいです。

[code]

    def download(url, path=None):
        ##ここから##
        p = "" 
        if (path != None and os.path.exists(path)):
            p = path
        ##ここまでのことです
        name = url.split("/")[-1]
        with open(p + name, "wb") as f:
            f.write(requests.get(url).content)
    
[/code]

デコレータなどを使うのでしょうか?方針がたたないので質問します。  
環境はpython3.6.0です。

3/21 14時追記  
この関数を他で呼び出したいということではなく、例えば以下のような先程の関数に似た関数を作るときに、pathに関する同じような処理をいちいち書くことを省略したいということです。

[code]

    def download_new(url, path=None):
        ##ここから##
        p = "" 
        if (path != None and os.path.exists(path)):
            p = path
        ##ここまでのことです
        name = url.split("/")[-1]
        with open(p + name + "_new.jpg", "wb") as f:
            f.write(requests.get(url).content)
    
[/code]


CC BY-SA 3.0
2017-03-20T12:55:51.437
33426
2017-03-21T05:19:53.780
2017-03-21T05:19:53.780
20533
20533
question
python
pythonでpathにファイルを書き込む関数の一般化


質問の意図が汲み取れてないと思いますが、「pathは任意で渡せる」のであれば書き込む前に、そのパス(ディレクトリ)が存在することを保証するのが良いと思いました。

あと、urlからデータが必ず取得できるとは限らないので、ダウンロードする際に、ちゃんとそのURLからデータが取得できてるかチェックしてあげるのが良いです。

[code]

    def download(url, save_path=None):
        res = requests.get(url)
        if res.status_code != 200:
            res.raise_for_status()
    
        filename = url.split('/')[-1]
        if save_path:
            os.makedirs(save_path, exist_ok=True)
            filename = os.path.join(save_path, filename)
    
        with open(filename, "wb") as f:
            f.write(res.content)
    
        return filename
    
[/code]

> 以下のように書き換えましたが、この機能をこれから先に作る関数にもつけたい場合、どうするのが効率的でしょうか?  
>  同じようなものを何度も書かずに済む方法をお聞きしたいです。

関数化できてしまえば、他のPythonスクリプトからimportすることは可能なので適宜必要なところで呼び出しあげればいいのではないでしょうか。

[code]

    def new_func():
        save_path = "/path/to"
        filepath = download('http://example.com', save_path)
    
        # ダウンロードしたファイルの中身をチェック
        with open(filepath, 'r') as fp:
            print(fp.read())
    
[/code]


CC BY-SA 3.0
2017-03-20T14:00:36.110
33429
2017-03-20T14:00:36.110
2168
33426
answer


デコレータを使う例を以下に示します。

[code]

    import os
    import requests
    
    def download_path(f):
      import functools
      @functools.wraps(f)
      def dlpath(url, path="."):
        if not ( os.path.isdir(path) and
                 os.access(path, os.W_OK|os.X_OK) ):
          path = "."
        return f(url, path)
      return dlpath
    
    @download_path
    def download(url, path):
      name = url.split("/")[-1]
      ## need to check `name'
      print(name)
      with open(os.path.join(path, name), "wb") as f:
        f.write(requests.get(url).content)
    
    ## Save to current directory
    ## download('http://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv')
    
    ## Save to /tmp directory
    ## download('http://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv', '/tmp')
    
[/code]

元のコードを見る限り、保存先のディレクトリが存在しない(or書き込み権限がない)場合にはカレントディレクトリに保存する様なので、その様にしています。また、`name
= url.split("/")[-1]`
の部分は、本来であればファイル名として正当かどうか、保存先ディレクトリに同名のファイルが存在するかどうか(同名のファイルが存在する場合、上書きするのかエラーとして処理するのか)などをチェックする必要があるかと思います(上記のコードではチェック処理を端折っています)。


CC BY-SA 3.0
2017-03-20T14:12:44.470
33430
2017-03-20T14:12:44.470
33426
answer