返回博客
指南
安德烈·奥吉奥兰2022年12月12日阅读时长:12分钟

了解如何在 Python 中使用 cURL

了解如何在 Python 中使用 cURL

什么是 cURL?

为了达到本文的目的——即学习如何在 Python 中使用 cURL,我们首先需要介绍一下 cURL。Client URL(简称 cURL)是一款易于使用的命令行工具,专为开发者设计,用于从服务器获取数据。

如何使用 cURL?

正如我前面提到的,使用 cURL 非常简单,只需一行命令即可提取信息。首先,你需要打开终端,输入 curl 并跟上网站链接,例如:

$ curl 'https://www.webscrapingapi.com/'

恭喜,你已经使用 cURL 发出了第一个请求。这个简单的命令就像传统浏览器一样向服务器请求信息,并返回页面的 HTML 内容。并非所有网站都会返回 HTML,有些接口会以 JSON 对象的形式返回数据。请看这个例子:

$ curl 'https://jsonplaceholder.typicode.com/todos/1'

在终端中输入此命令,你应该会得到以下响应:

{
  "userId": 1,
  "id": 1,
  "title": "delectus aut autem",
  "completed": false
}

当你使用 cURL 命令调用 API 时,大多数 API 会返回 HTML 或 JSON 格式的数据。不过,这并非 cURL 能为我们做的一切。实际上,它是一个非常强大的工具。如果你想进一步了解 cURL,我强烈建议你查阅cURL 文档,以便更好地理解其参数。或者,你可以运行以下命令:

$ curl --help

以下将向您展示一些可以设置给 cURL 的选项:

Usage: curl [options...] <url>
 -d, --data <data>   HTTP POST data
 -f, --fail          Fail silently (no output at all) on HTTP errors
 -h, --help <category>  Get help for commands
 -i, --include       Include protocol response headers in the output
 -o, --output <file>  Write to file instead of stdout
 -O, --remote-name   Write output to a file named as the remote file
 -s, --silent        Silent mode
 -T, --upload-file <file>  Transfer local FILE to destination
 -u, --user <user:password>  Server user and password
 -A, --user-agent <name>  Send User-Agent <name> to server
 -v, --verbose       Make the operation more talkative
 -V, --version       Show version number and quit

This is not the full help, this menu is stripped into categories.
Use "--help category" to get an overview of all categories.
For all options use the manual or "--help all".

正如你所见,这些还不是 cURL 所有可设置的选项,这是一个按类别划分的菜单。你可能已经猜到了,要想获取所有你想运行的选项:

$ curl --help all

如何在 Python 中使用 cURL?

本步骤有两个先决条件。第一个显而易见,你需要在电脑上安装 Python。你可以访问Python 官方网站,并 根据你的操作系统安装相应的版本请确保安装的是新版,因为旧版本很可能不包含pip,而我们后续要使用的多数包都需要pip。安装完成后,请运行以下命令:

 $ pip --version

安装成功后,这应该会显示您已安装的 pip 版本。 

否则,您很可能会收到以下提示:

“pip”不被视为外部或内部命令。批处理文件是一个用于执行操作的程序。

安装 pip 非常重要,因为您需要它来安装软件包,否则无法继续后续操作。

第二个先决条件是,你应该熟悉 Python 的语法,或者至少具备其他任何编程语言的入门级经验。

为什么要在 Python 中使用 cURL?

你可能在想:难道直接在命令行中使用 cURL 还不够吗?我们只需执行一行命令,API 就会返回所需的信息。这话没错,但在实际应用中,我们通常需要对从服务器接收到的数据进行某种处理,而这正是我们需要编程语言的原因。这时,Python 就派上用场了。

为什么要使用 Python?

Python 是一种用途广泛的高级编程语言。其简洁的语法和易用性使得初学者能够轻松上手。此外,它拥有庞大的用户社区随时准备为您提供帮助,因此如果您遇到任何问题,请不要犹豫,大胆提问。StackOverflow是一个绝佳的平台,您可以在那里提出问题,肯定会有人为您解答。

如何在 Python 中集成 cURL

和之前一样,Python 中的 cURL 同样非常简单直观。还记得我们之前只用一行命令就能从服务器获取数据吗?不同之处在于,现在你需要编写两行代码才能完成这个简单的调用,例如:

import os
os.system(f'curl "https://www.webscrapingapi.com/product/"')

现在让我们来看看使用编程语言的真正优势。我们可以编写一个函数,该函数能够接收自定义 URL,并针对该 URL 执行 curl 命令:

import os

def cURL(url):
   return os.system(f'curl "{url}"')

cURL('https://www.webscrapingapi.com/blog/')

你可以将https://www.webscrapingapi.com/blog/替换为任何其他你想获取数据的网站。恭喜,至此你已经编写了一个脚本,它能够接收一个 URL,执行 cURL 命令,并在控制台中显示结果。虽然你可以在终端中直接运行 Python,但为了获得更好的编程体验,我强烈建议你使用集成开发环境(IDE)。 可选的开发环境有很多,但针对 Python,我推荐 PyCharm,您可以从这里下载

正如我前面提到的,cURL 的功能远不止于此。除了发送 GET 请求外,它还能完成大量其他任务。它还可以下载文件,或发送 POST、PUT 或 DELETE 请求。以下是一个 Python 函数的示例,该函数向https://httpbin.org/post 发送 POST 请求:

import os

def cURL(method,url,data):
   return os.system(f'curl -X "{method}" --url "{url}" --data {data} ')

data = '{"foo":"bar"}'

cURL('POST', 'https://httpbin.org/post', data)

你可能已经注意到,发送 POST 请求时命令会略有不同。在之前的请求中,你无需使用 -X 和 –data 参数,因为 cURL 的默认方法是 GET。执行此命令后,你应该会从 httpbin API 收到一条响应,其中包含你的请求、IP 地址、参数以及你提交的请求主体,内容大致如下:

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "{foo:bar}": ""
  }, 
  "headers": {
    "Accept": "*/*", 
    "Content-Length": "9", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/X.XX.X", 
    "X-Amzn-Trace-Id": "Root=X-XXXXX-XXXXXX"
  }, 
  "json": null, 
  "origin": "0.0.0.0", 
  "url": "https://httpbin.org/post"
}

至此,您已经构建了一个简单的工具,可以发送 GET、POST、PUT、PATCH、DELETE 等请求,从 API 中获取数据。现在,我们只需在 Python 脚本中直接设置 URL、方法和请求体,而无需手动输入。 不过,这只是在 Python 中使用 cURL 带来的次要好处。主要优势在于,即使 API 没有提供我们期望的数据获取方式,我们现在也能按自己的需求处理数据。例如,假设我们获得了一个包含所有用户的列表,但我们希望将其分为两组,并获取第二组中的用户。

得益于 Python,我们可以将响应存储在一个名为 users 的变量中,并使用 json.loads() 方法将其转换为 JSON 对象数组(有关该方法的更多信息,请参见此处)。之后,我们可以遍历 users 数组,并仅显示列表后半部分的用户,或者 ID 编号大于列表中点位置的用户。为了便于理解,以下是对应的代码实现:

import subprocess
import json

def cURL(url):
   return subprocess.check_output(['curl', url])

users = json.loads(cURL('https://jsonplaceholder.typicode.com/users'))

for user in users:
   if user['id'] > len(users) / 2: print(user)

你可能已经注意到,我们现在不再使用 os 模块,而是改用了 subprocess 和 json 库。我们之所以使用 subprocess 库,是因为我们希望能够保存命令的输出结果,而 os 模块只是执行命令而不进行存储。这段代码是一个简单的 API GET 请求,它会以 JSON 对象的形式返回一个用户列表。输出结果应为:

{'id': 6, 'name': 'Mrs. Dennis Schulist', 'username': 'Leopoldo_Corkery', 'email': 'Karley_Dach@jasper.info', 'address': {'street': 'Norberto Crossing', 'suite': 'Apt. 950', 'city': 'South Christy', 'zipcode': '23505-1337', 'geo': {'lat': '-71.4197', 'lng': '71.7478'}}, 'phone': '1-477-935-8478 x6430', 'website': 'ola.org', 'company': {'name': 'Considine-Lockman', 'catchPhrase': 'Synchronised bottom-line interface', 'bs': 'e-enable innovative applications'}}

{'id': 7, 'name': 'Kurtis Weissnat', 'username': 'Elwyn.Skiles', 'email': 'Telly.Hoeger@billy.biz', 'address': {'street': 'Rex Trail', 'suite': 'Suite 280', 'city': 'Howemouth', 'zipcode': '58804-1099', 'geo': {'lat': '24.8918', 'lng': '21.8984'}}, 'phone': '210.067.6132', 'website': 'elvis.io', 'company': {'name': 'Johns Group', 'catchPhrase': 'Configurable multimedia task-force', 'bs': 'generate enterprise e-tailers'}}

{'id': 8, 'name': 'Nicholas Runolfsdottir V', 'username': 'Maxime_Nienow', 'email': 'Sherwood@rosamond.me', 'address': {'street': 'Ellsworth Summit', 'suite': 'Suite 729', 'city': 'Aliyaview', 'zipcode': '45169', 'geo': {'lat': '-14.3990', 'lng': '-120.7677'}}, 'phone': '586.493.6943 x140', 'website': 'jacynthe.com', 'company': {'name': 'Abernathy Group', 'catchPhrase': 'Implemented secondary concept', 'bs': 'e-enable extensible e-tailers'}}

{'id': 9, 'name': 'Glenna Reichert', 'username': 'Delphine', 'email': 'Chaim_McDermott@dana.io', 'address': {'street': 'Dayna Park', 'suite': 'Suite 449', 'city': 'Bartholomebury', 'zipcode': '76495-3109', 'geo': {'lat': '24.6463', 'lng': '-168.8889'}}, 'phone': '(775)976-6794 x41206', 'website': 'conrad.com', 'company': {'name': 'Yost and Sons', 'catchPhrase': 'Switchable contextually-based project', 'bs': 'aggregate real-time technologies'}}

{'id': 10, 'name': 'Clementina DuBuque', 'username': 'Moriah.Stanton', 'email': 'Rey.Padberg@karina.biz', 'address': {'street': 'Kattie Turnpike', 'suite': 'Suite 198', 'city': 'Lebsackbury', 'zipcode': '31428-2261', 'geo': {'lat': '-38.2386', 'lng': '57.2232'}}, 'phone': '024-648-3804', 'website': 'ambrose.net', 'company': {'name': 'Hoeger LLC', 'catchPhrase': 'Centralized empowering task-force', 'bs': 'target end-to-end models'}}

这只是 cURL 在 Python 中的众多用途之一。我们不再依赖 API 来按我们期望的方式返回数据,而是可以采用多种不同的方式进行处理,例如根据姓名、电子邮件、电话号码或任何其他个人唯一属性,来判断某人是否在响应列表中。 

例如,假设我们想检查列表中是否存在某个特定名称。为此,我们可以创建一个函数,该函数能够接收我们已转换为 JSON 数组的响应,以便 Python 进行解析。

import subprocess
import json

def cURL(url):
   return subprocess.check_output(['curl',url])

users = json.loads(cURL('https://jsonplaceholder.typicode.com/users'))

def check_if_user_exists(users,name):
   for user in users:
       if(user['name'] == name): print(f'An user called {name} exists in the list and has the id of {user["id"]}')

check_if_user_exists(users,'Clementina DuBuque')

这段代码将返回以下输出:

列表中存在一位名为 Clementina DuBuque 的用户,其 ID 为 10

恭喜你。你编写了一个脚本,能够从服务器获取数据、将其存储并随后进行解析。Python 的优势不仅限于此,它甚至还提供了一个专为使用 cURL 而设计的接口名为PycURL, 我们接下来将对此进行探讨。

什么是 PycURL?

正如我们之前提到的,PycURL 简而言之,是一个帮助我们更自然地使用 cURL 的 Python 工具。 PycURL 的一个显著优势在于它经过深度优化且支持并发,这意味着它的运行速度非常快(比流行的 Python request 库还要快)。另一方面,PycURL 的使用并不像我们之前见过的那些工具那样简单,它主要面向高级开发者。不过,您不必因此而望而却步,因为最终您将对网络通信有更深入的理解,并且对 Python 的掌握也会更加得心应手。

如何安装?

就像安装其他包一样,你可以使用 pip 安装它: 

$ pip install pycurl 

出于安全考虑,这次在使用 pycurl 时,你可能还想安装 certifi。certifi 是一款在验证 TLS 主机身份的同时验证 SSL 证书的工具。若想进一步了解 certifi,我强烈建议你查阅其文档。安装方法与之前相同: 

$ pip install certifi

您可以通过运行以下脚本来验证安装是否成功: 

import certifi
print(certifi.where())

输出应为该软件包的安装路径:

/usr/local/lib/python3.10/site-packages/certifi/cacert.pem

如何使用 pycURL?

与之前的做法不同的是,现在我们只编写代码,而不直接执行命令。简而言之,我们创建一个 pycurl.Curl() 类的实例,获取数据并将其写入缓冲区,稍后我们将对该缓冲区进行解码,以便能够读取接收到的数据:

import pycurl
import certifi
from io import BytesIO

buffer = BytesIO()
c = pycurl.Curl()
c.setopt(c.URL, 'https://docs.webscrapingapi.com/')
c.setopt(c.WRITEDATA, buffer)
c.setopt(c.CAINFO, certifi.where())
c.perform()
c.close()

body = buffer.getvalue()
print(body.decode('iso-8859-1'))

你猜对了,就像我们之前的例子一样,这段代码会获取WebScrapingAPI网页的 HTML 内容,并将其输出到命令行中。

POST 请求与之大同小异,只是你需要告知 pycurl.Curl() 类的实例,你将使用 POST 方法,并根据需要设置请求主体和请求头。具体实现如下:

​​import pycurl
import certifi
import json
from io import BytesIO

buffer = BytesIO()
c = pycurl.Curl()
c.setopt(c.URL, 'https://httpbin.org/post')
c.setopt(pycurl.HTTPHEADER, [ 'Content-Type: application/json' , 'Accept: application/json'])
data = json.dumps({"foo": "bar"})

c.setopt(pycurl.POST, 1)
c.setopt(pycurl.POSTFIELDS, data)
c.setopt(c.WRITEDATA, buffer)
c.setopt(c.CAINFO, certifi.where())

c.perform()
c.close()

body = buffer.getvalue()

print(body.decode('iso-8859-1'))

然后,我们应该会收到一个响应,其中包含您的请求、IP 地址、参数和请求主体,就像之前一样:

{
  "args": {}, 
  "data": "", 
  "files": {}, 
  "form": {
    "{foo:bar}": ""
  }, 
  "headers": {
    "Accept": "*/*", 
    "Content-Length": "9", 
    "Content-Type": "application/x-www-form-urlencoded", 
    "Host": "httpbin.org", 
    "User-Agent": "curl/X.XX.X", 
    "X-Amzn-Trace-Id": "Root=X-XXXXX-XXXXXX"
  }, 
  "json": null, 
  "origin": "0.0.0.0", 
  "url": "https://httpbin.org/post"
}

正如我之前提到的,pycURL 目前只能为我们提供一些基础功能。它是一个非常复杂且功能强大的工具,关于它我们可以写很多文章。如果你想进一步探索它的更多功能,我强烈建议你查阅其文档

摘要

总而言之,在 Python 中使用 cURL 非常高效,能节省大量时间,而且还能作为数据分析或网页抓取等有趣项目的起点。我建议的做法是:先熟悉 cURL 和 Python,然后再进阶使用 pycURL。希望这篇指南能帮助你掌握在 Python 中使用 cURL 的方法,并鼓励你去尝试编写一些脚本。

关于作者
安德烈·奥吉奥兰,全栈开发工程师 @ WebScrapingAPI
安德烈-奥吉奥兰全栈开发工程师

安德烈·奥吉奥兰(Andrei Ogiolan)是 WebScrapingAPI 的全栈开发工程师,他在产品各领域均有贡献,并协助为该平台构建可靠的工具和功能。

开始构建

准备好扩展您的数据收集规模了吗?

加入2,000多家企业,使用WebScrapingAPI在无需任何基础设施开销的情况下,以企业级规模提取网页数据。