在Python中下载文件(带请求?)

时间:2015-12-13 15:10:02

标签: python beautifulsoup web-crawler python-requests

我尝试做的是构建一个简单的抓取工具,帮助我从Ultimate-Guitar下载吉他标签。我可以为其提供一个乐队的网址,它会抓取列为“吉他专业版”的所有标签的链接。标签。

典型链接如下所示:

  

https://tabs.ultimate-guitar.com/a/agalloch/you_were_but_a_ghost_in_my_arms_guitar_pro.htm

我能够对此链接执行的操作是使用以下代码找到tab_id:

for tabid in tab.findAll("input", {"type" : "hidden", "name" : "id", "id" : "tab_id"}):
        tabID = tabid.get("value")

我尝试做的是使用它来构建实际下载的链接。我遇到问题的地方就在这里。我可以构建的最佳链接如下所示:

  

https://tabs.ultimate-guitar.com/tabs/download?id=904610

请注意,该网址末尾的ID是我之前提到的tab_id。

如果输入浏览器,此链接将立即导致下载。我遇到的问题是,我无法找到任何方法来生成依赖于实际文件名的链接。此文件名应该类似于[歌曲名称] .gp5。其他可接受的文件类型可以是.gpx,.gp4和.gp3。

我想要的是获取实际的文件名,以便我可以正确保存文件(如果下载被命名为垃圾,例如ID,它就无法帮助我,因为那是一个无用的文件名对我来说,我显然需要适当的扩展)。有没有办法获取上面的链接并正确初始化下载或者我可能在这个上运气不好?我确定有办法做我需要的东西,我只是没有足够的经验来处理这类事情。当涉及到请求等等时,我非常无知,所以也许可以提供这个URL并获得下载作为回报?

注意:如果获取实际文件名和扩展名太难了,我确实有解决方法的想法,但我显然至少需要适当的扩展名。

1 个答案:

答案 0 :(得分:4)

文件名包含在响应的标题中。您可以使用cgi.parse_header()解析这些标题,并使用它来保存文件:

>>> import requests
>>> r = requests.get('https://tabs.ultimate-guitar.com/tabs/download?id=904610')
>>> r.headers['Content-Disposition']
'attachment; filename="Agalloch - You Were But A Ghost In My Arms (Pro).gp5"'
>>> cgi.parse_header(r.headers['Content-Disposition'])[-1]['filename']
'Agalloch - You Were But A Ghost In My Arms (Pro).gp5'

执行下载的完整功能可能如下所示:

import cgi
import requests
import shutil

def download_url(url, directory):
    """Download file from url to directory

    URL is expected to have a Content-Disposition header telling us what
    filename to use.

    Returns filename of downloaded file.

    """
    response = requests.get(url, stream=True)
    if response.status != 200:
        raise ValueError('Failed to download')

    params = cgi.parse_header(
        response.headers.get('Content-Disposition', ''))[-1]
    if 'filename' not in params:
        raise ValueError('Could not find a filename')

    filename = os.path.basename(params['filename'])
    abs_path = os.path.join(directory, filename)
    with open(abs_path, 'wb') as target:
        response.raw.decode_content = True
        shutil.copyfileobj(response.raw, target)

    return filename