经典 Clipdrop 去背:
POST https://clipdrop-api.co/remove-background/v1,x-api-key+ multipartimage_file。成功返回图片字节,响应头含剩余 credits。
先看清成品
- mock 写出 PNG 并回显 remaining_credits。
- 官方无效密钥 HTTP 403。
- 1 项 pytest 通过。
合规提醒:Clipdrop 现属 Jasper;网页工具仍可用,API 额度/新模型以 Jasper 沟通与文档为准。只处理你有权使用的图片;勿去除水印用于侵权或伪造。输入限制常见为 30MB / 25MP(以文档为准)。本文不编造单价。
运行说明:无 Clipdrop/Jasper Image 正式密钥。A/C 用 mock 去背;对官方 clipdrop-api.co 无效密钥实测 HTTP 403。B 为本地体积预检。2026-10-23(Asia/Shanghai)pytest 通过。
做完之后你会得到什么?
| 成果 | 说明 |
|---|---|
remove_bg.py |
去背客户端 |
mock_clipdrop.py |
假服务 |
sample.png |
样例 |
test_109a.py |
测试 |
需要准备哪些环境?
| 项目 | 说明 |
|---|---|
| Python | 3.10+ |
| 环境变量 | CLIPDROP_API_KEY |
关键事实
| 事实 | 来源 |
|---|---|
| 字段名 image_file;最大约 30MB / 25MP | 官方 Remove Background 文档 |
| 响应头 x-remaining-credits / x-credits-consumed | 官方 |
| 属 Jasper 后,额度问题联系 Jasper 团队 | 官方页面提示 |
文件结构
109-clipdrop-A-src/
├── remove_bg.py
├── mock_clipdrop.py
├── sample.png
└── test_109a.py
第 1 步:客户端
#!/usr/bin/env python3
"""Clipdrop Remove Background API client (legacy host clipdrop-api.co).
Clipdrop is part of Jasper; docs still document this endpoint. Prefer Jasper Image
API for new business plans — see companion tutorial notes.
"""
from __future__ import annotations
import argparse, os, sys, urllib.error, urllib.request
def remove_bg(base, key, image_path, out_path):
with open(image_path, "rb") as f:
content = f.read()
filename = image_path.split("/")[-1]
boundary = "----binbhoClipdrop"
body = (
f"--{boundary}\r\nContent-Disposition: form-data; name=\"image_file\"; filename=\"{filename}\"\r\n"
f"Content-Type: application/octet-stream\r\n\r\n"
).encode() + content + f"\r\n--{boundary}--\r\n".encode()
req = urllib.request.Request(
f"{base.rstrip('/')}/remove-background/v1",
data=body,
headers={"x-api-key": key, "Content-Type": f"multipart/form-data; boundary={boundary}", "Accept": "image/png"},
method="POST",
)
try:
with urllib.request.urlopen(req, timeout=120) as resp:
data = resp.read()
remaining = resp.headers.get("x-remaining-credits")
Path_write(out_path, data)
return {"bytes": len(data), "remaining_credits": remaining, "out": out_path}
except urllib.error.HTTPError as e:
raise SystemExit(f"HTTP {e.code}: {e.read().decode(errors='replace')}") from e
def Path_write(path, data):
with open(path, "wb") as f:
f.write(data)
def main(argv=None) -> int:
ap = argparse.ArgumentParser(description=__doc__)
ap.add_argument("image")
ap.add_argument("-o", "--out", default="cutout.png")
ap.add_argument("--base", default=os.environ.get("CLIPDROP_BASE_URL", "https://clipdrop-api.co"))
ap.add_argument("--key", default=os.environ.get("CLIPDROP_API_KEY", ""))
args = ap.parse_args(argv)
if not args.key:
print("设置 CLIPDROP_API_KEY(额度耗尽或需新模型时联系 Jasper)", file=sys.stderr); return 2
info = remove_bg(args.base, args.key, args.image, args.out)
print(info)
return 0
if __name__ == "__main__":
raise SystemExit(main())
第 2 步:Mock
#!/usr/bin/env python3
"""Mock Clipdrop remove-background (multipart image_file → PNG bytes)."""
from __future__ import annotations
from http.server import BaseHTTPRequestHandler, HTTPServer
API_KEY = "clipdrop-test-key"
# minimal 1x1 PNG
PNG = (
b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01"
b"\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\nIDATx\x9cc\x00\x01"
b"\x00\x00\x05\x00\x01\r\n-\xb4\x00\x00\x00\x00IEND\xaeB`\x82"
)
class H(BaseHTTPRequestHandler):
def log_message(self, *a):
pass
def do_POST(self):
if self.path != "/remove-background/v1":
self.send_error(404); return
if self.headers.get("x-api-key") != API_KEY:
body = b'{"error":"No api key provided"}'
self.send_response(401)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers(); self.wfile.write(body); return
n = int(self.headers.get("Content-Length", 0))
_ = self.rfile.read(n)
self.send_response(200)
self.send_header("Content-Type", "image/png")
self.send_header("x-remaining-credits", "99")
self.send_header("x-credits-consumed", "1")
self.send_header("Content-Length", str(len(PNG)))
self.end_headers()
self.wfile.write(PNG)
def main():
import argparse
ap = argparse.ArgumentParser(); ap.add_argument("--port", type=int, default=9109)
args = ap.parse_args()
HTTPServer(("127.0.0.1", args.port), H).serve_forever()
if __name__ == "__main__":
main()
第 3 步:测试
import subprocess, sys, time
from pathlib import Path
HERE = Path(__file__).resolve().parent
def test_remove():
port = 19190
proc = subprocess.Popen([sys.executable, str(HERE/"mock_clipdrop.py"), "--port", str(port)], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
try:
time.sleep(0.3)
out = HERE / "_out.png"
r = subprocess.run([sys.executable, str(HERE/"remove_bg.py"), str(HERE/"sample.png"), "-o", str(out), "--base", f"http://127.0.0.1:{port}", "--key", "clipdrop-test-key"], capture_output=True, text=True, check=True)
assert out.exists() and out.stat().st_size > 0
assert "remaining_credits" in r.stdout
finally:
proc.terminate(); proc.wait(timeout=5)
if (HERE/"_out.png").exists():
(HERE/"_out.png").unlink()
第 4 步:运行
$ pytest clipdrop remove_bg
. [100%]
1 passed in 0.36s
$ invalid key clipdrop-api.co
HTTP 403
{"error":"Invalid API key"}
怎么检查结果?
- 输出文件非空。
- 正式调用前跑 B 篇预检。
调试示例
403 Invalid API key。 确认密钥来自 Clipdrop/Jasper 控制台且未耗尽;新业务也可改走 C 篇 Jasper Image 端点。
常见问题
| 现象 | 原因 | 处理 |
|---|---|---|
| 要 webp | Accept 头 | 官方支持 png/webp/jpg |
| 透明图 | transparency_handling | 见文档枚举 |
| Cleanup 去杂物 | 另一端点 | 见 Cleanup 文档 |
模板规则
- 仅处理授权素材。
- 勿去水印侵权。
- 密钥与额度分开监控。
项目资源
| 用途 | 地址 |
|---|---|
| Remove Background(官方) | https://clipdrop.co/apis/docs/remove-background |
| Clipdrop APIs | https://clipdrop.co/apis |
| Clipdrop | https://clipdrop.co/ |
去背是第一步,版权与额度同样要管。