# Python 123 网盘客户端





## 0. 安装
你可以从 [pypi](https://pypi.org/project/p123client/) 安装最新版本
```console
pip install -U p123client
```
## 1. 导入模块和创建实例
首先导入模块
```python
from p123client import P123Client
```
### 1. 用 token 创建实例
创建客户端对象,需要传入 JWT <kbd>token</kbd>,也就是 `access_token`
```python
# TODO: 访问令牌
token = "..."
client = P123Client(token=token)
```
或者直接写作
```python
client = P123Client(token)
```
不过我更推荐把 <kbd>token</kbd> 写入一个文件中,例如在 `~/123-token.txt`
```python
from pathlib import Path
client = P123Client(token=Path("~/123-token.txt").expanduser())
```
或者直接写作
```python
client = P123Client(Path("~/123-token.txt").expanduser())
```
### 2. 账号和密码登录
```python
# TODO: 手机号或者邮箱
passport = "..."
# TODO: 密码
password = "..."
client = P123Client(passport=passport, password=passport)
```
或者直接写作
```python
client = P123Client(passport, passport)
```
### 3. client_id 和 client_secret 登录
需要先申请成为开发者
https://123yunpan.yuque.com/org-wiki-123yunpan-muaork/cr6ced/hpengmyg32blkbg8
```python
# TODO: 应用标识,创建应用时分配的 appId
client_id = "..."
# TODO: 应用密钥,创建应用时分配的 secretId
client_secret = "..."
client = P123Client(client_id=client_id, client_secret=client_secret)
```
或者直接写作
```python
client = P123Client(client_id, client_secret)
```
### 4. refresh_token 登录
```python
# TODO: 刷新令牌
refresh_token = "..."
client = P123Client(refresh_token=refresh_token)
```
或者直接写作
```python
client = P123Client(refresh_token)
```
**注意**:使用 refresh_token(或者说 oauth 登录),只允许访问 open 接口
### 5. oauth 登录
需要先去开发者后台设置一下回调链接,审核通过后才可用
```python
# TODO: 应用标识,创建应用时分配的 appId
client_id = "..."
# TODO: 应用密钥,创建应用时分配的 secretId
client_secret = "..."
# TODO: 回调链接
redirect_uri = "..."
# TODO: 访问令牌
token = "..."
resp = P123Client.login_with_oauth(
client_id=client_id,
client_secret=client_secret,
redirect_uri=redirect_uri,
token=token,
)
client = P123Client(
client_id=client_id,
client_secret=client_secret,
token=resp["access_token"],
refresh_token=resp["refresh_token"],
)
```
### 6. 扫码登录
当你什么都不传时,就会要求你扫码登录
```python
client = P123Client()
```
### 7. 用 token 来扫码新的 token
你可以用一个 token 去扫码获得另一个 token(仅当不是 oauth 登录时)
```python
client_new = client.login_another()
```
另外你也可以用 oauth 登录,虽然仅能访问 open 接口,但是 QPS 上放得更宽
下面我用 clouddrive 在 123 网盘上用 oauth 登录举例说明第三方挂载应用登录:
https://123yunpan.yuque.com/org-wiki-123yunpan-muaork/cr6ced/kf05anzt1r0qnudd
```python
import requests
from urllib.parse import parse_qsl, urlsplit
resp = client.login_oauth_authorize({
"client_id": "7c278c60da9e43848c45ff8e6fa9da0a",
"redirect_uri": "https://redirect123pan.zhenyunpan.com/redirect_pan123",
"accessToken": client.token,
"state": "http://localhost:19798",
})
with requests.get(resp["url"], allow_redirects=False, stream=True) as response:
resp = dict(parse_qsl(urlsplit(response.headers["location"]).query))
client_new = P123Client(token=resp["access_token"], refresh_token=resp["refresh_token"])
```
### 8. 关于 token 的结构
<kbd>token</kbd> 中包含了一些信息,以 "." 进行分割,可以拆成 3 个部分:
- JWT 算法(经过 base64 加密)
- 用户信息(经过 base64 加密)
- 签名字符串
```python
from base64 import urlsafe_b64decode
from json import loads
token = client.token
method, user_info, sign = token.split(".", 2)
print("JWT 算法:", loads(urlsafe_b64decode(method)))
print("用户信息:", loads(urlsafe_b64decode(user_info+"==")))
print("签名:", sign)
```
## 2. 接口调用
所有需要直接或间接执行 HTTP 请求的接口,都有同步和异步的调用方式,且默认是采用 POST 发送 JSON 请求数据
```python
# 同步调用
client.method(payload)
client.method(payload, async_=False)
# 异步调用
await client.method(payload, async_=True)
```
从根本上讲,除了几个 `staticmethod`,它们都会调用 `P123Client.request`
```python
url = "https://www.123pan.com/api/someapi"
response = client.request(url=url, json={...})
```
当你需要构建自己的扩展模块,以增加一些新的 123 web 接口时,就需要用到此方法了
```python
from collections.abc import Coroutine
from typing import overload, Any, Literal
from p123client import P123Client
class MyCustom123Client(P123Client):
@overload
def foo(
self,
payload: dict,
/,
async_: Literal[False] = False,
**request_kwargs,
) -> dict:
...
@overload
def foo(
self,
payload: dict,
/,
async_: Literal[True],
**request_kwargs,
) -> Coroutine[Any, Any, dict]:
...
def foo(
self,
payload: dict,
/,
async_: bool = False,
**request_kwargs,
) -> dict | Coroutine[Any, Any, dict]:
api = "https://www.123pan.com/api/foo"
return self.request(
api,
method="GET",
params=payload,
async_=async_,
**request_kwargs,
)
@overload
def bar(
self,
payload: dict,
/,
async_: Literal[False] = False,
**request_kwargs,
) -> dict:
...
@overload
def bar(
self,
payload: dict,
/,
async_: Literal[True],
**request_kwargs,
) -> Coroutine[Any, Any, dict]:
...
def bar(
self,
payload: dict,
/,
async_: bool = False,
**request_kwargs,
) -> dict | Coroutine[Any, Any, dict]:
api = "https://www.123pan.com/api/bar"
return self.request(
api,
method="POST",
json=payload,
async_=async_,
**request_kwargs,
)
```
## 3. 检查响应
接口被调用后,如果返回的是 dict 类型的数据(说明原本是 JSON),则可以用 `p123client.check_response` 执行检查。首先会查看其中名为 "code" 的键的对应值,如果为 0 或 200 或者不存在,则原样返回被检查的数据;否则,抛出一个 `p123client.P123OSError` 的实例。
```python
from p123client import check_response
# 检查同步调用
data = check_response(client.method(payload))
# 检查异步调用
data = check_response(await client.method(payload, async_=True))
```
## 4. 辅助工具
一些简单的封装工具可能是必要的,特别是那种实现起来代码量比较少,可以封装成单个函数的。我把平常使用过程中,积累的一些经验具体化为一组工具函数。这些工具函数分别有着不同的功能,如果组合起来使用,或许能解决很多问题。
```python
from p123client import tool
```
## 5. 学习案例
### 1. 创建自定义 uri
```python
from p123client import P123Client
from p123client.tool import make_uri
# TODO: 改成你自己的账户和密码
client = P123Client(passport="手机号或邮箱", password="登录密码")
# TODO: 请改成你要处理的文件 id
file_id = 15688945
print(make_uri(client, file_id))
```
### 2. 由自定义 uri 转存文件到你的网盘
```python
from p123client import P123Client
from p123client.tool import upload_uri
# TODO: 改成你自己的账户和密码
client = P123Client(passport="手机号或邮箱", password="登录密码")
uri = "123://torrentgalaxy.db|1976025090|582aa8bfb0ad8e6f512d9661f6243bdd"
print(upload_uri(client, uri, duplicate=1))
```
### 3. 由自定义 uri 获取下载直链
```python
from p123client import P123Client
from p123client.tool import get_downurl
# TODO: 改成你自己的账户和密码
client = P123Client(passport="手机号或邮箱", password="登录密码")
# 带 s3_key_flag
print(get_downurl(client, "123://torrentgalaxy.db|1976025090|582aa8bfb0ad8e6f512d9661f6243bdd?1812602326-0"))
# 不带 s3_key_flag(会转存)
print(get_downurl(client, "123://torrentgalaxy.db|1976025090|582aa8bfb0ad8e6f512d9661f6243bdd"))
```
### 4. 直链服务
需要先安装 [blacksheep](https://www.neoteroi.dev/blacksheep/)
```console
pip install 'blacksheep[uvicorn]'
```
然后启动如下服务,就可以访问以获取直链了
**带 s3_key_flag**
http://localhost:8123/torrentgalaxy.db|1976025090|582aa8bfb0ad8e6f512d9661f6243bdd?1812602326-0
**不带 s3_key_flag(会转存)**
http://localhost:8123/torrentgalaxy.db|1976025090|582aa8bfb0ad8e6f512d9661f6243bdd
```python
from blacksheep import json, redirect, Application, Request
from p123client import P123Client
from p123client.tool import get_downurl
# TODO: 改成你自己的账户和密码
client = P123Client(passport="", password="")
app = Application(show_error_details=__debug__)
@app.router.route("/{path:uri}", methods=["GET", "HEAD"])
async def index(request: Request, uri: str):
try:
payload = int(uri)
except ValueError:
if uri.count("|") < 2:
return json({"state": False, "message": f"bad uri: {uri!r}"}, 500)
payload = uri
if s3_key_flag := request.url.query:
payload += "?" + s3_key_flag.decode("ascii")
url = await get_downurl(client, payload, quoted=False, async_=True)
return redirect(url)
if __name__ == "__main__":
from uvicorn import run
run(app, host="0.0.0.0", port=8123)
```
### 5. 遍历文件列表
导出的文件信息中,有个 `"uri"`,表示文件的自定义链接,前面以 `123://` 开头,你可以替换成 302 服务的地址,例如 `http://localhost:8123/`
#### 1. 遍历网盘中的文件列表
```python
from p123client import P123Client
from p123client.tool import iterdir
# TODO: 改成你自己的账户和密码
client = P123Client(passport="手机号或邮箱", password="登录密码")
for info in iterdir(client, parent_id=0, max_depth=-1):
print(info)
```
#### 2. 遍历分享中的文件列表,不含目录
```python
from p123client.tool import share_iterdir
# NOTE: 无需登录
for info in share_iterdir(
"https://www.123684.com/s/oec7Vv-CIYWh?提取码:ZY4K",
max_depth=-1,
predicate=lambda a: not a["is_dir"],
):
print(info)
```
#### 3. 导出分享中的文件列表到本地 .json 文件
```python
from orjson import dumps
from p123client.tool import share_iterdir
def export_share_files_json(
link: str,
path: str = "",
cooldown: float = 0,
):
"""把分享链接中的文件列表导出到 json 文件
:param link: 分享链接,可以包含提取码
:param path: 保存的路径,如果不提供则自动确定
:param cooldown: 两次调用之间,冷却的时间(用两次调用开始时的时间差,而不是一次完成到下一次开始的时间差)
"""
print("< 将拉取:", link)
ls: list[dict] = []
for i, a in enumerate(share_iterdir(link, max_depth=-1, cooldown=cooldown), 1):
ls.append(a)
print(i, a)
if ls:
info = ls[0]
if not path:
suffix = f"@{info['ShareKey']},{info['SharePwd']}.json"
path = f"{info['name'][:255-len(suffix)]}{suffix}"
open(path, "wb").write(dumps(ls))
print()
print("> 已拉取:", link)
print("> 已保存:", path)
export_share_files_json("https://www.123684.com/s/oec7Vv-CIYWh?提取码:ZY4K")
```
### 6. 最新的操作记录
在网盘中,你可以按更新时间逆序查询,即可得到最新上传的文件列表
```python
client.fs_list_new({
"orderBy": "update_time",
"orderDirection": "desc",
"SearchData": ".",
})
```
更一般的,你可以在[同步空间](https://www.123pan.com/SynchronousSpace/main)中执行文件操作。
而在拉取文件列表时,需要指定
```python
client.fs_list_new({
"operateType": "SyncSpacePage",
"event": "syncFileList",
"RequestSource": 1,
})
```
同步空间中的增删改操作都有[操作记录](https://www.123pan.com/SynchronousSpace/record),你可以用接口
```python
client.fs_sync_log()
```
## 其它资源
- 如果你需要更详细的文档,特别是关于各种接口的信息,可以阅读
[https://p123client.readthedocs.io/en/latest/](https://p123client.readthedocs.io/en/latest/)
Raw data
{
"_id": null,
"home_page": "https://github.com/ChenyangGao/p123client",
"name": "p123client",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.12",
"maintainer_email": null,
"keywords": "123, webdisk, client",
"author": "ChenyangGao",
"author_email": "wosiwujm@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/e4/85/3e0af42d5746361f2153ba3681887d18b17fed8c70eadf90c1d2d52834cc/p123client-0.0.8.8.tar.gz",
"platform": null,
"description": "# Python 123 \u7f51\u76d8\u5ba2\u6237\u7aef\n\n\n\n\n\n\n\n## 0. \u5b89\u88c5\n\n\u4f60\u53ef\u4ee5\u4ece [pypi](https://pypi.org/project/p123client/) \u5b89\u88c5\u6700\u65b0\u7248\u672c\n\n```console\npip install -U p123client\n```\n\n## 1. \u5bfc\u5165\u6a21\u5757\u548c\u521b\u5efa\u5b9e\u4f8b\n\n\u9996\u5148\u5bfc\u5165\u6a21\u5757\n\n```python\nfrom p123client import P123Client\n```\n\n### 1. \u7528 token \u521b\u5efa\u5b9e\u4f8b\n\n\u521b\u5efa\u5ba2\u6237\u7aef\u5bf9\u8c61\uff0c\u9700\u8981\u4f20\u5165 JWT <kbd>token</kbd>\uff0c\u4e5f\u5c31\u662f `access_token`\n\n```python\n# TODO: \u8bbf\u95ee\u4ee4\u724c\ntoken = \"...\"\n\nclient = P123Client(token=token)\n```\n\n\u6216\u8005\u76f4\u63a5\u5199\u4f5c\n\n```python\nclient = P123Client(token)\n```\n\n\u4e0d\u8fc7\u6211\u66f4\u63a8\u8350\u628a <kbd>token</kbd> \u5199\u5165\u4e00\u4e2a\u6587\u4ef6\u4e2d\uff0c\u4f8b\u5982\u5728 `~/123-token.txt`\n\n```python\nfrom pathlib import Path\n\nclient = P123Client(token=Path(\"~/123-token.txt\").expanduser())\n```\n\n\u6216\u8005\u76f4\u63a5\u5199\u4f5c\n\n```python\nclient = P123Client(Path(\"~/123-token.txt\").expanduser())\n```\n\n### 2. \u8d26\u53f7\u548c\u5bc6\u7801\u767b\u5f55\n\n```python\n# TODO: \u624b\u673a\u53f7\u6216\u8005\u90ae\u7bb1\npassport = \"...\" \n# TODO: \u5bc6\u7801\npassword = \"...\"\n\nclient = P123Client(passport=passport, password=passport)\n```\n\n\u6216\u8005\u76f4\u63a5\u5199\u4f5c\n\n```python\nclient = P123Client(passport, passport)\n```\n\n### 3. client_id \u548c client_secret \u767b\u5f55\n\n\u9700\u8981\u5148\u7533\u8bf7\u6210\u4e3a\u5f00\u53d1\u8005\n\nhttps://123yunpan.yuque.com/org-wiki-123yunpan-muaork/cr6ced/hpengmyg32blkbg8\n\n```python\n# TODO: \u5e94\u7528\u6807\u8bc6\uff0c\u521b\u5efa\u5e94\u7528\u65f6\u5206\u914d\u7684 appId\nclient_id = \"...\" \n# TODO: \u5e94\u7528\u5bc6\u94a5\uff0c\u521b\u5efa\u5e94\u7528\u65f6\u5206\u914d\u7684 secretId\nclient_secret = \"...\"\n\nclient = P123Client(client_id=client_id, client_secret=client_secret)\n```\n\n\u6216\u8005\u76f4\u63a5\u5199\u4f5c\n\n```python\nclient = P123Client(client_id, client_secret)\n```\n\n### 4. refresh_token \u767b\u5f55\n\n```python\n# TODO: \u5237\u65b0\u4ee4\u724c\nrefresh_token = \"...\"\n\nclient = P123Client(refresh_token=refresh_token)\n```\n\n\u6216\u8005\u76f4\u63a5\u5199\u4f5c\n\n```python\nclient = P123Client(refresh_token)\n```\n\n**\u6ce8\u610f**\uff1a\u4f7f\u7528 refresh_token\uff08\u6216\u8005\u8bf4 oauth \u767b\u5f55\uff09\uff0c\u53ea\u5141\u8bb8\u8bbf\u95ee open \u63a5\u53e3\n\n### 5. oauth \u767b\u5f55\n\n\u9700\u8981\u5148\u53bb\u5f00\u53d1\u8005\u540e\u53f0\u8bbe\u7f6e\u4e00\u4e0b\u56de\u8c03\u94fe\u63a5\uff0c\u5ba1\u6838\u901a\u8fc7\u540e\u624d\u53ef\u7528\n\n```python\n# TODO: \u5e94\u7528\u6807\u8bc6\uff0c\u521b\u5efa\u5e94\u7528\u65f6\u5206\u914d\u7684 appId\nclient_id = \"...\" \n# TODO: \u5e94\u7528\u5bc6\u94a5\uff0c\u521b\u5efa\u5e94\u7528\u65f6\u5206\u914d\u7684 secretId\nclient_secret = \"...\"\n# TODO: \u56de\u8c03\u94fe\u63a5\nredirect_uri = \"...\"\n# TODO: \u8bbf\u95ee\u4ee4\u724c\ntoken = \"...\"\n\nresp = P123Client.login_with_oauth(\n client_id=client_id, \n client_secret=client_secret, \n redirect_uri=redirect_uri, \n token=token, \n)\nclient = P123Client(\n client_id=client_id, \n client_secret=client_secret, \n token=resp[\"access_token\"], \n refresh_token=resp[\"refresh_token\"], \n)\n```\n\n### 6. \u626b\u7801\u767b\u5f55\n\n\u5f53\u4f60\u4ec0\u4e48\u90fd\u4e0d\u4f20\u65f6\uff0c\u5c31\u4f1a\u8981\u6c42\u4f60\u626b\u7801\u767b\u5f55\n\n```python\nclient = P123Client()\n```\n\n### 7. \u7528 token \u6765\u626b\u7801\u65b0\u7684 token\n\n\u4f60\u53ef\u4ee5\u7528\u4e00\u4e2a token \u53bb\u626b\u7801\u83b7\u5f97\u53e6\u4e00\u4e2a token\uff08\u4ec5\u5f53\u4e0d\u662f oauth \u767b\u5f55\u65f6\uff09\n\n```python\nclient_new = client.login_another()\n```\n\n\u53e6\u5916\u4f60\u4e5f\u53ef\u4ee5\u7528 oauth \u767b\u5f55\uff0c\u867d\u7136\u4ec5\u80fd\u8bbf\u95ee open \u63a5\u53e3\uff0c\u4f46\u662f QPS \u4e0a\u653e\u5f97\u66f4\u5bbd\n\n\u4e0b\u9762\u6211\u7528 clouddrive \u5728 123 \u7f51\u76d8\u4e0a\u7528 oauth \u767b\u5f55\u4e3e\u4f8b\u8bf4\u660e\u7b2c\u4e09\u65b9\u6302\u8f7d\u5e94\u7528\u767b\u5f55\uff1a\n\nhttps://123yunpan.yuque.com/org-wiki-123yunpan-muaork/cr6ced/kf05anzt1r0qnudd\n\n```python\nimport requests\nfrom urllib.parse import parse_qsl, urlsplit\n\nresp = client.login_oauth_authorize({\n \"client_id\": \"7c278c60da9e43848c45ff8e6fa9da0a\", \n \"redirect_uri\": \"https://redirect123pan.zhenyunpan.com/redirect_pan123\", \n \"accessToken\": client.token, \n \"state\": \"http://localhost:19798\", \n})\nwith requests.get(resp[\"url\"], allow_redirects=False, stream=True) as response:\n resp = dict(parse_qsl(urlsplit(response.headers[\"location\"]).query))\nclient_new = P123Client(token=resp[\"access_token\"], refresh_token=resp[\"refresh_token\"])\n```\n\n### 8. \u5173\u4e8e token \u7684\u7ed3\u6784\n\n<kbd>token</kbd> \u4e2d\u5305\u542b\u4e86\u4e00\u4e9b\u4fe1\u606f\uff0c\u4ee5 \".\" \u8fdb\u884c\u5206\u5272\uff0c\u53ef\u4ee5\u62c6\u6210 3 \u4e2a\u90e8\u5206\uff1a\n\n- JWT \u7b97\u6cd5\uff08\u7ecf\u8fc7 base64 \u52a0\u5bc6\uff09\n- \u7528\u6237\u4fe1\u606f\uff08\u7ecf\u8fc7 base64 \u52a0\u5bc6\uff09\n- \u7b7e\u540d\u5b57\u7b26\u4e32\n\n```python\nfrom base64 import urlsafe_b64decode\nfrom json import loads\n\ntoken = client.token\n\nmethod, user_info, sign = token.split(\".\", 2)\nprint(\"JWT \u7b97\u6cd5:\", loads(urlsafe_b64decode(method)))\nprint(\"\u7528\u6237\u4fe1\u606f:\", loads(urlsafe_b64decode(user_info+\"==\")))\nprint(\"\u7b7e\u540d:\", sign)\n```\n\n## 2. \u63a5\u53e3\u8c03\u7528\n\n\u6240\u6709\u9700\u8981\u76f4\u63a5\u6216\u95f4\u63a5\u6267\u884c HTTP \u8bf7\u6c42\u7684\u63a5\u53e3\uff0c\u90fd\u6709\u540c\u6b65\u548c\u5f02\u6b65\u7684\u8c03\u7528\u65b9\u5f0f\uff0c\u4e14\u9ed8\u8ba4\u662f\u91c7\u7528 POST \u53d1\u9001 JSON \u8bf7\u6c42\u6570\u636e\n\n```python\n# \u540c\u6b65\u8c03\u7528\nclient.method(payload)\nclient.method(payload, async_=False)\n\n# \u5f02\u6b65\u8c03\u7528\nawait client.method(payload, async_=True)\n```\n\n\u4ece\u6839\u672c\u4e0a\u8bb2\uff0c\u9664\u4e86\u51e0\u4e2a `staticmethod`\uff0c\u5b83\u4eec\u90fd\u4f1a\u8c03\u7528 `P123Client.request`\n\n```python\nurl = \"https://www.123pan.com/api/someapi\"\nresponse = client.request(url=url, json={...})\n```\n\n\u5f53\u4f60\u9700\u8981\u6784\u5efa\u81ea\u5df1\u7684\u6269\u5c55\u6a21\u5757\uff0c\u4ee5\u589e\u52a0\u4e00\u4e9b\u65b0\u7684 123 web \u63a5\u53e3\u65f6\uff0c\u5c31\u9700\u8981\u7528\u5230\u6b64\u65b9\u6cd5\u4e86\n\n```python\nfrom collections.abc import Coroutine\nfrom typing import overload, Any, Literal\n\nfrom p123client import P123Client\n\nclass MyCustom123Client(P123Client):\n\n @overload\n def foo(\n self, \n payload: dict, \n /, \n async_: Literal[False] = False, \n **request_kwargs, \n ) -> dict:\n ...\n @overload\n def foo(\n self, \n payload: dict, \n /, \n async_: Literal[True], \n **request_kwargs, \n ) -> Coroutine[Any, Any, dict]:\n ...\n def foo(\n self, \n payload: dict, \n /, \n async_: bool = False, \n **request_kwargs, \n ) -> dict | Coroutine[Any, Any, dict]:\n api = \"https://www.123pan.com/api/foo\"\n return self.request(\n api, \n method=\"GET\", \n params=payload, \n async_=async_, \n **request_kwargs, \n )\n\n @overload\n def bar(\n self, \n payload: dict, \n /, \n async_: Literal[False] = False, \n **request_kwargs, \n ) -> dict:\n ...\n @overload\n def bar(\n self, \n payload: dict, \n /, \n async_: Literal[True], \n **request_kwargs, \n ) -> Coroutine[Any, Any, dict]:\n ...\n def bar(\n self, \n payload: dict, \n /, \n async_: bool = False, \n **request_kwargs, \n ) -> dict | Coroutine[Any, Any, dict]:\n api = \"https://www.123pan.com/api/bar\"\n return self.request(\n api, \n method=\"POST\", \n json=payload, \n async_=async_, \n **request_kwargs, \n )\n```\n\n## 3. \u68c0\u67e5\u54cd\u5e94\n\n\u63a5\u53e3\u88ab\u8c03\u7528\u540e\uff0c\u5982\u679c\u8fd4\u56de\u7684\u662f dict \u7c7b\u578b\u7684\u6570\u636e\uff08\u8bf4\u660e\u539f\u672c\u662f JSON\uff09\uff0c\u5219\u53ef\u4ee5\u7528 `p123client.check_response` \u6267\u884c\u68c0\u67e5\u3002\u9996\u5148\u4f1a\u67e5\u770b\u5176\u4e2d\u540d\u4e3a \"code\" \u7684\u952e\u7684\u5bf9\u5e94\u503c\uff0c\u5982\u679c\u4e3a 0 \u6216 200 \u6216\u8005\u4e0d\u5b58\u5728\uff0c\u5219\u539f\u6837\u8fd4\u56de\u88ab\u68c0\u67e5\u7684\u6570\u636e\uff1b\u5426\u5219\uff0c\u629b\u51fa\u4e00\u4e2a `p123client.P123OSError` \u7684\u5b9e\u4f8b\u3002\n\n```python\nfrom p123client import check_response\n\n# \u68c0\u67e5\u540c\u6b65\u8c03\u7528\ndata = check_response(client.method(payload))\n# \u68c0\u67e5\u5f02\u6b65\u8c03\u7528\ndata = check_response(await client.method(payload, async_=True))\n```\n\n## 4. \u8f85\u52a9\u5de5\u5177\n\n\u4e00\u4e9b\u7b80\u5355\u7684\u5c01\u88c5\u5de5\u5177\u53ef\u80fd\u662f\u5fc5\u8981\u7684\uff0c\u7279\u522b\u662f\u90a3\u79cd\u5b9e\u73b0\u8d77\u6765\u4ee3\u7801\u91cf\u6bd4\u8f83\u5c11\uff0c\u53ef\u4ee5\u5c01\u88c5\u6210\u5355\u4e2a\u51fd\u6570\u7684\u3002\u6211\u628a\u5e73\u5e38\u4f7f\u7528\u8fc7\u7a0b\u4e2d\uff0c\u79ef\u7d2f\u7684\u4e00\u4e9b\u7ecf\u9a8c\u5177\u4f53\u5316\u4e3a\u4e00\u7ec4\u5de5\u5177\u51fd\u6570\u3002\u8fd9\u4e9b\u5de5\u5177\u51fd\u6570\u5206\u522b\u6709\u7740\u4e0d\u540c\u7684\u529f\u80fd\uff0c\u5982\u679c\u7ec4\u5408\u8d77\u6765\u4f7f\u7528\uff0c\u6216\u8bb8\u80fd\u89e3\u51b3\u5f88\u591a\u95ee\u9898\u3002\n\n```python\nfrom p123client import tool\n```\n\n## 5. \u5b66\u4e60\u6848\u4f8b\n\n### 1. \u521b\u5efa\u81ea\u5b9a\u4e49 uri\n\n```python\nfrom p123client import P123Client\nfrom p123client.tool import make_uri\n\n# TODO: \u6539\u6210\u4f60\u81ea\u5df1\u7684\u8d26\u6237\u548c\u5bc6\u7801\nclient = P123Client(passport=\"\u624b\u673a\u53f7\u6216\u90ae\u7bb1\", password=\"\u767b\u5f55\u5bc6\u7801\")\n\n# TODO: \u8bf7\u6539\u6210\u4f60\u8981\u5904\u7406\u7684\u6587\u4ef6 id\nfile_id = 15688945\nprint(make_uri(client, file_id))\n```\n\n### 2. \u7531\u81ea\u5b9a\u4e49 uri \u8f6c\u5b58\u6587\u4ef6\u5230\u4f60\u7684\u7f51\u76d8\n\n```python\nfrom p123client import P123Client\nfrom p123client.tool import upload_uri\n\n# TODO: \u6539\u6210\u4f60\u81ea\u5df1\u7684\u8d26\u6237\u548c\u5bc6\u7801\nclient = P123Client(passport=\"\u624b\u673a\u53f7\u6216\u90ae\u7bb1\", password=\"\u767b\u5f55\u5bc6\u7801\")\n\nuri = \"123://torrentgalaxy.db|1976025090|582aa8bfb0ad8e6f512d9661f6243bdd\"\nprint(upload_uri(client, uri, duplicate=1))\n```\n\n### 3. \u7531\u81ea\u5b9a\u4e49 uri \u83b7\u53d6\u4e0b\u8f7d\u76f4\u94fe\n\n```python\nfrom p123client import P123Client\nfrom p123client.tool import get_downurl\n\n# TODO: \u6539\u6210\u4f60\u81ea\u5df1\u7684\u8d26\u6237\u548c\u5bc6\u7801\nclient = P123Client(passport=\"\u624b\u673a\u53f7\u6216\u90ae\u7bb1\", password=\"\u767b\u5f55\u5bc6\u7801\")\n\n# \u5e26 s3_key_flag\nprint(get_downurl(client, \"123://torrentgalaxy.db|1976025090|582aa8bfb0ad8e6f512d9661f6243bdd?1812602326-0\"))\n# \u4e0d\u5e26 s3_key_flag\uff08\u4f1a\u8f6c\u5b58\uff09\nprint(get_downurl(client, \"123://torrentgalaxy.db|1976025090|582aa8bfb0ad8e6f512d9661f6243bdd\"))\n```\n\n### 4. \u76f4\u94fe\u670d\u52a1\n\n\u9700\u8981\u5148\u5b89\u88c5 [blacksheep](https://www.neoteroi.dev/blacksheep/)\n\n```console\npip install 'blacksheep[uvicorn]'\n```\n\n\u7136\u540e\u542f\u52a8\u5982\u4e0b\u670d\u52a1\uff0c\u5c31\u53ef\u4ee5\u8bbf\u95ee\u4ee5\u83b7\u53d6\u76f4\u94fe\u4e86\n\n**\u5e26 s3_key_flag**\n\nhttp://localhost:8123/torrentgalaxy.db|1976025090|582aa8bfb0ad8e6f512d9661f6243bdd?1812602326-0\n\n**\u4e0d\u5e26 s3_key_flag\uff08\u4f1a\u8f6c\u5b58\uff09**\n\nhttp://localhost:8123/torrentgalaxy.db|1976025090|582aa8bfb0ad8e6f512d9661f6243bdd\n\n```python\nfrom blacksheep import json, redirect, Application, Request\nfrom p123client import P123Client\nfrom p123client.tool import get_downurl\n\n# TODO: \u6539\u6210\u4f60\u81ea\u5df1\u7684\u8d26\u6237\u548c\u5bc6\u7801\nclient = P123Client(passport=\"\", password=\"\")\n\napp = Application(show_error_details=__debug__)\n\n@app.router.route(\"/{path:uri}\", methods=[\"GET\", \"HEAD\"])\nasync def index(request: Request, uri: str):\n try:\n payload = int(uri)\n except ValueError:\n if uri.count(\"|\") < 2:\n return json({\"state\": False, \"message\": f\"bad uri: {uri!r}\"}, 500)\n payload = uri\n if s3_key_flag := request.url.query:\n payload += \"?\" + s3_key_flag.decode(\"ascii\")\n url = await get_downurl(client, payload, quoted=False, async_=True)\n return redirect(url)\n\nif __name__ == \"__main__\":\n from uvicorn import run\n\n run(app, host=\"0.0.0.0\", port=8123)\n```\n\n### 5. \u904d\u5386\u6587\u4ef6\u5217\u8868\n\n\u5bfc\u51fa\u7684\u6587\u4ef6\u4fe1\u606f\u4e2d\uff0c\u6709\u4e2a `\"uri\"`\uff0c\u8868\u793a\u6587\u4ef6\u7684\u81ea\u5b9a\u4e49\u94fe\u63a5\uff0c\u524d\u9762\u4ee5 `123://` \u5f00\u5934\uff0c\u4f60\u53ef\u4ee5\u66ff\u6362\u6210 302 \u670d\u52a1\u7684\u5730\u5740\uff0c\u4f8b\u5982 `http://localhost:8123/`\n\n#### 1. \u904d\u5386\u7f51\u76d8\u4e2d\u7684\u6587\u4ef6\u5217\u8868\n\n```python\nfrom p123client import P123Client\nfrom p123client.tool import iterdir\n\n# TODO: \u6539\u6210\u4f60\u81ea\u5df1\u7684\u8d26\u6237\u548c\u5bc6\u7801\nclient = P123Client(passport=\"\u624b\u673a\u53f7\u6216\u90ae\u7bb1\", password=\"\u767b\u5f55\u5bc6\u7801\")\n\nfor info in iterdir(client, parent_id=0, max_depth=-1):\n print(info)\n```\n\n#### 2. \u904d\u5386\u5206\u4eab\u4e2d\u7684\u6587\u4ef6\u5217\u8868\uff0c\u4e0d\u542b\u76ee\u5f55\n\n```python\nfrom p123client.tool import share_iterdir\n\n# NOTE: \u65e0\u9700\u767b\u5f55\nfor info in share_iterdir(\n \"https://www.123684.com/s/oec7Vv-CIYWh?\u63d0\u53d6\u7801:ZY4K\", \n max_depth=-1, \n predicate=lambda a: not a[\"is_dir\"], \n):\n print(info)\n```\n\n#### 3. \u5bfc\u51fa\u5206\u4eab\u4e2d\u7684\u6587\u4ef6\u5217\u8868\u5230\u672c\u5730 .json \u6587\u4ef6\n\n```python\nfrom orjson import dumps\nfrom p123client.tool import share_iterdir\n\ndef export_share_files_json(\n link: str, \n path: str = \"\", \n cooldown: float = 0, \n):\n \"\"\"\u628a\u5206\u4eab\u94fe\u63a5\u4e2d\u7684\u6587\u4ef6\u5217\u8868\u5bfc\u51fa\u5230 json \u6587\u4ef6\n\n :param link: \u5206\u4eab\u94fe\u63a5\uff0c\u53ef\u4ee5\u5305\u542b\u63d0\u53d6\u7801\n :param path: \u4fdd\u5b58\u7684\u8def\u5f84\uff0c\u5982\u679c\u4e0d\u63d0\u4f9b\u5219\u81ea\u52a8\u786e\u5b9a\n :param cooldown: \u4e24\u6b21\u8c03\u7528\u4e4b\u95f4\uff0c\u51b7\u5374\u7684\u65f6\u95f4\uff08\u7528\u4e24\u6b21\u8c03\u7528\u5f00\u59cb\u65f6\u7684\u65f6\u95f4\u5dee\uff0c\u800c\u4e0d\u662f\u4e00\u6b21\u5b8c\u6210\u5230\u4e0b\u4e00\u6b21\u5f00\u59cb\u7684\u65f6\u95f4\u5dee\uff09\n \"\"\"\n print(\"< \u5c06\u62c9\u53d6:\", link)\n ls: list[dict] = []\n for i, a in enumerate(share_iterdir(link, max_depth=-1, cooldown=cooldown), 1):\n ls.append(a)\n print(i, a)\n if ls:\n info = ls[0]\n if not path:\n suffix = f\"@{info['ShareKey']},{info['SharePwd']}.json\"\n path = f\"{info['name'][:255-len(suffix)]}{suffix}\"\n open(path, \"wb\").write(dumps(ls))\n print()\n print(\"> \u5df2\u62c9\u53d6:\", link)\n print(\"> \u5df2\u4fdd\u5b58:\", path)\n\nexport_share_files_json(\"https://www.123684.com/s/oec7Vv-CIYWh?\u63d0\u53d6\u7801:ZY4K\")\n```\n\n### 6. \u6700\u65b0\u7684\u64cd\u4f5c\u8bb0\u5f55\n\n\u5728\u7f51\u76d8\u4e2d\uff0c\u4f60\u53ef\u4ee5\u6309\u66f4\u65b0\u65f6\u95f4\u9006\u5e8f\u67e5\u8be2\uff0c\u5373\u53ef\u5f97\u5230\u6700\u65b0\u4e0a\u4f20\u7684\u6587\u4ef6\u5217\u8868\n\n```python\nclient.fs_list_new({\n \"orderBy\": \"update_time\", \n \"orderDirection\": \"desc\", \n \"SearchData\": \".\", \n})\n```\n\n\u66f4\u4e00\u822c\u7684\uff0c\u4f60\u53ef\u4ee5\u5728[\u540c\u6b65\u7a7a\u95f4](https://www.123pan.com/SynchronousSpace/main)\u4e2d\u6267\u884c\u6587\u4ef6\u64cd\u4f5c\u3002\n\n\u800c\u5728\u62c9\u53d6\u6587\u4ef6\u5217\u8868\u65f6\uff0c\u9700\u8981\u6307\u5b9a\n\n```python\nclient.fs_list_new({\n \"operateType\": \"SyncSpacePage\", \n \"event\": \"syncFileList\", \n \"RequestSource\": 1, \n})\n```\n\n\u540c\u6b65\u7a7a\u95f4\u4e2d\u7684\u589e\u5220\u6539\u64cd\u4f5c\u90fd\u6709[\u64cd\u4f5c\u8bb0\u5f55](https://www.123pan.com/SynchronousSpace/record)\uff0c\u4f60\u53ef\u4ee5\u7528\u63a5\u53e3\n\n```python\nclient.fs_sync_log()\n```\n\n## \u5176\u5b83\u8d44\u6e90\n\n- \u5982\u679c\u4f60\u9700\u8981\u66f4\u8be6\u7ec6\u7684\u6587\u6863\uff0c\u7279\u522b\u662f\u5173\u4e8e\u5404\u79cd\u63a5\u53e3\u7684\u4fe1\u606f\uff0c\u53ef\u4ee5\u9605\u8bfb\n\n [https://p123client.readthedocs.io/en/latest/](https://p123client.readthedocs.io/en/latest/)\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Python 123 webdisk client.",
"version": "0.0.8.8",
"project_urls": {
"Homepage": "https://github.com/ChenyangGao/p123client",
"Repository": "https://github.com/ChenyangGao/p123client"
},
"split_keywords": [
"123",
" webdisk",
" client"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "388b545b2e4662ae442133b909e1908c8fc7f2b17b429cb040721a4e057b6993",
"md5": "3fdb01ed8a438922a0abca2c838aa7bb",
"sha256": "1c1f2b0750c3a03a9bd7591e862912031081d8d77d12c77f951881db54d81ce7"
},
"downloads": -1,
"filename": "p123client-0.0.8.8-py3-none-any.whl",
"has_sig": false,
"md5_digest": "3fdb01ed8a438922a0abca2c838aa7bb",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.12",
"size": 62905,
"upload_time": "2025-07-25T18:18:30",
"upload_time_iso_8601": "2025-07-25T18:18:30.081129Z",
"url": "https://files.pythonhosted.org/packages/38/8b/545b2e4662ae442133b909e1908c8fc7f2b17b429cb040721a4e057b6993/p123client-0.0.8.8-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "e4853e0af42d5746361f2153ba3681887d18b17fed8c70eadf90c1d2d52834cc",
"md5": "1d4ea27544c166cc6a2368b6d600c31c",
"sha256": "1f4d6effc798505ddef1925beac60bc4231aa88ba0982c139f16b2b542292c1a"
},
"downloads": -1,
"filename": "p123client-0.0.8.8.tar.gz",
"has_sig": false,
"md5_digest": "1d4ea27544c166cc6a2368b6d600c31c",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.12",
"size": 57812,
"upload_time": "2025-07-25T18:18:31",
"upload_time_iso_8601": "2025-07-25T18:18:31.790361Z",
"url": "https://files.pythonhosted.org/packages/e4/85/3e0af42d5746361f2153ba3681887d18b17fed8c70eadf90c1d2d52834cc/p123client-0.0.8.8.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-07-25 18:18:31",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "ChenyangGao",
"github_project": "p123client",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "p123client"
}