# 用 Python 发送企业微信消息
[](https://pypi.org/project/wechat-enterprise-sdk/)
[](https://opensource.org/licenses/MIT)
一个用于发送企业微信应用消息的 Python SDK,内置智能 Access Token 缓存。
## 核心特性
* **消息发送**:支持发送文本、Markdown、图片、文件等消息。
* **智能混合缓存**:自动管理 `access_token`,结合**内存缓存**(适用于常驻服务)和**文件缓存**(适用于高频短脚本,如 Cron),自动续期,避免 API 频率限制。
* **API 封装**:简洁的 API,已封装获取部门、成员信息、手机号换 UserID 等常用接口。
* **健壮的错误处理**:自动处理 API 响应,非 0 `errcode` 将抛出 `WechatEnterpriseError` 异常。
## 安装
```sh
pip install wechat-enterprise-sdk
```
本库依赖 `requests`。
## 快速上手
下面是一个完整的使用示例:
```python
from pathlib import Path
from wechat_enterprise import WechatEnterprise, WechatEnterpriseError
if __name__ == '__main__':
# 替换为你自己的企业微信凭证
CORP_ID = "YOUR_CORP_ID"
APP_ID = "YOUR_APP_ID" # (AgentId)
CORP_SECRET = "YOUR_CORP_SECRET"
# 接收消息的用户账号
USER_LIST = ["ZhangSan", "LiSi"]
try:
# 1. 初始化
# 默认会在当前目录创建 'we_token_cache.json' 用于文件缓存
wechat = WechatEnterprise(corpid=CORP_ID, ...appid=APP_ID, corpsecret=CORP_SECRET)
# 2. 发送文本
print("正在发送文本消息...")
response_text = wechat.send_text("这是一条来自 SDK 的测试文本消息。", USER_LIST)
print(f"发送成功: {response_text}")
# 3. 发送 Markdown
print("\n正在发送 Markdown 消息...")
markdown_content = (
"**Markdown 测试**\n"
"> 引用内容\n"
"包含 `code` 和 [链接](https://work.weixin.qq.com/api/doc/90000/90135/90236)"
)
response_md = wechat.send_markdown(markdown_content, USER_LIST)
print(f"发送成功: {response_md}")
# 4. 发送文件 (示例)
# print("\n正在发送文件...")
# test_file_path = "./test_upload.txt"
# with open(test_file_path, "w") as f:
# f.write("这是一个测试上传的文件。")
#
# response_file = wechat.send_file(test_file_path, USER_LIST)
# print(f"发送成功: {response_file}")
# 5. 获取用户信息
print(f"\n正在获取用户 {USER_LIST[0]} 的信息...")
user_info = wechat.get_user_info(USER_LIST[0])
print(f"获取成功: {user_info.get('name')}")
# 6. 根据手机号获取 userid
# user_id = wechat.get_userid("13800138000")
# print(f"\n手机号对应的 userid: {user_id}")
except WechatEnterpriseError as e:
# 捕获 API 业务异常
print(f"企业微信 API 出错: {e}")
except Exception as e:
# 捕获其他异常,如网络连接、文件读写等
print(f"发生其他错误: {e}")
```
## 核心功能:Access Token 混合缓存
为了避免 `access_token` 在常驻内存的程序中和频繁调用的短脚本中被过度请求,本库默认启用了一个\*\*内存缓存(一级)+ 文件缓存(二级)\*\*的混合策略。
* **内存缓存**:Token 加载到实例内存中,在程序/实例的生命周期内极速获取。
* **文件缓存**:当内存缓存失效时(如脚本重启),会尝试从文件加载 Token,避免了不必要的 API 请求。
### 1\. 默认行为 (推荐)
默认情况下,文件缓存被启用,并会在当前工作目录创建 `we_token_cache.json` 文件。
```python
# 将在 ./we_token_cache.json 读写缓存
wechat = WechatEnterprise(CORP_ID, APP_ID, CORP_SECRET)
```
### 2\. 自定义缓存路径
你可以通过 `cache_path` 参数指定一个自定义的 `Path` 对象路径。
```python
from pathlib import Path
# 指定缓存文件到 /tmp/cache/ 目录下
custom_path = Path("/tmp/cache/my_wechat_token.json")
wechat = WechatEnterprise(CORP_ID, APP_ID, CORP_SECRET, cache_path=custom_path)
```
### 3\. 禁用文件缓存 (仅使用内存缓存)
如果你在只读文件系统(如某些 Serverless 环境)或不希望创建文件,可以将 `cache_path` 设为 `None` 来禁用文件缓存。
```python
# 禁用文件缓存,仅使用实例内存缓存
wechat_mem_only = WechatEnterprise(CORP_ID, APP_ID, CORP_SECRET, cache_path=None)
```
## API 概览
### 消息发送
* `wechat.send_text(content: str, users: List[str]) -> dict`
* `wechat.send_markdown(content: str, users: List[str]) -> dict`
* `wechat.send_image(image_path: str, users: List[str]) -> dict`
* `wechat.send_file(file_path: str, users: List[str]) -> dict`
### 用户与部门
* `wechat.get_user_info(userid: str) -> dict`
* 获取成员详细信息。
* `wechat.get_userid(telephone: str) -> Optional[str]`
* 根据手机号获取成员 `userid`。
* `wechat.get_department_id(dept_id: int = 0) -> dict`
* 获取部门列表。
* `wechat.get_department_userlist(department_id: int = 1) -> dict`
* 获取部门成员简易列表。
## Todo
* [ ] 添加企业微信的其他实用功能(如群机器人、审批等)。
## 联系我
添加微信 「somenzz-enjoy」 备注 「github」
个人公众号 「Python七号」,微信搜一搜关注。
Raw data
{
"_id": null,
"home_page": "https://github.com/somenzz/wechat_enterprise",
"name": "wechat-enterprise-sdk",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "python wechat enterprise, wechat_enterprise, send message",
"author": "somenzz",
"author_email": "somenzz@163.com",
"download_url": "https://files.pythonhosted.org/packages/ee/7a/cb9ccd570dad843fdd285d95f0b2d2b602f38fcc2d844abd1561b80799c8/wechat_enterprise_sdk-1.0.1.tar.gz",
"platform": null,
"description": "# \u7528 Python \u53d1\u9001\u4f01\u4e1a\u5fae\u4fe1\u6d88\u606f\n\n[](https://pypi.org/project/wechat-enterprise-sdk/)\n[](https://opensource.org/licenses/MIT)\n\n\u4e00\u4e2a\u7528\u4e8e\u53d1\u9001\u4f01\u4e1a\u5fae\u4fe1\u5e94\u7528\u6d88\u606f\u7684 Python SDK\uff0c\u5185\u7f6e\u667a\u80fd Access Token \u7f13\u5b58\u3002\n\n## \u6838\u5fc3\u7279\u6027\n\n * **\u6d88\u606f\u53d1\u9001**\uff1a\u652f\u6301\u53d1\u9001\u6587\u672c\u3001Markdown\u3001\u56fe\u7247\u3001\u6587\u4ef6\u7b49\u6d88\u606f\u3002\n * **\u667a\u80fd\u6df7\u5408\u7f13\u5b58**\uff1a\u81ea\u52a8\u7ba1\u7406 `access_token`\uff0c\u7ed3\u5408**\u5185\u5b58\u7f13\u5b58**\uff08\u9002\u7528\u4e8e\u5e38\u9a7b\u670d\u52a1\uff09\u548c**\u6587\u4ef6\u7f13\u5b58**\uff08\u9002\u7528\u4e8e\u9ad8\u9891\u77ed\u811a\u672c\uff0c\u5982 Cron\uff09\uff0c\u81ea\u52a8\u7eed\u671f\uff0c\u907f\u514d API \u9891\u7387\u9650\u5236\u3002\n * **API \u5c01\u88c5**\uff1a\u7b80\u6d01\u7684 API\uff0c\u5df2\u5c01\u88c5\u83b7\u53d6\u90e8\u95e8\u3001\u6210\u5458\u4fe1\u606f\u3001\u624b\u673a\u53f7\u6362 UserID \u7b49\u5e38\u7528\u63a5\u53e3\u3002\n * **\u5065\u58ee\u7684\u9519\u8bef\u5904\u7406**\uff1a\u81ea\u52a8\u5904\u7406 API \u54cd\u5e94\uff0c\u975e 0 `errcode` \u5c06\u629b\u51fa `WechatEnterpriseError` \u5f02\u5e38\u3002\n\n## \u5b89\u88c5\n\n```sh\npip install wechat-enterprise-sdk\n```\n\n\u672c\u5e93\u4f9d\u8d56 `requests`\u3002\n\n## \u5feb\u901f\u4e0a\u624b\n\n\u4e0b\u9762\u662f\u4e00\u4e2a\u5b8c\u6574\u7684\u4f7f\u7528\u793a\u4f8b\uff1a\n\n```python\nfrom pathlib import Path\nfrom wechat_enterprise import WechatEnterprise, WechatEnterpriseError\n\nif __name__ == '__main__':\n # \u66ff\u6362\u4e3a\u4f60\u81ea\u5df1\u7684\u4f01\u4e1a\u5fae\u4fe1\u51ed\u8bc1\n CORP_ID = \"YOUR_CORP_ID\"\n APP_ID = \"YOUR_APP_ID\" # (AgentId)\n CORP_SECRET = \"YOUR_CORP_SECRET\"\n\n # \u63a5\u6536\u6d88\u606f\u7684\u7528\u6237\u8d26\u53f7\n USER_LIST = [\"ZhangSan\", \"LiSi\"]\n\n try:\n # 1. \u521d\u59cb\u5316\n # \u9ed8\u8ba4\u4f1a\u5728\u5f53\u524d\u76ee\u5f55\u521b\u5efa 'we_token_cache.json' \u7528\u4e8e\u6587\u4ef6\u7f13\u5b58\n wechat = WechatEnterprise(corpid=CORP_ID, ...appid=APP_ID, corpsecret=CORP_SECRET)\n\n # 2. \u53d1\u9001\u6587\u672c\n print(\"\u6b63\u5728\u53d1\u9001\u6587\u672c\u6d88\u606f...\")\n response_text = wechat.send_text(\"\u8fd9\u662f\u4e00\u6761\u6765\u81ea SDK \u7684\u6d4b\u8bd5\u6587\u672c\u6d88\u606f\u3002\", USER_LIST)\n print(f\"\u53d1\u9001\u6210\u529f: {response_text}\")\n\n # 3. \u53d1\u9001 Markdown\n print(\"\\n\u6b63\u5728\u53d1\u9001 Markdown \u6d88\u606f...\")\n markdown_content = (\n \"**Markdown \u6d4b\u8bd5**\\n\"\n \"> \u5f15\u7528\u5185\u5bb9\\n\"\n \"\u5305\u542b `code` \u548c [\u94fe\u63a5](https://work.weixin.qq.com/api/doc/90000/90135/90236)\"\n )\n response_md = wechat.send_markdown(markdown_content, USER_LIST)\n print(f\"\u53d1\u9001\u6210\u529f: {response_md}\")\n\n # 4. \u53d1\u9001\u6587\u4ef6 (\u793a\u4f8b)\n # print(\"\\n\u6b63\u5728\u53d1\u9001\u6587\u4ef6...\")\n # test_file_path = \"./test_upload.txt\"\n # with open(test_file_path, \"w\") as f:\n # f.write(\"\u8fd9\u662f\u4e00\u4e2a\u6d4b\u8bd5\u4e0a\u4f20\u7684\u6587\u4ef6\u3002\")\n #\n # response_file = wechat.send_file(test_file_path, USER_LIST)\n # print(f\"\u53d1\u9001\u6210\u529f: {response_file}\")\n\n # 5. \u83b7\u53d6\u7528\u6237\u4fe1\u606f\n print(f\"\\n\u6b63\u5728\u83b7\u53d6\u7528\u6237 {USER_LIST[0]} \u7684\u4fe1\u606f...\")\n user_info = wechat.get_user_info(USER_LIST[0])\n print(f\"\u83b7\u53d6\u6210\u529f: {user_info.get('name')}\")\n \n # 6. \u6839\u636e\u624b\u673a\u53f7\u83b7\u53d6 userid\n # user_id = wechat.get_userid(\"13800138000\")\n # print(f\"\\n\u624b\u673a\u53f7\u5bf9\u5e94\u7684 userid: {user_id}\")\n\n\n except WechatEnterpriseError as e:\n # \u6355\u83b7 API \u4e1a\u52a1\u5f02\u5e38\n print(f\"\u4f01\u4e1a\u5fae\u4fe1 API \u51fa\u9519: {e}\")\n except Exception as e:\n # \u6355\u83b7\u5176\u4ed6\u5f02\u5e38\uff0c\u5982\u7f51\u7edc\u8fde\u63a5\u3001\u6587\u4ef6\u8bfb\u5199\u7b49\n print(f\"\u53d1\u751f\u5176\u4ed6\u9519\u8bef: {e}\")\n```\n\n## \u6838\u5fc3\u529f\u80fd\uff1aAccess Token \u6df7\u5408\u7f13\u5b58\n\n\u4e3a\u4e86\u907f\u514d `access_token` \u5728\u5e38\u9a7b\u5185\u5b58\u7684\u7a0b\u5e8f\u4e2d\u548c\u9891\u7e41\u8c03\u7528\u7684\u77ed\u811a\u672c\u4e2d\u88ab\u8fc7\u5ea6\u8bf7\u6c42\uff0c\u672c\u5e93\u9ed8\u8ba4\u542f\u7528\u4e86\u4e00\u4e2a\\*\\*\u5185\u5b58\u7f13\u5b58\uff08\u4e00\u7ea7\uff09+ \u6587\u4ef6\u7f13\u5b58\uff08\u4e8c\u7ea7\uff09\\*\\*\u7684\u6df7\u5408\u7b56\u7565\u3002\n\n * **\u5185\u5b58\u7f13\u5b58**\uff1aToken \u52a0\u8f7d\u5230\u5b9e\u4f8b\u5185\u5b58\u4e2d\uff0c\u5728\u7a0b\u5e8f/\u5b9e\u4f8b\u7684\u751f\u547d\u5468\u671f\u5185\u6781\u901f\u83b7\u53d6\u3002\n * **\u6587\u4ef6\u7f13\u5b58**\uff1a\u5f53\u5185\u5b58\u7f13\u5b58\u5931\u6548\u65f6\uff08\u5982\u811a\u672c\u91cd\u542f\uff09\uff0c\u4f1a\u5c1d\u8bd5\u4ece\u6587\u4ef6\u52a0\u8f7d Token\uff0c\u907f\u514d\u4e86\u4e0d\u5fc5\u8981\u7684 API \u8bf7\u6c42\u3002\n\n### 1\\. \u9ed8\u8ba4\u884c\u4e3a (\u63a8\u8350)\n\n\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u6587\u4ef6\u7f13\u5b58\u88ab\u542f\u7528\uff0c\u5e76\u4f1a\u5728\u5f53\u524d\u5de5\u4f5c\u76ee\u5f55\u521b\u5efa `we_token_cache.json` \u6587\u4ef6\u3002\n\n```python\n# \u5c06\u5728 ./we_token_cache.json \u8bfb\u5199\u7f13\u5b58\nwechat = WechatEnterprise(CORP_ID, APP_ID, CORP_SECRET)\n```\n\n### 2\\. \u81ea\u5b9a\u4e49\u7f13\u5b58\u8def\u5f84\n\n\u4f60\u53ef\u4ee5\u901a\u8fc7 `cache_path` \u53c2\u6570\u6307\u5b9a\u4e00\u4e2a\u81ea\u5b9a\u4e49\u7684 `Path` \u5bf9\u8c61\u8def\u5f84\u3002\n\n```python\nfrom pathlib import Path\n\n# \u6307\u5b9a\u7f13\u5b58\u6587\u4ef6\u5230 /tmp/cache/ \u76ee\u5f55\u4e0b\ncustom_path = Path(\"/tmp/cache/my_wechat_token.json\")\nwechat = WechatEnterprise(CORP_ID, APP_ID, CORP_SECRET, cache_path=custom_path)\n```\n\n### 3\\. \u7981\u7528\u6587\u4ef6\u7f13\u5b58 (\u4ec5\u4f7f\u7528\u5185\u5b58\u7f13\u5b58)\n\n\u5982\u679c\u4f60\u5728\u53ea\u8bfb\u6587\u4ef6\u7cfb\u7edf\uff08\u5982\u67d0\u4e9b Serverless \u73af\u5883\uff09\u6216\u4e0d\u5e0c\u671b\u521b\u5efa\u6587\u4ef6\uff0c\u53ef\u4ee5\u5c06 `cache_path` \u8bbe\u4e3a `None` \u6765\u7981\u7528\u6587\u4ef6\u7f13\u5b58\u3002\n\n```python\n# \u7981\u7528\u6587\u4ef6\u7f13\u5b58\uff0c\u4ec5\u4f7f\u7528\u5b9e\u4f8b\u5185\u5b58\u7f13\u5b58\nwechat_mem_only = WechatEnterprise(CORP_ID, APP_ID, CORP_SECRET, cache_path=None)\n```\n\n## API \u6982\u89c8\n\n### \u6d88\u606f\u53d1\u9001\n\n * `wechat.send_text(content: str, users: List[str]) -> dict`\n * `wechat.send_markdown(content: str, users: List[str]) -> dict`\n * `wechat.send_image(image_path: str, users: List[str]) -> dict`\n * `wechat.send_file(file_path: str, users: List[str]) -> dict`\n\n### \u7528\u6237\u4e0e\u90e8\u95e8\n\n * `wechat.get_user_info(userid: str) -> dict`\n * \u83b7\u53d6\u6210\u5458\u8be6\u7ec6\u4fe1\u606f\u3002\n * `wechat.get_userid(telephone: str) -> Optional[str]`\n * \u6839\u636e\u624b\u673a\u53f7\u83b7\u53d6\u6210\u5458 `userid`\u3002\n * `wechat.get_department_id(dept_id: int = 0) -> dict`\n * \u83b7\u53d6\u90e8\u95e8\u5217\u8868\u3002\n * `wechat.get_department_userlist(department_id: int = 1) -> dict`\n * \u83b7\u53d6\u90e8\u95e8\u6210\u5458\u7b80\u6613\u5217\u8868\u3002\n\n## Todo\n\n * [ ] \u6dfb\u52a0\u4f01\u4e1a\u5fae\u4fe1\u7684\u5176\u4ed6\u5b9e\u7528\u529f\u80fd\uff08\u5982\u7fa4\u673a\u5668\u4eba\u3001\u5ba1\u6279\u7b49\uff09\u3002\n\n## \u8054\u7cfb\u6211\n\n\u6dfb\u52a0\u5fae\u4fe1 \u300csomenzz-enjoy\u300d \u5907\u6ce8 \u300cgithub\u300d\n\n\u4e2a\u4eba\u516c\u4f17\u53f7 \u300cPython\u4e03\u53f7\u300d\uff0c\u5fae\u4fe1\u641c\u4e00\u641c\u5173\u6ce8\u3002\n",
"bugtrack_url": null,
"license": "MIT",
"summary": null,
"version": "1.0.1",
"project_urls": {
"Homepage": "https://github.com/somenzz/wechat_enterprise"
},
"split_keywords": [
"python wechat enterprise",
" wechat_enterprise",
" send message"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "5f318ec14d7254a0e3b197bf596532e9a76ae62b54d10cde96027b12ddc7dff7",
"md5": "f25d08e8ff17da7682dee239ec452855",
"sha256": "334a6c3882f8264c44e543b410297f3700dac75ff6ba8c29d82dae0d8595664f"
},
"downloads": -1,
"filename": "wechat_enterprise_sdk-1.0.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "f25d08e8ff17da7682dee239ec452855",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 8645,
"upload_time": "2025-10-25T02:57:23",
"upload_time_iso_8601": "2025-10-25T02:57:23.022350Z",
"url": "https://files.pythonhosted.org/packages/5f/31/8ec14d7254a0e3b197bf596532e9a76ae62b54d10cde96027b12ddc7dff7/wechat_enterprise_sdk-1.0.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "ee7acb9ccd570dad843fdd285d95f0b2d2b602f38fcc2d844abd1561b80799c8",
"md5": "ff9302a9b32f204ba3763b84b180e489",
"sha256": "0dee78a5aac56d5cfeee4fded12e5b9a0d7b8c58e855bbbff75e5c8db0b3a09b"
},
"downloads": -1,
"filename": "wechat_enterprise_sdk-1.0.1.tar.gz",
"has_sig": false,
"md5_digest": "ff9302a9b32f204ba3763b84b180e489",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 10746,
"upload_time": "2025-10-25T02:57:24",
"upload_time_iso_8601": "2025-10-25T02:57:24.356158Z",
"url": "https://files.pythonhosted.org/packages/ee/7a/cb9ccd570dad843fdd285d95f0b2d2b602f38fcc2d844abd1561b80799c8/wechat_enterprise_sdk-1.0.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-25 02:57:24",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "somenzz",
"github_project": "wechat_enterprise",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"requirements": [
{
"name": "requests",
"specs": []
},
{
"name": "requests_toolbelt",
"specs": []
}
],
"lcname": "wechat-enterprise-sdk"
}