在Python脚本中执行curl命令

131

我希望通过Python脚本执行一个curl命令。

如果在终端中执行,命令如下:

curl -X POST -d  '{"nw_src": "10.0.0.1/32", "nw_dst": "10.0.0.2/32", "nw_proto": "ICMP", "actions": "ALLOW", "priority": "10"}' http://localhost:8080/firewall/rules/0000000000000001

我看到了使用pycurl的建议,但是我不知道如何将其应用到我的代码中。

我尝试了以下方法:

subprocess.call([
    'curl',
    '-X',
    'POST',
    '-d',
    flow_x,
    'http://localhost:8080/firewall/rules/0000000000000001'
])

它可以工作,但有更好的方法吗?


1
你不必使用 cURL 来向服务器发送 POST 请求。requests 可以轻松实现此功能(urllib 也可以,但需要更多的努力)。 - roippi
查看此链接以了解如何在Python中执行shell命令:https://dev59.com/SnVD5IYBdhLWcg3wGXlI - Sainath Motlakunta
9个回答

265

不要这样做!

我知道,这不是任何人想听到的答案。但是如果某件事值得去做,那就应该做对,对吧?

这种看似好主意可能源于一个非常普遍的误解,即像curl这样的shell命令不是程序本身。

所以你要问的是“我如何从我的程序中运行另一个程序,只是为了进行一次微不足道的网络请求?” 这很疯狂,肯定有更好的方法,对吧?

Uxio's answer 确实可行。 但是它看起来并不是很符合Pythonic的思想,对吧?这么做太麻烦了,就为了一个小小的请求。Python应该是关于飞翔的!那些写这个的人可能希望只是调用了curl


它可以运作,但是否有更好的方法呢?

是的,更好的方法!

Requests:人类可读的HTTP

事情不应该是这样的。在Python中不应该这样。

让我们GET这个页面:

import requests
res = requests.get('https://dev59.com/Ll8e5IYBdhLWcg3wLYDs')

就是这样了!然后你就有了原始的res.text或者res.json()输出,以及res.headers等。

你可以查看上述链接中的文档以获取设置所有选项的详细信息,因为我想OP现在已经离开了,而现在的你——读者可能需要不同的选项。

但是,例如,它就像这样简单:

url     = 'http://example.tld'
payload = { 'key' : 'val' }
headers = {}
res = requests.post(url, data=payload, headers=headers)

你甚至可以使用一个漂亮的Python字典来提供GET请求中的查询字符串,方法是params={}

简单而优雅。保持冷静,继续前进。


2
我正在使用Python 2.4.3版本。无法使用requests库。导入错误:找不到requests模块。 - Gary
14
请执行以下命令以安装requests模块:pip install requests - OJFord
1
@marciokoko 当然没问题!:) requests 库本质上就是 HTTP。你可以像使用 curl 一样使用它,但用 Python 更快速。 - OJFord
1
据我所知,任何可以使用cURL完成的操作也可以通过Python requests完成。最好使用后者。 - idungotnosn
1
这对我寻找的东西完美地起作用了。谢谢@OJFord - galois
显示剩余11条评论

64
使用这个工具(免费托管在这里)将您的curl命令转换为等效的Python requests代码:
示例:这个,
curl 'https://www.example.com/' -H 'Connection: keep-alive' -H 'Cache-Control: max-age=0' -H 'Origin: https://www.example.com' -H 'Accept-Encoding: gzip, deflate, br' -H 'Cookie: SESSID=ABCDEF' --data-binary 'Pathfinder' --compressed

被整洁地转换为:

import requests

cookies = {
    'SESSID': 'ABCDEF',
}

headers = {
    'Connection': 'keep-alive',
    'Cache-Control': 'max-age=0',
    'Origin': 'https://www.example.com',
    'Accept-Encoding': 'gzip, deflate, br',
}

data = 'Pathfinder'

response = requests.post('https://www.example.com/', headers=headers, cookies=cookies, data=data)

6
@OJFord 向我们阐明了为什么不应该在Python中使用curl,而Nitin则描绘了使用“requests”实现相同功能的最简单方法。我强烈推荐这个答案。 - mdabdullah

46

正如 @roippi 所说,您可以使用 urllib:

import urllib2
data = '{"nw_src": "10.0.0.1/32", "nw_dst": "10.0.0.2/32", "nw_proto": "ICMP", "actions": "ALLOW", "priority": "10"}'
url = 'http://localhost:8080/firewall/rules/0000000000000001'
req = urllib2.Request(url, data, {'Content-Type': 'application/json'})
f = urllib2.urlopen(req)
for x in f:
    print(x)
f.close()

urllib2相对于subprocess来说更加高效吗? - Kiran Vemuri
3
这取决于子进程的情况,但是如果语言有核心库可以实现相应功能时,生成子进程来调用命令并不是绝对正确的做法。 - Uxío
类型错误:POST 数据应该是字节、字节的可迭代对象或文件对象。不能是字符串类型。 - foobar

39

如果您没有太多修改curl命令的话,也可以直接调用curl命令

import shlex
cmd = '''curl -X POST -d  '{"nw_src": "10.0.0.1/32", "nw_dst": "10.0.0.2/32", "nw_proto": "ICMP", "actions": "ALLOW", "priority": "10"}' http://localhost:8080/firewall/rules/0000000000000001'''
args = shlex.split(cmd)
process = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = process.communicate()

我使用subprocess.call()实现了它。 - Kiran Vemuri
1
我得到了@Ollie Ford的建议,非常有帮助:我已经在W10下安装了Requests。从命令行启动Python,然后组合我的请求URL。从现在开始,我将需要弄清如何在.py文件中设置(和查看)流内容。欢迎提供建议! - user3722096
"subprocess" 未定义。 - Anton Duzenko

14

试用 subprocess

CurlUrl="curl 'https://www.example.com/' -H 'Connection: keep-alive' -H 'Cache- 
          Control: max-age=0' -H 'Origin: https://www.example.com' -H 'Accept-Encoding: 
          gzip, deflate, br' -H 'Cookie: SESSID=ABCDEF' --data-binary 'Pathfinder' -- 
          compressed"

使用getstatusoutput来存储结果

status, output = subprocess.getstatusoutput(CurlUrl)

5

您可以使用以下代码片段

import shlex
import subprocess
import json

def call_curl(curl):
    args = shlex.split(curl)
    process = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    stdout, stderr = process.communicate()
    return json.loads(stdout.decode('utf-8'))


if __name__ == '__main__':
    curl = '''curl - X
    POST - d
    '{"nw_src": "10.0.0.1/32", "nw_dst": "10.0.0.2/32", "nw_proto": "ICMP", "actions": "ALLOW", "priority": "10"}'
    http: // localhost: 8080 / firewall / rules / 0000000000000001 '''
    output = call_curl(curl)
    print(output)

3

1
subprocess 模块内,还有一个名为 run 的选项,请使用它。
from subprocess import run
run(curl -X POST -d  '{"nw_src": "10.0.0.1/32", "nw_dst": "10.0.0.2/32", "nw_proto": "ICMP", "actions": "ALLOW", "priority": "10"}' http://localhost:8080/firewall/rules/0000000000000001)


0
使用Python 3,内置的HTTP协议客户端是cURL的一个可行替代品。使用提供的示例:
>>> import http.client, urllib.parse
>>> params = urllib.parse.urlencode({"nw_src": "10.0.0.1/32", "nw_dst": "10.0.0.2/32", "nw_proto": "ICMP", "actions": "ALLOW", "priority": "10"})
>>> headers = {"Content-type": "application/x-www-form-urlencoded", "Accept": "text/plain"}
>>> conn = http.client.HTTPConnection("localhost:8080")
>>> conn.request("POST", "/firewall/rules/0000000000000001", params, headers)
>>> response = conn.getresponse()
>>> print(response.status, response.reason)
302 Found
>>> data = response.read()
>>> conn.close()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接