如何使用 Python 中的 Wget 下载网页和文件

本综合指南介绍了 wget,这是一款通过 HTTP、HTTPS 和 FTP 下载文件的强大命令行工具,并将其与 Python 中的 requests 库进行了有利的比较。
3 min read
如何在Python中使用Wget

在本指南中,您将看到:

  • 什么是wget
  • 为什么它比 requests 库更好。
  • 如何轻松地将wget与 Python 一起使用。
  • 在 Python 脚本中使用它的优缺点。

让我们开始吧!

什么是 Wget?

wget 是一个命令行工具,用于通过 HTTP、HTTPS、FTP、FTPS 和其他互联网协议从网络下载文件。它在大多数类 Unix 操作系统中已原生安装,但也可用于 Windows。

为什么选择 Wget 而不是 Python 库如 requests?

当然,wget 是一个很酷的命令行工具,但为什么要在 Python 中使用它来下载文件,而不是像 requests 这样流行的库呢?

嗯,使用 wget 而不是 requests 的理由有很多:

  • 支持的协议比 requests 多得多。
  • 可以恢复中断或中止的下载。
  • 支持限制下载速度,以免占用所有网络带宽。
  • 支持带有通配符的文件名和网络位置。
  • 多语言支持的基于 NLS 的消息文件。
  • 可以将下载文档中的绝对链接转换为相对链接。
  • 支持HTTP/S 代理
  • 支持持久的 HTTP 连接。
  • 可以执行无人值守/后台下载操作。
  • 使用本地文件时间戳来确定在镜像时是否需要重新下载文档。
  • 可以递归下载特定网页上链接的文件,或直到达到用户指定的递归深度。
  • 自动遵守 robots.txt 中定义的机器人排除规则。请在我们的 web scraping 中的 robots.txt 指南中了解更多信息。

这些只是 wget 的一些功能,使它在任何 Python HTTP 客户端库中如此强大和特殊。在 官方手册中发现更多信息。

特别是,注意 wget 如何可以跟随 HTML 页面中的链接并下载这些页面中引用的文件。这有助于您甚至可以检索整个网站,使 wget 成为 web 爬虫的理想选择。

简而言之,在编写需要从网络下载文件和网页的脚本时,wget 是一个很好的选择。让我们学习如何在 Python 中使用 wget 吧!

在 Python 中运行 CLI 命令

按照以下步骤,构建一个可以运行 wget 命令的 Python 脚本。

前提条件

在开始之前,请确保您已经在计算机上安装了 wget。安装过程根据操作系统而有所不同:

  • 在 Linux 上,您应该已经预装了它。如果没有,请使用发行版的包管理器安装。
  • 在 Mac 上,使用 Homebrew 安装 wget
  • 在 Windows 上,下载Wget 二进制文件,并将其放在一个文件夹中。然后,将 wget 二进制文件路径(例如 C:\Program Files (x86)\Wget)添加到PATH 环境变量中。

您还需要在计算机上安装 Python 3+。请下载安装程序,双击并按照说明进行操作。

一个 Python IDE,如PyCharm Community Edition带有 Python 扩展的 Visual Studio Code也会很有用。

设置一个 Python 项目

使用以下命令创建一个 wget Python 项目并创建一个虚拟环境

mkdir wget-python-demo

cd wget-python-demo

python -m venv env

上述创建的 wget-python-demo 目录表示您的项目文件夹。

将其加载到您的 Python IDE 中,创建一个 script.py 文件,并初始化如下:

print('Hello, World!')

现在,这只是一个示例脚本,它在终端中打印“Hello, World!”。很快,它将包含 wget 集成逻辑。

通过按下 IDE 的运行按钮或使用以下命令验证脚本是否有效:

python script.py

在终端中,您应该看到:

Hello, World!

完美!您现在已经有了一个 Python 项目。

请参阅下一部分,了解如何使用 wget

编写一个函数,通过 Subprocess 模块执行 CLI 命令

在 Python 脚本中运行 CLI 命令的最简单方法是使用subprocess 模块

这个库是 Python 标准库的一部分,允许您生成新进程,连接到它们的输入/输出/错误管道,并获得它们的返回代码。换句话说,它为您提供了在 Python 中执行终端命令所需的一切。

以下是如何使用 subprocess 的Popen() 方法在 Python 中执行 wget 等 CLI 命令的方法:

import subprocess

def execute_command(command):

"""

Execute a CLI command and return the output and error messages.

Parameters:

- command (str): The CLI command to execute.

Returns:

- output (str): The output generated by the command.

- error (str): The error message generated by the command, if any.

"""

try:

# execute the command and capture the output and error messages

process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

output, error = process.communicate()

output = output.decode("utf-8")

error = error.decode("utf-8")

# return the output and error messages

return output, error

except Exception as e:

# if an exception occurs, return the exception message as an error

return None, str(e)

Popen() 以字符串形式执行您传递的命令,并在操作系统中新建一个进程。shell=True 选项确保该方法将使用您操作系统中配置的默认 shell。

将上述代码段粘 贴到您的 script.py 文件中。您现在可以在 Python 中通过以下示例调用 CLI 命令:

output, error = execute_command("<CLI command string>")

if error:

print("An error occurred while running the CLI command:", error)

else:

print("CLI command output:", output)

使用 Python 和 Wget:用例

这是 wget 命令的语法:

wget [options] [url]

其中:

  • [options] 是 CLI 工具支持的选项和标志列表,用于自定义其行为。
  • url 是您要下载的文件的 URL。它可以是文件的直接链接,也可以是包含多个文件链接的网页的 URL。

注意:在 Windows 上,写 wget.exe 而不是 wget

现在来看一些在 Python 代码片段中使用 wget 的流行用例吧!

下载文件

假设您想用 wget 下载 http://lumtest.com/myip.json。实现此目的的命令如下:

wget http://lumtest.com/myip.json

在 Python 中,这将变成以下代码行:

output, error = execute_command("wget http://lumtest.com/myip.json")

如果打印输出,您将看到类似:

--2024-04-18 15:20:59-- http://lumtest.com/myip.json

Resolving lumtest.com (lumtest.com)... 3.94.72.89, 3.94.40.55

Connecting to lumtest.com (lumtest.com)|3.94.72.89|:80... connected.

HTTP request sent, awaiting response... 200 OK

Length: 266 [application/json]

Saving to: 'myip.json.1'

myip.json.1 100%[=================================================>] 266 --.-KB/s in 0s

2024-04-18 15:20:59 (5.41 MB/s) - 'myip.json.1' saved [266/266]

从命令的输出中,您可以看到:

  1. URL 被解析为服务器的 IP 地址。
  2. wget 通过 HTTP 请求连接到服务器并请求指定资源。
  3. 服务器返回的HTTP 响应状态码为 200。
  4. wget 下载文件并将其存储在当前目录中。

您的 Python 项目目录现在将包含一个 myip.json 文件。

如果您想更改存储文件的目标文件夹,请使用 –directory-prefix 或 -P 标志,如下所示:

output, error = execute_command("wget --directory-prefix=./download http://lumtest.com/myip.json")

myip.json 文件现在将存储在项目目录内的 download 文件夹中。请注意,如果目标文件夹不存在,wget 将自动创建它。

要更改下载资源的文件名,请使用 –output-document 或 -O 标志:

output, error = execute_command("wget --output-document=custom-name.json http://lumtest.com/myip.json")

这次,wget Python 脚本将创建一个名为 custom-name.json 的文件,而不是 myip.json。

下载网页

wget 命令与之前相同,主要区别在于这次 url 将指向一个网页:

output, error = execute_command("wget https://brightdata.com/")

您的项目目录现在将包含一个 index.html 文件,其中包含 https://brightdata.com/ 网页的 HTML 内容。

仅在文件自上次下载以来有更改时才下载

为了节省磁盘空间和网络资源,您可能不希望在文件自上次下载以来没有更改时再次下载该文件。这就是 wget 提供文件时间戳功能的原因。

详细来说,–timestamping 选项指示 wget 比较本地文件与服务器上的文件的时间戳。如果本地文件的时间戳与服务器上的文件相同或更新,wget 将不会再次下载该文件。否则,它将下载文件。

以下是时间戳机制的工作原理:

  1. 当您使用 –timestamping 或 -N 选项下载文件时,wget 会检索远程文件的时间戳。
  2. 它检查本地文件的时间戳(如果存在)并将其与远程文件的时间戳进行比较。
  3. 如果本地文件不存在或其时间戳比服务器上的文件旧,wget 将下载该文件。如果本地文件存在且其时间戳与服务器上的文件相同或更新,wget 将不会下载该文件。

HTTP 中的时间戳通过检查服务器在 HEAD 请求后返回的Last-Modified 标头来实现。wget 还会查看Content-Length 标头以比较文件大小。如果它们不相同,无论 Last-Modified 标头说什么,远程文件都将被下载。请记住,Last-Modified 是一个可选的响应标头。如果没有出现,wget 将无论如何都会下载文件。

使用 –timestamping 选项在 Python 中通过以下代码行:

output, error = execute_command("wget --timestamping https://brightdata.com")

如果您已经下载了 index.html,您将收到以下消息,表示文件将不会再次下载:

--2024-04-18 15:55:06-- https://brightdata.com

Resolving brightdata.com (brightdata.com)... 104.18.25.60, 104.18.24.60

Connecting to brightdata.com (brightdata.com)|104.18.25.60|:443... connected.

HTTP request sent, awaiting response... 304 Not Modified

File 'index.html' not modified on server. Omitting download.

相同的机制在通过FTP 下载文件时也有效。

完成中断的下载

默认情况下,wget 在连接丢失期间自动重试下载文件最多 20 次。如果您想手动继续部分下载的文件,请使用 –continue 或 -c 选项,如下所示:

output, error = execute_command("wget --continue http://lumtest.com/myip.json")

下载整个网站

递归下载是一种 wget 功能,可以通过单个命令下载整个网站。

从指定的 URL 开始,wget 解析 HTML 页面并跟随 src 和 href HTML 属性或 url() CSS 属性中的其他文档。如果下一个文件也是一个 text/HTML 文件,它将解析并跟随其文档,直到达到所需的深度。递归下载遵循广度优先搜索算法,检索深度 1 的文件,然后是深度 2,依此类推。

使用这种下载模式时需要记住的 wget 选项如下:

  • –recursive 或 -r:告诉 wget 递归下载文件,意味着它将跟随网页上的链接。它使您能够创建整个网站的本地副本,包括所有链接资源(如图像、样式表、脚本等)。指定此选项时,wget 将所有下载的文件存储在与目标站点域名相同的文件夹中。
  • –level=<depth> 或 -l=<depth>:指定下载链接页面时要遵循的最大递归深度。例如,如果设置 –level=1,wget 只会下载直接从起始 URL 链接的页面。它不会跟随这些页面上的链接以下载进一步的页面。为了防止爬取庞大的网站,默认深度值为 5。将此选项设置为 0 或 ‘inf’ 以达到无限深度。如果希望确保无论指定的深度如何,所有正确显示页面所需的资源都被下载,请添加 -p 或 –page-requisites 选项。
  • –convert-links 或 -k:修改下载的 HTML 文件中的链接,使其指向本地下载的文件,而不是原始 URL。此选项在您希望创建站点的本地镜像并确保所有下载页面内的链接在脱机时正常工作时非常有用。

假设您希望以 1 级递归深度递归下载 Bright Data 网站,同时将所有链接转换为指向本地文件。这是您应该编写的 Python wget 指令:

output, error = execute_command("wget --recursive --level=1 --convert-links https://brightdata.com")

注意:此命令可能需要一些时间,具体取决于您的互联网连接速度,请耐心等待。

brightdata.com 文件夹现在将包含 Bright Data 网站文件的本地副本,递归深度为 1。

在 Python 中使用 Wget 的优缺点

让我们看看在 Python 中使用 wget 的优缺点。

 优点

  • 由于 subprocess 模块,易于 Python 集成。
  • 功能和选项丰富,包括递归下载、自动重试、文件时间戳等。
  • 可以通过一个命令创建整个站点的本地副本。
  • 支持 FTP。
  • 支持代理集成。
  • 可以恢复中断的下载。

 缺点

  • 输出是下载的文件,而不是可以直接在 Python 脚本中使用的字符串变量。
  • 需要像Beautiful Soup这样的解析器来访问下载的 HTML 文件中的特定 DOM 元素。

[额外] 使用代理与 Wget

使用 wget 下载文件或整个站点的主要挑战在于您的请求可能会被阻止。这是因为 wget 的请求在目标服务器上会显示为来自机器人的请求。为了防范这些请求,一些网站为其页面和资源实施限制和限制。这些限制可能包括地理限制、速率限制策略或反爬虫措施。

代理服务器集成到 wget 中是绕过这些限制的可行解决方案。代理充当计算机与互联网之间的中间服务器。通过代理服务器转发 wget 流量,您可以避免暴露您的 IP 并绕过大多数网站对其内容实施的限制。

有关更完整的教程,请参阅我们的如何使用代理与 Wget指南。

总结

在本文中,您了解了 wget 是什么,为什么它比 requests 库更好,以及如何在 Python 中使用它。现在,您知道 wget 是一个用于通过 HTTP、HTTPS 和 FTP 下载文件和网页的强大工具。感谢您在这里学到的知识,您知道如何在 Python 中使用 wget

正如在本指南中所见,代理可以成为避免所有站点采取的反机器人措施的好帮手。问题是网上有几十个提供商,选择最好的并不容易。节省时间,直接选择市场上最好的,Bright Data

Bright Data 拥有世界上最好的代理服务器,为财富 500 强公司和超过 20,000 名客户提供服务。它的产品包括多种类型的代理:

开始免费试用或与我们的一位数据专家讨论我们的代理和抓取解决方案。