一言语录获取工具 - 完整技术文档
项目简介
一言语录获取工具是一个简单而强大的Python程序,用于从 一言 API 获取随机的中文语录。该工具提供了多种HTTP请求实现方案,并包含友好的命令行交互界面。
功能特点
- 🔄 获取随机中文语录
- 📝 显示语录来源和作者信息
- 🏷️ 显示语录分类
- 🔌 支持4种不同的HTTP请求方式
- 🛡️ 完善的错误处理机制
- 🎨 友好的命令行界面
问题背景
一言API默认会屏蔽不包含 User-Agent 头的请求,特别是使用Python标准库 urllib 时,默认的User-Agent会被识别并拒绝访问。
错误示例:
# ❌ 这样会失败
import urllib.request
response = urllib.request.urlopen('https://v1.hitokoto.cn/')
# 返回:403 Forbidden
解决方案: 在请求头中添加合法的 User-Agent。
解决方案
方案对比
| 方案 |
库 |
优点 |
缺点 |
推荐度 |
| 方案1 |
urllib |
标准库,无需安装 |
API较原始 |
⭐⭐⭐ |
| 方案2 |
requests |
API友好,功能丰富 |
需第三方库 |
⭐⭐⭐⭐⭐ |
| 方案3 |
httpx |
支持HTTP/2,异步 |
需第三方库 |
⭐⭐⭐⭐ |
| 方案4 |
aiohttp |
纯异步,高性能 |
需第三方库 |
⭐⭐⭐ |
完整代码
方案1:使用 urllib(标准库)
import json
import urllib.request
import urllib.error
def get_hitokoto():
"""
使用urllib获取一言语录
Returns:
dict: 包含语录信息,失败返回None
"""
try:
url = "https://v1.hitokoto.cn/"
# 创建请求并添加User-Agent
req = urllib.request.Request(url)
req.add_header(
'User-Agent',
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
)
# 发送请求
with urllib.request.urlopen(req, timeout=10) as response:
data = response.read()
result = json.loads(data.decode('utf-8'))
return {
'hitokoto': result['hitokoto'],
'from': result.get('from', ''),
'from_who': result.get('from_who', ''),
'type': result['type'],
'id': result.get('id', '')
}
except urllib.error.HTTPError as e:
print(f"HTTP错误: {e.code} - {e.reason}")
return None
except urllib.error.URLError as e:
print(f"网络错误: {e.reason}")
return None
except json.JSONDecodeError as e:
print(f"数据解析错误: {e}")
return None
except Exception as e:
print(f"未知错误: {e}")
return None
def main():
"""主程序"""
print("=" * 50)
print("一言语录获取工具 v1.0 (urllib)")
print("=" * 50)
while True:
print("\n1. 获取随机语录")
print("2. 退出程序")
choice = input("\n请选择操作 (1/2): ").strip()
if choice == '1':
print("\n正在获取语录...")
quote = get_hitokoto()
if quote:
print("\n" + "=" * 40)
print(f"「{quote['hitokoto']}」\n")
# 显示来源信息
if quote['from_who'] or quote['from']:
print("——", end="")
if quote['from_who']:
print(f" {quote['from_who']}", end="")
if quote['from']:
if quote['from_who']:
print(f" 《{quote['from']}》")
else:
print(f" 《{quote['from']}》")
else:
print()
print(f"分类: {quote['type']}")
print("=" * 40)
else:
print("❌ 获取失败,请检查网络连接!")
elif choice == '2':
print("\n感谢使用,再见!👋")
break
else:
print("❌ 无效选择,请输入1或2!")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\n程序被中断,再见!👋")
方案2:使用 requests(推荐)
import requests
import json
def get_hitokoto():
"""
使用requests获取一言语录
Returns:
dict: 包含语录信息,失败返回None
"""
try:
url = "https://v1.hitokoto.cn/"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
'Accept': 'application/json',
'Accept-Language': 'zh-CN,zh;q=0.9'
}
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status() # 检查HTTP错误
result = response.json()
return {
'hitokoto': result['hitokoto'],
'from': result.get('from', ''),
'from_who': result.get('from_who', ''),
'type': result['type'],
'id': result.get('id', '')
}
except requests.exceptions.HTTPError as e:
print(f"HTTP错误: {e.response.status_code}")
return None
except requests.exceptions.ConnectionError:
print("网络连接失败,请检查网络")
return None
except requests.exceptions.Timeout:
print("请求超时")
return None
except json.JSONDecodeError as e:
print(f"数据解析错误: {e}")
return None
except Exception as e:
print(f"未知错误: {e}")
return None
def display_quote(quote):
"""显示语录"""
if not quote:
print("❌ 获取失败")
return
print("\n" + "=" * 40)
print(f"「{quote['hitokoto']}」\n")
# 构建来源信息
source_parts = []
if quote['from_who']:
source_parts.append(quote['from_who'])
if quote['from']:
source_parts.append(f"《{quote['from']}》")
if source_parts:
print("—— " + " ".join(source_parts))
print(f"分类: {quote['type']}")
print("=" * 40)
def main():
"""主程序"""
print("=" * 50)
print("一言语录获取工具 v1.0 (requests)")
print("=" * 50)
while True:
print("\n1. 获取随机语录")
print("2. 退出程序")
choice = input("\n请选择操作 (1/2): ").strip()
if choice == '1':
print("\n正在获取语录...")
quote = get_hitokoto()
display_quote(quote)
elif choice == '2':
print("\n感谢使用,再见!👋")
break
else:
print("❌ 无效选择,请输入1或2!")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\n程序被中断,再见!👋")
方案3:使用 httpx(支持HTTP/2)
import httpx
import json
def get_hitokoto():
"""
使用httpx获取一言语录
Returns:
dict: 包含语录信息,失败返回None
"""
try:
url = "https://v1.hitokoto.cn/"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
}
with httpx.Client(http2=True, timeout=10.0) as client:
response = client.get(url, headers=headers)
response.raise_for_status()
result = response.json()
return {
'hitokoto': result['hitokoto'],
'from': result.get('from', ''),
'from_who': result.get('from_who', ''),
'type': result['type'],
'id': result.get('id', '')
}
except httpx.HTTPStatusError as e:
print(f"HTTP错误: {e.response.status_code}")
return None
except httpx.ConnectError:
print("网络连接失败")
return None
except httpx.TimeoutException:
print("请求超时")
return None
except json.JSONDecodeError as e:
print(f"数据解析错误: {e}")
return None
except Exception as e:
print(f"未知错误: {e}")
return None
def main():
"""主程序"""
print("=" * 50)
print("一言语录获取工具 v1.0 (httpx)")
print("=" * 50)
while True:
print("\n1. 获取随机语录")
print("2. 退出程序")
choice = input("\n请选择操作 (1/2): ").strip()
if choice == '1':
print("\n正在获取语录...")
quote = get_hitokoto()
if quote:
print("\n" + "=" * 40)
print(f"「{quote['hitokoto']}」\n")
if quote['from_who'] or quote['from']:
print("——", end="")
if quote['from_who']:
print(f" {quote['from_who']}", end="")
if quote['from']:
if quote['from_who']:
print(f" 《{quote['from']}》")
else:
print(f" 《{quote['from']}》")
else:
print()
print(f"分类: {quote['type']}")
print("=" * 40)
else:
print("❌ 获取失败,请检查网络连接!")
elif choice == '2':
print("\n感谢使用,再见!👋")
break
else:
print("❌ 无效选择,请输入1或2!")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\n程序被中断,再见!👋")
方案4:使用 aiohttp(异步)
python
import asyncio
import aiohttp
import json
async def get_hitokoto_async():
"""
异步获取一言语录
Returns:
dict: 包含语录信息,失败返回None
"""
try:
url = "https://v1.hitokoto.cn/"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
}
timeout = aiohttp.ClientTimeout(total=10)
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=headers, timeout=timeout) as response:
if response.status != 200:
print(f"HTTP错误: {response.status}")
return None
result = await response.json()
return {
'hitokoto': result['hitokoto'],
'from': result.get('from', ''),
'from_who': result.get('from_who', ''),
'type': result['type'],
'id': result.get('id', '')
}
except aiohttp.ClientConnectorError:
print("网络连接失败")
return None
except asyncio.TimeoutError:
print("请求超时")
return None
except json.JSONDecodeError as e:
print(f"数据解析错误: {e}")
return None
except Exception as e:
print(f"未知错误: {e}")
return None
def get_hitokoto():
"""同步包装器,调用异步函数"""
return asyncio.run(get_hitokoto_async())
async def get_multiple_hitokoto_async(count=5):
"""异步批量获取多条语录"""
tasks = [get_hitokoto_async() for _ in range(count)]
results = await asyncio.gather(*tasks)
return [r for r in results if r is not None]
def get_multiple_hitokoto(count=5):
"""同步批量获取多条语录"""
return asyncio.run(get_multiple_hitokoto_async(count))
def main():
"""主程序"""
print("=" * 50)
print("一言语录获取工具 v1.0 (aiohttp)")
print("=" * 50)
while True:
print("\n1. 获取随机语录")
print("2. 批量获取5条语录")
print("3. 退出程序")
choice = input("\n请选择操作 (1/2/3): ").strip()
if choice == '1':
print("\n正在获取语录...")
quote = get_hitokoto()
if quote:
print("\n" + "=" * 40)
print(f"「{quote['hitokoto']}」\n")
if quote['from_who'] or quote['from']:
print("——", end="")
if quote['from_who']:
print(f" {quote['from_who']}", end="")
if quote['from']:
if quote['from_who']:
print(f" 《{quote['from']}》")
else:
print(f" 《{quote['from']}》")
else:
print()
print(f"分类: {quote['type']}")
print("=" * 40)
else:
print("❌ 获取失败,请检查网络连接!")
elif choice == '2':
print("\n正在批量获取5条语录...")
quotes = get_multiple_hitokoto(5)
if quotes:
print("\n" + "=" * 40)
for i, quote in enumerate(quotes, 1):
print(f"{i}. 「{quote['hitokoto']}」")
if quote['from_who'] or quote['from']:
source = []
if quote['from_who']:
source.append(quote['from_who'])
if quote['from']:
source.append(f"《{quote['from']}》")
print(f" —— {' '.join(source)}")
print()
print("=" * 40)
else:
print("❌ 获取失败,请检查网络连接!")
elif choice == '3':
print("\n感谢使用,再见!👋")
break
else:
print("❌ 无效选择,请输入1、2或3!")
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n\n程序被中断,再见!👋")
使用说明
安装依赖
方案1(urllib)
# 无需安装额外依赖,使用Python标准库即可
方案2(requests)
pip install requests
方案3(httpx)
pip install httpx```
方案4(aiohttp)
```bash
pip install aiohttp```
## 运行程序
```bash
# 直接运行
python hitokoto.py```
## API参数说明
一言API支持多种查询参数,可以根据需要定制:
参数 类型 说明 可选值
c string 分类筛选 a(动画), b(漫画), c(游戏), d(文学), e(原创), f(网络), g(其他), h(影视), i(诗词), j(网易云), k(哲学), l(抖机灵)
encode string 返回格式 json(默认), text
charset string 字符编码 utf-8(默认)
### 带参数的示例
```python
def get_hitokoto_by_category(category='a'):
"""
获取指定分类的语录
Args:
category: 分类代码 (a,b,c,d,e,f,g,h,i,j,k,l)
"""
try:
url = f"https://v1.hitokoto.cn/?c={category}"
headers = {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
}
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
result = response.json()
return result['hitokoto']
except Exception as e:
print(f"获取失败: {e}")
return None
使用示例
quote = get_hitokoto_by_category('a') # 获取动画分类的语录
print(quote)
分类代码对照表
代码 分类 代码 分类
a 动画 g 其他
b 漫画 h 影视
c 游戏 i 诗词
d 文学 j 网易云
e 原创 k 哲学
f 网络 l 抖机灵