强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Buku 书签管理完全指南 / 第 09 章:HTTP API

第 09 章:HTTP API

深入了解 Buku 的 RESTful API 接口,实现编程访问、脚本集成和自动化工作流。

9.1 API 概述

bukuserver 提供完整的 RESTful API,支持通过 HTTP 请求管理书签数据。

API 端点总览

方法端点功能说明
GET/api/bookmarks获取所有书签支持分页和搜索
GET/api/bookmarks/{id}获取单个书签返回书签详情
POST/api/bookmarks添加书签JSON 请求体
PUT/api/bookmarks/{id}更新书签部分或完整更新
DELETE/api/bookmarks/{id}删除书签删除指定书签
GET/api/tags获取所有标签含使用计数
GET/api/bookmarks/search搜索书签支持关键词和标签

API 数据格式

// 书签对象结构
{
  "id": 1,
  "url": "https://github.com/jarun/buku",
  "title": "Buku - Bookmark Manager",
  "tags": ",bookmark,cli,python,",
  "description": "A powerful bookmark manager"
}

// 标签对象结构
{
  "tags": {
    "python": 15,
    "javascript": 12,
    "tutorial": 8
  }
}

9.2 启动 API 服务

# 启动 bukuserver
buku --sr 8080

# 带 Token 认证启动
buku --sr 8080 --sall token:your_api_token

# 后台运行
nohup buku --sr 8080 > bukuserver.log 2>&1 &

# 验证服务运行
curl http://localhost:8080/api/bookmarks | head

9.3 书签 CRUD 操作

获取所有书签

# 获取所有书签
curl http://localhost:8080/api/bookmarks

# 带 Token 认证
curl -H "Authorization: Bearer your_token" \
     http://localhost:8080/api/bookmarks

# 获取特定书签
curl http://localhost:8080/api/bookmarks/1

添加书签

# 添加书签
curl -X POST http://localhost:8080/api/bookmarks \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://example.com",
    "title": "示例网站",
    "tags": ",demo,test,",
    "description": "这是一个测试书签"
  }'

# 仅添加 URL(自动获取标题)
curl -X POST http://localhost:8080/api/bookmarks \
  -H "Content-Type: application/json" \
  -d '{"url": "https://example.com"}'

更新书签

# 更新书签标题
curl -X PUT http://localhost:8080/api/bookmarks/1 \
  -H "Content-Type: application/json" \
  -d '{"title": "新标题"}'

# 更新多个字段
curl -X PUT http://localhost:8080/api/bookmarks/1 \
  -H "Content-Type: application/json" \
  -d '{
    "title": "新标题",
    "tags": ",newtag1,newtag2,",
    "description": "新描述"
  }'

删除书签

# 删除书签
curl -X DELETE http://localhost:8080/api/bookmarks/1

# 批量删除
for id in 1 2 3 4 5; do
  curl -X DELETE http://localhost:8080/api/bookmarks/$id
done

9.4 搜索 API

# 关键词搜索
curl "http://localhost:8080/api/bookmarks/search?q=python"

# 标签搜索
curl "http://localhost:8080/api/bookmarks/search?tags=python"

# 组合搜索
curl "http://localhost:8080/api/bookmarks/search?q=tutorial&tags=python"

# 分页
curl "http://localhost:8080/api/bookmarks?page=1&per_page=20"

9.5 标签 API

# 获取所有标签
curl http://localhost:8080/api/tags

# 获取特定书签的标签
curl http://localhost:8080/api/bookmarks/1/tags

9.6 Python 客户端示例

#!/usr/bin/env python3
"""buku_api_client.py - Buku API Python 客户端"""

import requests
import json
from typing import Optional, List, Dict


class BukuClient:
    """Buku API 客户端"""

    def __init__(self, base_url: str = "http://localhost:8080",
                 token: Optional[str] = None):
        self.base_url = base_url.rstrip('/')
        self.session = requests.Session()
        if token:
            self.session.headers['Authorization'] = f'Bearer {token}'

    def get_bookmarks(self, page: int = 1, per_page: int = 100) -> List[Dict]:
        """获取所有书签"""
        resp = self.session.get(
            f"{self.base_url}/api/bookmarks",
            params={'page': page, 'per_page': per_page}
        )
        resp.raise_for_status()
        return resp.json()

    def get_bookmark(self, bookmark_id: int) -> Dict:
        """获取单个书签"""
        resp = self.session.get(f"{self.base_url}/api/bookmarks/{bookmark_id}")
        resp.raise_for_status()
        return resp.json()

    def add_bookmark(self, url: str, title: str = "",
                     tags: str = "", description: str = "") -> Dict:
        """添加书签"""
        data = {'url': url}
        if title:
            data['title'] = title
        if tags:
            data['tags'] = tags if tags.startswith(',') else f',{tags},'
        if description:
            data['description'] = description

        resp = self.session.post(
            f"{self.base_url}/api/bookmarks",
            json=data
        )
        resp.raise_for_status()
        return resp.json()

    def update_bookmark(self, bookmark_id: int, **kwargs) -> Dict:
        """更新书签"""
        resp = self.session.put(
            f"{self.base_url}/api/bookmarks/{bookmark_id}",
            json=kwargs
        )
        resp.raise_for_status()
        return resp.json()

    def delete_bookmark(self, bookmark_id: int) -> bool:
        """删除书签"""
        resp = self.session.delete(f"{self.base_url}/api/bookmarks/{bookmark_id}")
        return resp.status_code == 200

    def search(self, query: str = "", tags: str = "") -> List[Dict]:
        """搜索书签"""
        params = {}
        if query:
            params['q'] = query
        if tags:
            params['tags'] = tags

        resp = self.session.get(
            f"{self.base_url}/api/bookmarks/search",
            params=params
        )
        resp.raise_for_status()
        return resp.json()

    def get_tags(self) -> Dict:
        """获取所有标签"""
        resp = self.session.get(f"{self.base_url}/api/tags")
        resp.raise_for_status()
        return resp.json()


# 使用示例
if __name__ == '__main__':
    client = BukuClient(
        base_url="http://localhost:8080",
        token="your_token_here"
    )

    # 添加书签
    result = client.add_bookmark(
        url="https://python.org",
        title="Python 官方网站",
        tags=",python,official,reference,"
    )
    print(f"添加书签: {result}")

    # 搜索书签
    results = client.search(query="python")
    print(f"搜索结果: {len(results)} 条")

    # 获取所有标签
    tags = client.get_tags()
    print(f"标签: {tags}")

9.7 Shell 脚本集成

批量导入脚本

#!/bin/bash
# batch_import.sh - 从 URL 列表批量导入书签

API_URL="http://localhost:8080"
TOKEN="your_token"
INPUT_FILE="$1"

if [ -z "$INPUT_FILE" ]; then
    echo "用法: $0 <url_list_file>"
    exit 1
fi

while IFS= read -r url; do
    [ -z "$url" ] && continue
    echo "添加: $url"
    curl -s -X POST "$API_URL/api/bookmarks" \
      -H "Authorization: Bearer $TOKEN" \
      -H "Content-Type: application/json" \
      -d "{\"url\": \"$url\"}" | jq -r '.id // "失败"'
done < "$INPUT_FILE"

自动标记脚本

#!/bin/bash
# auto_tag.sh - 根据 URL 自动添加标签

API_URL="http://localhost:8080"
TOKEN="your_token"

# 定义规则
declare -A TAG_RULES=(
    ["github.com"]=",github,code,git,"
    ["stackoverflow.com"]=",stackoverflow,q&a,"
    ["docs.python.org"]=",python,doc,reference,"
    ["youtube.com"]=",video,tutorial,"
)

# 获取所有书签
bookmarks=$(curl -s -H "Authorization: Bearer $TOKEN" \
            "$API_URL/api/bookmarks")

# 处理每个书签
echo "$bookmarks" | jq -c '.[]' | while read -r bookmark; do
    id=$(echo "$bookmark" | jq -r '.id')
    url=$(echo "$bookmark" | jq -r '.url')
    existing_tags=$(echo "$bookmark" | jq -r '.tags')

    # 检查规则
    for domain in "${!TAG_RULES[@]}"; do
        if echo "$url" | grep -q "$domain"; then
            new_tags="${TAG_RULES[$domain]}"

            # 避免重复添加
            if ! echo "$existing_tags" | grep -q "${new_tags:1:5}"; then
                combined_tags="${existing_tags%,},${new_tags:1}"
                curl -s -X PUT "$API_URL/api/bookmarks/$id" \
                  -H "Authorization: Bearer $TOKEN" \
                  -H "Content-Type: application/json" \
                  -d "{\"tags\": \"$combined_tags\"}" > /dev/null
                echo "更新 #$id: 添加标签 $new_tags"
            fi
        fi
    done
done

链接检查脚本

#!/bin/bash
# check_links_api.sh - 使用 API 检查死链

API_URL="http://localhost:8080"
TOKEN="your_token"

# 获取所有书签
bookmarks=$(curl -s -H "Authorization: Bearer $TOKEN" \
            "$API_URL/api/bookmarks")

# 检查每个链接
echo "$bookmarks" | jq -c '.[]' | while read -r bookmark; do
    id=$(echo "$bookmark" | jq -r '.id')
    url=$(echo "$bookmark" | jq -r '.url')
    title=$(echo "$bookmark" | jq -r '.title')

    status=$(curl -o /dev/null -s -w "%{http_code}" \
             --connect-timeout 5 --max-time 10 "$url" 2>/dev/null)

    if [ "$status" = "000" ]; then
        echo "[无法连接] #$id: $title ($url)"
    elif [ "$status" != "200" ]; then
        echo "[${status}] #$id: $title ($url)"
    fi
done

9.8 自动化工作流

定时备份工作流

#!/bin/bash
# workflow_backup.sh - 定时备份书签

API_URL="http://localhost:8080"
TOKEN="your_token"
BACKUP_DIR="$HOME/backups/buku"
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p "$BACKUP_DIR"

# 通过 API 导出
curl -s -H "Authorization: Bearer $TOKEN" \
     "$API_URL/api/bookmarks" | \
     python3 -m json.tool > "$BACKUP_DIR/bookmarks_$DATE.json"

# 清理 30 天前的备份
find "$BACKUP_DIR" -name "*.json" -mtime +30 -delete

echo "备份完成: $BACKUP_DIR/bookmarks_$DATE.json"

RSS 自动收藏

#!/bin/bash
# rss_to_buku.sh - 从 RSS 订阅自动添加书签

API_URL="http://localhost:8080"
TOKEN="your_token"
RSS_URL="https://example.com/feed.xml"

# 获取 RSS 并解析
curl -s "$RSS_URL" | python3 -c "
import sys
import xml.etree.ElementTree as ET

tree = ET.parse(sys.stdin)
root = tree.getroot()

for item in root.findall('.//item'):
    link = item.find('link').text
    title = item.find('title').text
    tags = ',rss,auto-import,'
    print(f'{link}\t{title}\t{tags}')
" | while IFS=$'\t' read -r url title tags; do
    # 检查是否已存在
    existing=$(curl -s -H "Authorization: Bearer $TOKEN" \
               "$API_URL/api/bookmarks/search?q=$url")

    if echo "$existing" | jq -e '.[0]' > /dev/null 2>&1; then
        echo "已存在: $title"
    else
        curl -s -X POST "$API_URL/api/bookmarks" \
          -H "Authorization: Bearer $TOKEN" \
          -H "Content-Type: application/json" \
          -d "{\"url\": \"$url\", \"title\": \"$title\", \"tags\": \"$tags\"}" > /dev/null
        echo "已添加: $title"
    fi
done

9.9 API 最佳实践

┌────────────────────────────────────────────────────────────┐
│                    API 使用最佳实践                          │
├────────────────────────────────────────────────────────────┤
│                                                            │
│  🔐 安全                                                   │
│  ├── 始终使用 Token 认证                                    │
│  ├── 通过 HTTPS 访问 API                                   │
│  ├── 定期更换 Token                                        │
│  └── 限制 API 访问 IP                                      │
│                                                            │
│  📊 性能                                                   │
│  ├── 使用分页获取大量数据                                    │
│  ├── 避免频繁的批量请求                                     │
│  ├── 缓存不常变化的数据                                     │
│  └── 使用异步请求提高并发性能                                │
│                                                            │
│  🔧 可靠性                                                  │
│  ├── 检查 HTTP 响应状态码                                   │
│  ├── 实现重试机制                                          │
│  ├── 处理超时和连接错误                                     │
│  └── 记录 API 调用日志                                      │
│                                                            │
└────────────────────────────────────────────────────────────┘

9.10 常见问题

问题原因解决方案
API 401 未授权Token 错误或未提供检查 Token 配置
API 404 不存在端点错误或书签不存在检查 URL 和 ID
连接被拒绝服务未启动启动 bukuserver
请求超时网络或服务器问题增加超时时间
JSON 格式错误请求体格式错误检查 JSON 格式

9.11 本章小结

要点说明
API 基础 URLhttp://localhost:8080/api/
认证方式Bearer Token
数据格式JSON
分页支持?page=1&per_page=20
搜索方式?q=keyword&tags=tag

扩展阅读


下一章第 10 章:集成与插件 — 学习将 Buku 集成到浏览器、编辑器和其他工具中。