从微信读书 API 抓取高清书籍封面

·

🚀 核心逻辑

  1. API 请求:访问 https://weread.qq.com/api/store/search?keyword=书名
  2. 数据解析:定位 results[0] -> books[0] -> bookInfo -> cover
  3. 高清处理:将封面 URL 中的 /s_ 替换为 /t9_ 获取高清大图。
  4. 本地保存:以书名命名并保存为 .jpg

🐍 1. Python 实现(最简易)

Python 的代码最接近自然语言,适合快速原型开发。

环境准备: pip install requests

import requests

def download_weread_cover(keyword):
    api_url = f"https://weread.qq.com/api/store/search?keyword={keyword}"
    
    try:
        response = requests.get(api_url)
        data = response.json()
        
        # 修正后的路径:results[0] -> books[0]
        # 使用 .get() 增加容错性
        results = data.get("results", [])
        if results and "books" in results[0]:
            first_book = results[0]["books"][0]["bookInfo"]
            cover_url = first_book.get("cover")
            
            if cover_url:
                # 将 /s_ 替换为 /t9_ 以获取高清原图, 微信是使用的/t6 可以自己调整看看,最大是t9
                hd_url = cover_url.replace("/s_", "/t9_")
                img_data = requests.get(hd_url).content
                with open(f"{keyword}.jpg", "wb") as f:
                    f.write(img_data)
                print(f"[Python] 成功!已保存: {keyword}.jpg")
        else:
            print("未找到书籍数据")
            
    except Exception as e:
        print(f"发生错误: {e}")

if __name__ == "__main__":
    import sys
    if len(sys.argv) > 1:
        book_name = sys.argv[1]
        download_weread_cover(book_name)
    else:
        print("请提供书名作为参数,如:python3 main.py 书名")

🟢 2. Node.js 实现(异步 I/O)

Node.js 使用可选链(Optional Chaining)处理深层 JSON 非常优雅。

环境准备: npm install axios

const axios = require('axios');
const fs = require('fs');
const path = require('path');

async function download(keyword) {
    const url = `https://weread.qq.com/api/store/search?keyword=${encodeURIComponent(keyword)}`;
    
    try {
        const { data } = await axios.get(url);
        const firstBook = data.results?.[0]?.books?.[0]?.bookInfo;

        if (!firstBook || !firstBook.cover) {
            console.log("未找到相关书籍或封面");
            return;
        }

        // 核心逻辑:替换为高清地址 t9_
        const hdCoverUrl = firstBook.cover.replace("/s_", "/t9_");

        const response = await axios({
            url: hdCoverUrl,
            method: 'GET',
            responseType: 'stream'
        });

        const fileName = `${keyword}.jpg`;
        const writer = fs.createWriteStream(fileName);
        response.data.pipe(writer);

        writer.on('finish', () => console.log(`[Node.js] 高清封面保存成功: ${fileName}`));
    } catch (err) {
        console.error(`[Node.js] 出错: ${err.message}`);
    }
}

// 获取命令行参数:node index.js 书名
const args = process.argv.slice(2);
if (args.length > 0) {
    download(args[0]);
} else {
    console.log("用法: node index.js <书名>");
}

🦀 3. Rust 实现(高性能与安全)

Rust 提供了极高的运行效率和类型安全,适合对性能有追求的场景。

项目配置 (Cargo.toml):

[dependencies]
reqwest = { version = "0.11", features = ["json", "blocking"] }
serde_json = "1.0"

核心代码 (src/main.rs):

use std::env;
use std::fs::File;
use std::io::copy;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 1. 获取命令行参数
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        println!("用法: cargo run -- <书名>");
        return Ok(());
    }
    let keyword = &args[1];

    let api_url = format!("https://weread.qq.com/api/store/search?keyword={}", keyword);

    // 2. 请求 API
    let resp = reqwest::blocking::get(api_url)?.json::<serde_json::Value>()?;

    // 3. 提取并替换 URL
    if let Some(cover_url) = resp["results"][0]["books"][0]["bookInfo"]["cover"].as_str() {
        // 核心逻辑:字符串替换
        let hd_url = cover_url.replace("/s_", "/t9_");
        
        // 4. 下载高清图
        let mut img_resp = reqwest::blocking::get(hd_url)?;
        let file_name = format!("{}.jpg", keyword);
        let mut dest = File::create(&file_name)?;
        
        copy(&mut img_resp, &mut dest)?;
        println!("[Rust] 高清封面保存成功: {}", file_name);
    } else {
        println!("[Rust] 未找到封面地址");
    }

    Ok(())
}