jwf-common-python 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jwf_common_python/__init__.py +14 -0
- jwf_common_python/db_utils/__init__.py +6 -0
- jwf_common_python/db_utils/clickhouse.py +124 -0
- jwf_common_python/db_utils/elasticsearch.py +196 -0
- jwf_common_python/db_utils/mysql.py +64 -0
- jwf_common_python/eudic/__init__.py +5 -0
- jwf_common_python/eudic/wordbook.py +211 -0
- jwf_common_python/file_storage/__init__.py +2 -0
- jwf_common_python/file_storage/file_interface.py +29 -0
- jwf_common_python/file_storage/local.py +50 -0
- jwf_common_python/file_storage/tencent_cos.py +65 -0
- jwf_common_python/geography/__init__.py +8 -0
- jwf_common_python/geography/district_amap.py +181 -0
- jwf_common_python/geography/search_amap.py +84 -0
- jwf_common_python/github/__init__.py +5 -0
- jwf_common_python/github/webhooks.py +87 -0
- jwf_common_python/language_utils/__init__.py +34 -0
- jwf_common_python/language_utils/core.py +203 -0
- jwf_common_python/language_utils/files.py +52 -0
- jwf_common_python/language_utils/web.py +117 -0
- jwf_common_python/llm/__init__.py +1 -0
- jwf_common_python/llm/aliyun_qwen.py +10 -0
- jwf_common_python/llm/azure_gpt.py +66 -0
- jwf_common_python/llm/baidu_ernie.py +112 -0
- jwf_common_python/message_queue/__init__.py +7 -0
- jwf_common_python/message_queue/interface.py +49 -0
- jwf_common_python/message_queue/kafka.py +145 -0
- jwf_common_python/message_queue/rabbitmq.py +113 -0
- jwf_common_python/oauth2/__init__.py +0 -0
- jwf_common_python/oauth2/google.py +158 -0
- jwf_common_python/parse_subtitle/__init__.py +6 -0
- jwf_common_python/parse_subtitle/aa.vtt +2364 -0
- jwf_common_python/parse_subtitle/parse_srt_file.py +60 -0
- jwf_common_python/pdf/__init__.py +0 -0
- jwf_common_python/pdf/aaa.py +38 -0
- jwf_common_python/pdf/local_parse.py +259 -0
- jwf_common_python/sms/__init__.py +1 -0
- jwf_common_python/sms/interface.py +7 -0
- jwf_common_python/sms/tencent_sms.py +117 -0
- jwf_common_python/translate/__init__.py +152 -0
- jwf_common_python/translate/_youdao.py +234 -0
- jwf_common_python/translate/microsoft.py +696 -0
- jwf_common_python/translate/tencent.py +194 -0
- jwf_common_python/tts/__init__.py +0 -0
- jwf_common_python/tts/microsoft.py +87 -0
- jwf_common_python/utils/__init__.py +0 -0
- jwf_common_python/utils/image_utils.py +18 -0
- jwf_common_python/utils/tencent_utils.py +51 -0
- jwf_common_python/utils/utils.py +46 -0
- jwf_common_python/video/__init__.py +5 -0
- jwf_common_python/video/merge.py +276 -0
- jwf_common_python/weather/__init__.py +0 -0
- jwf_common_python/weather/amap.py +127 -0
- jwf_common_python/weather/wanwei.py +58 -0
- jwf_common_python/youdao/__init__.py +13 -0
- jwf_common_python/youdao/wordbook.py +88 -0
- jwf_common_python-1.0.0.dist-info/METADATA +669 -0
- jwf_common_python-1.0.0.dist-info/RECORD +60 -0
- jwf_common_python-1.0.0.dist-info/WHEEL +4 -0
- jwf_common_python-1.0.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
|
|
3
|
+
import requests
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class WeatherAmap:
|
|
7
|
+
def __init__(self, key: str):
|
|
8
|
+
"""
|
|
9
|
+
city: 城市的 adcode,adcode
|
|
10
|
+
extensions: base 返回实时天气, all 返回预报天气
|
|
11
|
+
"""
|
|
12
|
+
self.key = key
|
|
13
|
+
self.base_url = (
|
|
14
|
+
f"https://restapi.amap.com/v3/weather/weatherInfo?key={self.key}"
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
def _send_request(self, url: str) -> dict:
|
|
18
|
+
response = requests.get(url)
|
|
19
|
+
if response.status_code != 200:
|
|
20
|
+
raise Exception("请求高德地图API失败")
|
|
21
|
+
res = response.json()
|
|
22
|
+
|
|
23
|
+
# 1. 校验响应状态
|
|
24
|
+
status = res["status"]
|
|
25
|
+
assert status == "1", f"请求高德地图API失败,status: {status}"
|
|
26
|
+
info = res.get("info")
|
|
27
|
+
assert info == "OK", f"请求高德地图API失败,info: {info}"
|
|
28
|
+
infocode = res.get("infocode")
|
|
29
|
+
assert infocode == "10000", f"请求高德地图API失败,infocode: {infocode}"
|
|
30
|
+
|
|
31
|
+
return res
|
|
32
|
+
|
|
33
|
+
def get_current_weather(self, city_name: str, city_adcode: str):
|
|
34
|
+
"""获取实时/当前天气
|
|
35
|
+
实况天气每小时更新多次,预报天气每天更新3次,分别在8、11、18点左右更新。
|
|
36
|
+
|
|
37
|
+
返回结构:
|
|
38
|
+
{
|
|
39
|
+
"province": "河南",
|
|
40
|
+
"city": "新安县",
|
|
41
|
+
"adcode": "410323",
|
|
42
|
+
"weather": "晴",
|
|
43
|
+
"temperature": "19",
|
|
44
|
+
"winddirection": "东南",
|
|
45
|
+
"windpower": "≤3",
|
|
46
|
+
"humidity": "36",
|
|
47
|
+
"reporttime": "2024-10-23 14:00:05",
|
|
48
|
+
"temperature_float": "19.0",
|
|
49
|
+
"humidity_float": "36.0"
|
|
50
|
+
}
|
|
51
|
+
"""
|
|
52
|
+
url = f"{self.base_url}&city={city_name}&extensions=base"
|
|
53
|
+
res = self._send_request(url)
|
|
54
|
+
lives_data = res["lives"]
|
|
55
|
+
|
|
56
|
+
# check1 结构检查
|
|
57
|
+
assert len(lives_data) == 1
|
|
58
|
+
live_data = lives_data[0]
|
|
59
|
+
|
|
60
|
+
assert live_data["city"] == city_name
|
|
61
|
+
assert live_data["adcode"] == city_adcode
|
|
62
|
+
|
|
63
|
+
return live_data
|
|
64
|
+
|
|
65
|
+
def get_forecast_weather(
|
|
66
|
+
self, city_name: str, city_adcode: str, isTodayForecast: bool = True
|
|
67
|
+
) -> list[dict]:
|
|
68
|
+
"""获取预报/未来天气
|
|
69
|
+
city_name: 城市名
|
|
70
|
+
adcode: 城市的 adcode
|
|
71
|
+
isTodayForecast: 是否获取今天的天气预报
|
|
72
|
+
- True 表示只获取今天的预报天气。list长度为1,仅有今天的数据. [默认值]
|
|
73
|
+
- False 表示获取未来3天的预报天气。list长度为4
|
|
74
|
+
|
|
75
|
+
返回的数据结构:
|
|
76
|
+
{
|
|
77
|
+
"date": "2024-10-23",
|
|
78
|
+
"week": "3",
|
|
79
|
+
"dayweather": "晴",
|
|
80
|
+
"nightweather": "晴",
|
|
81
|
+
"daytemp": "20",
|
|
82
|
+
"nighttemp": "9",
|
|
83
|
+
"daywind": "东",
|
|
84
|
+
"nightwind": "东",
|
|
85
|
+
"daypower": "1-3",
|
|
86
|
+
"nightpower": "1-3",
|
|
87
|
+
"daytemp_float": "20.0",
|
|
88
|
+
"nighttemp_float": "9.0"
|
|
89
|
+
}
|
|
90
|
+
"""
|
|
91
|
+
url = f"{self.base_url}&city={city_name}&extensions=all"
|
|
92
|
+
res = self._send_request(url)
|
|
93
|
+
forecasts_data = res["forecasts"]
|
|
94
|
+
|
|
95
|
+
# check1 结构检查
|
|
96
|
+
assert len(forecasts_data) == 1
|
|
97
|
+
forecast_data = forecasts_data[0]
|
|
98
|
+
|
|
99
|
+
# check2 数据准确性检查
|
|
100
|
+
city_res = forecast_data["city"]
|
|
101
|
+
assert city_res == city_name, f"请求的城市名与返回的城市名不一致: {city_res} != {city_name}"
|
|
102
|
+
adcode_res = forecast_data["adcode"]
|
|
103
|
+
assert (
|
|
104
|
+
adcode_res == city_adcode
|
|
105
|
+
), f"请求的城市adcode与返回的城市adcode不一致: {adcode_res} != {city_adcode}"
|
|
106
|
+
|
|
107
|
+
# 解析核心数据
|
|
108
|
+
forecast_core_res = forecast_data["casts"]
|
|
109
|
+
|
|
110
|
+
if isTodayForecast:
|
|
111
|
+
# 只获取今天的天气预报
|
|
112
|
+
|
|
113
|
+
# 1. 获取今天的日期字符串
|
|
114
|
+
today_datetime: datetime.datetime = datetime.datetime.now()
|
|
115
|
+
today_date: str = today_datetime.strftime("%Y-%m-%d")
|
|
116
|
+
|
|
117
|
+
# 2. 获取今天的预报天气数据
|
|
118
|
+
today_forecast_res = forecast_core_res[0]
|
|
119
|
+
|
|
120
|
+
# 断言,校验数据获取是否准确
|
|
121
|
+
today_forecast_date = today_forecast_res["date"]
|
|
122
|
+
_msg = f"获取到的预报数据的日期不是今天: 预测日期:'{today_forecast_date}' 实际日期:'{today_date}'"
|
|
123
|
+
assert today_date == today_forecast_date, _msg
|
|
124
|
+
return [today_forecast_res]
|
|
125
|
+
|
|
126
|
+
# 获取未来3天的预报天气
|
|
127
|
+
return forecast_core_res
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# 【万维易源服务商】
|
|
2
|
+
import requests
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class WeatherWanwei:
|
|
6
|
+
"""万维易源历史天气数据查询
|
|
7
|
+
https://market.aliyun.com/apimarket/detail/cmapi010812
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
def __init__(self, key: str):
|
|
11
|
+
self.key = key
|
|
12
|
+
self.base_url = "https://ali-weather.showapi.com/weatherhistory"
|
|
13
|
+
|
|
14
|
+
def get_history_weather(
|
|
15
|
+
self, city_name: str, city_adcode: str, year_month: str
|
|
16
|
+
) -> list[dict]:
|
|
17
|
+
"""获取指定地区、月份的历史天气数据
|
|
18
|
+
:param area: 地区名称,如新安县
|
|
19
|
+
:param month: 年月,格式为YYYYMM, 201601
|
|
20
|
+
|
|
21
|
+
:return: 历史天气数据
|
|
22
|
+
{
|
|
23
|
+
"aqiLevel": "6",
|
|
24
|
+
"wind_direction": "西南风",
|
|
25
|
+
"aqi": "406",
|
|
26
|
+
"wind_power": "微风",
|
|
27
|
+
"area": "新安",
|
|
28
|
+
"time": "20160101",
|
|
29
|
+
"min_temperature": "1",
|
|
30
|
+
"max_temperature": "12",
|
|
31
|
+
"aqiInfo": "严重",
|
|
32
|
+
"weather": "晴"
|
|
33
|
+
}
|
|
34
|
+
"""
|
|
35
|
+
url = f"{self.base_url}?area={city_name}&month={year_month}"
|
|
36
|
+
headers = {"Authorization": f"APPCODE {self.key}"}
|
|
37
|
+
params = {"area": city_name, "month": year_month}
|
|
38
|
+
response = requests.get(url, headers=headers, params=params)
|
|
39
|
+
|
|
40
|
+
# check 1
|
|
41
|
+
if response.status_code != 200:
|
|
42
|
+
raise Exception(f"请求历史天气数据失败,状态码:{response.status_code}")
|
|
43
|
+
res = response.json()
|
|
44
|
+
|
|
45
|
+
# check2
|
|
46
|
+
showapi_res_error = res["showapi_res_error"]
|
|
47
|
+
if showapi_res_error != "":
|
|
48
|
+
raise Exception(f"请求历史天气数据失败,错误码:{showapi_res_error}")
|
|
49
|
+
|
|
50
|
+
# check3
|
|
51
|
+
showapi_res_body = res["showapi_res_body"]
|
|
52
|
+
remark = showapi_res_body["remark"]
|
|
53
|
+
areaCode_res = showapi_res_body["areaCode"]
|
|
54
|
+
if remark != "查询成功!" or areaCode_res != city_adcode:
|
|
55
|
+
raise Exception(f"请求历史天气数据失败,错误信息:{remark} {areaCode_res}")
|
|
56
|
+
|
|
57
|
+
data = showapi_res_body["list"]
|
|
58
|
+
return data
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""有道单词本工具;不读写文件,不打印,不在导入时发起请求。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from collections.abc import Iterable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
import requests
|
|
9
|
+
|
|
10
|
+
WORDBOOK_URL = "https://youdao.com/wordbook/webapi/words"
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def fetch_wordbook_data(
|
|
14
|
+
cookie: str,
|
|
15
|
+
limit: int = 15,
|
|
16
|
+
offset: int = 0,
|
|
17
|
+
timeout: float = 30,
|
|
18
|
+
) -> dict[str, Any]:
|
|
19
|
+
"""获取一页单词本,返回完整 JSON,保留所有响应字段。
|
|
20
|
+
|
|
21
|
+
cookie 为浏览器请求头中的完整 Cookie 字符串。
|
|
22
|
+
HTTP 错误、超时、JSON 解析失败直接抛出 requests 对应异常;
|
|
23
|
+
接口返回失败状态时抛出 ValueError。
|
|
24
|
+
"""
|
|
25
|
+
if not cookie.strip():
|
|
26
|
+
raise ValueError("cookie 不能为空")
|
|
27
|
+
if limit <= 0:
|
|
28
|
+
raise ValueError("limit 必须大于 0")
|
|
29
|
+
if offset < 0:
|
|
30
|
+
raise ValueError("offset 不能小于 0")
|
|
31
|
+
|
|
32
|
+
response = requests.get(
|
|
33
|
+
WORDBOOK_URL,
|
|
34
|
+
params={"limit": limit, "offset": offset},
|
|
35
|
+
headers={"Cookie": cookie},
|
|
36
|
+
timeout=timeout,
|
|
37
|
+
)
|
|
38
|
+
response.raise_for_status()
|
|
39
|
+
data = response.json()
|
|
40
|
+
if not isinstance(data, dict) or data.get("msg") != "SUCCESS":
|
|
41
|
+
raise ValueError("获取有道单词本失败,请检查登录状态和 Cookie")
|
|
42
|
+
return data
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def fetch_all_wordbook_data(
|
|
46
|
+
cookie: str, limit: int = 15, timeout: float = 30
|
|
47
|
+
) -> list[dict[str, Any]]:
|
|
48
|
+
"""按页导出整个单词本,返回每页完整 JSON 响应组成的列表。
|
|
49
|
+
|
|
50
|
+
单词及释义等原始字段位于每页的 data.itemList 中,不提取或删减字段。
|
|
51
|
+
中途请求失败时抛出异常,不将部分结果当作完整导出返回。
|
|
52
|
+
"""
|
|
53
|
+
pages = []
|
|
54
|
+
offset = 0
|
|
55
|
+
while True:
|
|
56
|
+
page = fetch_wordbook_data(cookie, limit=limit, offset=offset, timeout=timeout)
|
|
57
|
+
data = page.get("data")
|
|
58
|
+
if not isinstance(data, dict) or not isinstance(data.get("itemList"), list):
|
|
59
|
+
raise ValueError("有道单词本响应缺少 data.itemList")
|
|
60
|
+
total = data.get("total")
|
|
61
|
+
if not isinstance(total, int) or total < 0:
|
|
62
|
+
raise ValueError("有道单词本响应中的 total 无效")
|
|
63
|
+
|
|
64
|
+
pages.append(page)
|
|
65
|
+
items = data["itemList"]
|
|
66
|
+
offset += len(items)
|
|
67
|
+
if not items and offset < total:
|
|
68
|
+
raise ValueError("有道单词本尚未导出完毕,但接口返回了空页")
|
|
69
|
+
if offset >= total:
|
|
70
|
+
return pages
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def filter_new_words(
|
|
74
|
+
existing_words: Iterable[str], current_words: Iterable[str]
|
|
75
|
+
) -> list[str]:
|
|
76
|
+
"""返回当前词表中尚未存在的单词,按当前顺序去重。
|
|
77
|
+
|
|
78
|
+
两个参数均为单词字符串的可迭代对象;多行文本请先调用 splitlines()。
|
|
79
|
+
去除首尾空白、忽略空词,保留大小写差异,不修改传入的词表。
|
|
80
|
+
"""
|
|
81
|
+
seen = {word.strip() for word in existing_words}
|
|
82
|
+
new_words = []
|
|
83
|
+
for word in current_words:
|
|
84
|
+
word = word.strip()
|
|
85
|
+
if word and word not in seen:
|
|
86
|
+
seen.add(word)
|
|
87
|
+
new_words.append(word)
|
|
88
|
+
return new_words
|