Tencent-WxRead-Daily

Posted on 2026-03-03 17:03  笔名钟意  阅读(2)  评论(0)    收藏  举报

---
title: 微信读书自动阅读
layout: post
cover: 'https://upyun.thatcdn.cn/myself/typora/202309151542253.jpg'
tags: ['Tencent']
categories: ['堆栈']
poster:
headline: ''
caption: ''
color: 'white'
description: '实现微信读书自动阅读, 增加阅读时长, 打卡阅读天数'
date: 2023-09-14 20:00:00

前言

本文章实现需要服务器, 无可视化界面亦可。
使用的Cookie获取上一篇文章有介绍, 顺手写了这篇。

每日一问: 我为什么要实现这个功能???

{% link https://blog.thatcoder.cn/Tencent-WxRead-Cookies/ 微信读书Cookie续活 %}

机制分析

网页版状态下阅读, 每分钟左右会有一个read请求, 通过回执可以判断是否阅读成功。
具体参数我不想耗费时间去逆向, 但是可以通过模拟浏览阅读页面来等待read响应进行read重播,进而轻易实现自动阅读。

稳定性

服务器测试了24小时, 阅读时间也是相应增加24。

有趣的是, 经测试, 每次程序运行5min, 增加的时长可能是 5min、6min、8min、11min、13min 甚至是 21min。
但是总时长是稳定的, 也就是说会回归一天能拉满的时间24h。

实现代码

虽说是浏览器模拟事件, 到了python的表演时间, 但是我采用了JS去写, 辅佐包是 Playwright
总体是一次有趣的尝试。

准备事项

开始吧, 安装 Playwright

# 先创建一个文件夹
mkdir /server/auto/wxread && cd /server/auto/wxread

# 安装 playwright
npm install playwright
npx playwright install
# 下面这个可能需要点时间
# 因为有浏览器的下载
npx playwright install-deps

# 当然少不了 axios
npm install axios

# 好的, 一切准备就绪, 创建代码吧

代码

const { firefox } = require('playwright');
const axios = require('axios');

// 获取命令行参数
const args = process.argv.slice(2);
const params = {};
args.forEach((arg) => {
    const [key, value] = arg.split('=');
    if (key && value) {
        params[key] = value;
    }
});

const url1 = 'https://weread.qq.com/web/reader/8f5329e0813ab7d1eg012feake4d32d5015e4da3b7fbb1fa';
const url2 = 'https://weread.qq.com/web/book/read';
let capturedResponse = null;
let browser = null;

const scrollInterval = 10000; // 上下滑动间隔时间 单位毫秒
const totalTime = 400000; // 单次阅读时间 单位毫秒

const getXHR = async () => {
    console.log("Success: 启动 Playwright 浏览器");
    browser = await firefox.launch({
        headless: true,
    });
    const page = await browser.newPage();
    await page.setExtraHTTPHeaders({
        cookie: (await axios.get("https://sijnzx.laf.thatcoder.cn/tencent-weread-refcookie?key="+params['key'])).data["data"]["cookies"]
    });
    await page.goto(url1, {
        waitUntil: 'networkidle',
    });

    console.log("Success: 打开内容页面");
    page.on('response', async (response) => {
        if (response.url() === url2) {
            const data = await response.json();
            if (data['succ'] === 1) {
                console.log("Success: 目标URL响应成功");
            } else {
                console.log("Error: 目标URL响应失败");
            }
            capturedResponse = data['succ'] === 1 ? response : null;
            await repeatXHR(100);
            // 不要关闭浏览器
        }
    });

// 定期上下滑动
    let scrollCount = 0; // 计数器
    let scrollDirection = 1; // 1表示向下滑动,-1表示向上滑动

    setInterval(async () => {
        await page.evaluate((scrollDirection) => {
            const windowHeight = window.innerHeight;
            window.scrollBy(0, scrollDirection * windowHeight); // 向上或向下滑动一个屏幕高度
        }, scrollDirection);

        scrollCount++;

        // 如果达到了五次滑动,切换方向并重置计数器
        if (scrollCount === 5) {
            scrollDirection *= -1; // 切换方向
            scrollCount = 0; // 重置计数器
        }
    }, scrollInterval);


    // 设置浏览器关闭定时器
    setTimeout(async () => {
        console.log("Success: 关闭浏览器");
        await browser.close();
    }, totalTime);
};

const repeatXHR = async (count) => {
    if (!capturedResponse) {
        console.log("Failed: 没有捕获到响应,无法重放");
        return;
    }
    const request = capturedResponse.request();
    for (let i = 0; i < count; i++) {
        try {
            const response = await axios({
                method: request.method(),
                url: request.url(),
                headers: request.headers(),
                params: request.params,
                data: request.postData(),
            });
            if (response.data.succ !== 1) {
                console.log(`Failed: 重放响应 ${i + 1}: 失败, succ!==1`);
                return;
            }
        } catch (error) {
            console.error(`Failed: 重放响应 ${i + 1}: 失败, ${error.message}`);
        }
    }
    console.log(`Success: 重放响应 ${count} 次完毕`)
};

(async () => {
    await getXHR();
})();

运行

代码会启动一个无头浏览器, 所以没有可视化也不需要担心。
个人测试24小时, 无任何问题, 使用的内存为300MB左右, CPU占用率为0.1%左右。
对了, 带上key参数是我接口的鉴权, 也就是上一篇文章的参数(个人有所修改)。
你实现了上一篇文章的获取可以使用你的接口。保证cookie是有效的即可。

node wxread.js key=xxxx

# 成功运行大概输出如下
#    Success: 启动 Playwright 浏览器
#    Success: 打开内容页面
#    Success: 目标URL响应成功
#    Success: 重放响应 100 次完毕
#    Success: 目标URL响应成功
#    Success: 重放响应 100 次完毕
#    Success: 浏览器关闭 (400秒后)