980.png

在本教程中,我们将学习到使用不同的Python模块从网页上下载文件,包括常规文件,网页和其它来源。
 
我们会在其中遇到各种可能的挑战,比如下载重定向,大文件下载,多线程下载等。
 
请来看本文。
 
使用 Requests
 
从某个网址下载文件可以使用 requests 模块。
 
请看如下代码:
 
import requests
url = 'https://www.python.org/static/img/python-logo@2x.png'
myfile = requests.get(url)
open('c:/users/21cto/downloads/PythonImage.png', 'wb').write(myfile.content)


 
只需要使用request模块的get方法获取URL,接着将结果存储到myfile变量中即可,最后将变量的内容写入到文件。
 
使用 wget
 
还可以使用 wget 模块来从URL上下载文件,它也是Python的标准模块,如果没有安装,使用pip命令开始安装:
 
pip install wget


 
如下代码,我们将下载Python的logo和相关图片:
 
 
import wget
url = "python-logo@2x.png"" rel="nofollow" target="_blank">https://www.python.org/static/ ... ot%3B
wget.download(url, 'c:/users/LikeGeeks/downloads/pythonLogo.png')


 
在以上代码中,URL和图片的全路径会传递到wget模块的download方法,最后文件如果存在就会下载到目的路径和文件存储。
 
下载重定向文件
 
在本节中,我们将学习到从URL下载,该URL将请求会重定向到另一个URL中,比如如下URL:
 
https://readthedocs.org/projec ... test/
 
想要下载该pdf文件,我们使用如下代码:
 
import requests
url = 'https://readthedocs.org/projects/python-guide/downloads/pdf/latest/'
myfile = requests.get(url, allow_redirects=True)
open('c:/users/21cto/documents/PythonBook.pdf', 'wb').write(myfile.content)

 

在以上代码中,我们第一步是指定URL,然后使用requests模块的get方法来获取URL内容。在get方法中,需要设置allow_redirects为True,也就是允许URL重定向,然后将重定向的内容分配给myfile变量。
 
最后,打开一个文件用来写入获取到的内容,完成下载。
 
 
下载大尺寸文件
 
请看如下代码:
 
import requests
url = 'https://www.python.org/static/img/python-logo@2x.png'
myfile = requests.get(url)
open('c:/users/21cto/downloads/PythonImage.png', 'wb').write(myfile.content)

 
第一步,我们和前面一样使用requests模块的get方法,但是在此处,需要把stream属性设置为True。接下来,在当前工作目录中创建一个名叫PythonBook.pdf的文件,打开并开始写入。
 
接下来指定一次要下载的块尺寸,我们将其设置为1024字节,然后遍历每个块,接着把这些块写入文件中,直接块全部写完为止。
 
貌似不是很好看?不有担心,一会我们使用显示进度条的下载方式。
 
下载多个文件(并行/批量下载)
 
如果要一次下载多个文件,需要导入如下模块:
 
import os
import requests
from time import time
from multiprocessing.pool import ThreadPool

 
从上面代码中,我们可以导入了os和time模块,用来检查下载文件花了多少时间。模块ThreadPool线程池用来处理多个线程和进程。
 
我们来创建一个函数,将返回内容分块写入到文件中:
 
def url_response(url):
    path, url = url
    r = requests.get(url, stream = True)
    with open(path, 'wb') as f:
        for ch in r:
            f.write(ch)

 
 
URL将是一个二维数组,用来指定下载的网页路径或文件。
 
urls = [("Event1", "https://www.python.org/events/ ... 6quot;),
("Event2", "https://www.python.org/events/ ... 6quot;),
("Event3", "https://www.python.org/events/ ... 6quot;),
("Event4", "https://www.python.org/events/ ... 6quot;),
("Event5", "https://www.python.org/events/ ... 6quot;),
("Event6", "https://www.python.org/events/ ... 6quot;),
("Event7", "https://www.python.org/events/ ... 6quot;),
("Event8", "https://www.python.org/events/ ... 6quot;)]

 
结合前一节所描述的那样,我们将URL传递给request.get,接着打开文件(URL中指定之路径)并写入页面内容。
 
现在,我们就可以为每个网址下载调用此函数,也可以同时为所有网址调用此函数。接下来分别在for循环中为每个URL进行操作,请注意计时器:
 
start = time()
for x in urls:
    url_response (x)
print(f"Time to download: {time() - start}")

 
接下来,使用如下代码替换for循环:
 
ThreadPool(9).imap_unordered(url_response, urls)

 
运行该脚本即可。
 
下载显示进度条
 
Python中的进度条是clint模块的UI组件,需要安装Clint模块,命令如下:
 
pip install clint

 
接下来使用如下代码:
 
import requests
from clint.textui import progress
url = 'http://do1.dr-chuck.com/pythonlearn/EN_us/pythonlearn.pdf'
r = requests.get(url, stream=True)
with open("LearnPython.pdf", "wb") as Pypdf:
    total_length = int(r.headers.get('content-length'))
    for ch in progress.bar(r.iter_content(chunk_size = 2391975), expected_size=(total_length/1024) + 1):
        if ch:
            Pypdf.write(ch)

 
 
在这段代码中,我们引入了requests组件,接下来在clint.textui中导入进度条组件,与前面的唯一区别在于for循环,在把内容写入文件时,我们使用了progress模块的bar方法。
 
使用urllib下载
 
我们也可以使用urllib来下载网页。urllib库是Python的标准库,不需要额外再安装。
 
可以用如下代码来下载URL资源:
 
urllib.request.urlretrieve('url', 'path')

 
接下来要指定要保存的路径,如下:
 
urllib.request.urlretrieve('https://www.python.org/', 'c:/users/21cto/documents/PythonOrganization.html')

 
在此段代码中,使用urlretrieve方法,传给了源下载文件的网址以及文件存储的路径,扩展名为.html。
 
通过代理服务器下载
 
因为相关原因,我们需要使用代理服务器下载时,则可以使用urllib模块的ProxyHandler。
 
来看如下代码:
 
import urllib.request
>>> myProxy = urllib.request.ProxyHandler({'http': '127.0.0.2'})
>>> openProxy = urllib.request.build_opener(myProxy)
>>> urllib.request.urlretrieve('https://www.python.org/')

 
 
在此段代码中,首先创建了代理对象,通过调用urllib的build_opener方法打开代理服务器,传递代理对象。
 
接着,开始发起检索页面之请求。
 
另外,还可以使用Python官方文档中的requests模块来运行。代码如下:
 
import requests
myProxy = { 'http': 'http://127.0.0.2:3001' }
requests.get("https://www.python.org/", proxies=myProxy)

 

只需导入requests模块,创建自己的代理对象,就可以下载文件了。
 
使用urllib3
 
urllib3是urllib模块的改进版本。需要使用pip下载并安装它:
 
pip install urllib3

 
接下来下载一个网页,使用urllib3来把它存储在文本文件中。
 
import urllib3, shutil

 
shutil是用来处理文件时使用。接下来,初始化URL字符串变量:
 
url = 'https://www.python.org/'

 
接下来,使用PoolManager来跟踪相关的连接池。
 
c = urllib3.PoolManager()

 
接下来,创建一个文件。
 
filename = "mytest.txt"

 
最后,使用GET方式来请求并获取URL内容,接下来打开文件,将返回的响应写入该文件中。代码如下:
 
 
with c.request('GET', url, preload_content=False) as res, open(filename, 'wb') as out_file:
    shutil.copyfileobj(res, out_file)

 
 
异步下载
 
asyncio模块专门用来处理系统事件,它围绕着事件循环来工作,循环等待,事件发生,然后对该事件进行反应,该反应可能是调用另一个函数。
 
asyncio模块使用协程进行事件处理。
 
接下来使用asyncio事件处理和协程功能,导入asyncio模块:
 
import asyncio

 
接下来,定义异步协程之方法:
 
async def coroutine():
    await my_func()

 
这里给大家说明一下,关键字asyncz表示是一个本地asyncio协程。在协程主体内部,有await关键字,此关键字返回某值,亦可以使用return关键字。
 
现在,我们使用协程来创建一段完整代码,从Python官方网站下载文件:
 
>>> import os
>>> import urllib.request
>>> async def coroutine(url):
    r = urllib.request.urlopen(url)
    filename = "couroutine_downloads.txt"
    with open(filename, 'wb') as f:
        for ch in r:
            f.write(ch)
    print_msg = '下载已经成功'
    return print_msg
>>> async def main_func(urls_to_download):
    co = [coroutine(url) for url in urls_to_download]
    downloaded, downloading = await asyncio.wait(co)
    for i in downloaded:
        print(i.result())
urls_to_download = ["https://www.python.org/events/ ... ot%3B,
"https://www.python.org/events/ ... ot%3B
"https://www.python.org/events/ ... ot%3B,
 "https://www.python.org/events/ ... ot%3B]
>>> eventLoop = asyncio.get_event_loop()
>>> eventLoop.run_until_complete(main_func(urls_to_download))

 
 
在上面的Python代码中,我们创建了一个异步协程函数,该函数下载文件后会返回一条消息,接下来有另一个异步协程调用,main_fun形成队列,asyncio等待功能完成后,协程结束。
 
如果要启动协程,需要使用asyncio的get_event_loop()方法把协程放入事件循环中,最后用run_until_complete()执行事件循环。
 
使用Pyton下载文件非常有趣,多磨练,会有更多有益的东西。希望本教程对大家有帮助!
 
 

作者:新楚
来源:21CTO学院