# WeRead — Auto Reading Field-tested against weread.qq.com on 2026-05-06. Requires WeChat login for reading features. --- ## Core Flow ``` Step 1: Check login status ├── Not logged in → Prompt user to scan QR code → Wait for login completion └── Logged in → Proceed to Step 2 Step 2: Check reading progress ├── No progress or finished → Pick first book from New Book ranking → Record status → Proceed to Step 3 └── Unfinished book → Resume reading → Proceed to Step 3 Step 3: Auto reading (1 minute per session) ├── Every 3-5s scroll down 100px ├── See "Next Chapter" → Click, continue reading ├── See "Book Complete" + "Mark as Finished" → Click mark as finished → Record status as finished └── Time's up → Save progress, wait for next session ``` --- ## URL Patterns | Page | URL | |------|-----| | Home | `https://weread.qq.com/` | | Rising | `https://weread.qq.com/web/category/rising` | | Hot Search | `https://weread.qq.com/web/category/hot_search` | | New Book | `https://weread.qq.com/web/category/newbook` | | Book Detail | `https://weread.qq.com/web/bookDetail/{BOOK_ID}` | | Reader | `https://weread.qq.com/web/reader/{BOOK_ID}k{CHAPTER_HASH}` | ### Reader URL Pattern ``` https://weread.qq.com/web/reader/{BOOK_ID}k{CHAPTER_HASH} ``` - `BOOK_ID` — unique book identifier (e.g., `ee0320b053b925ee0519857`) - `CHAPTER_HASH` — chapter hash value (e.g., `08432c902c4084b6fbb18c9`) - Directly accessing the URL jumps to the specified chapter --- ## Step 1: Login Flow ### Login Detection ```python # Check if login is needed login_needed = js(""" const loginBtn = document.querySelector('[class*=login], [class*=Login]'); const qrCode = document.querySelector('[class*=qrcode], [class*=QRCode]'); return !!(loginBtn || qrCode); """) ``` ### Login Process 1. Navigate to `https://weread.qq.com/` 2. Page shows a QR code login prompt 3. User scans the QR code with the WeChat mobile app 4. Page auto-redirects to the home page after successful scan 5. Login state is persisted — no need to re-login ### Wait For Login Completion ```python # Wait for login to complete import time def wait_for_login(timeout=120): start = time.time() while time.time() - start < timeout: # Check if still on login page on_login = js(""" const loginBtn = document.querySelector('[class*=login], [class*=Login]'); return !!loginBtn; """) if not on_login: return True time.sleep(2) return False ``` --- ## Step 2: Reading Progress Management ### progress.json Structure ```json { "book": { "title": "book title", "author": "author", "url": "current chapter URL" }, "progress": { "status": "reading | finished", "currentChapter": "chapter name", "completedChapters": ["list of chapters"], "lastReadTime": "2026-05-06" } } ``` ### Load Progress ```python import json import os PROGRESS_FILE = "progress.json" def load_progress(): if not os.path.exists(PROGRESS_FILE): return None with open(PROGRESS_FILE, "r", encoding="utf-8") as f: return json.load(f) ``` ### Save Progress ```python def save_progress(book_title, author, url, chapter, completed_chapters, status="reading"): progress = { "book": { "title": book_title, "author": author, "url": url }, "progress": { "status": status, "currentChapter": chapter, "completedChapters": completed_chapters, "lastReadTime": time.strftime("%Y-%m-%d") } } with open(PROGRESS_FILE, "w", encoding="utf-8") as f: json.dump(progress, f, ensure_ascii=False, indent=2) ``` ### Check Reading Status ```python def should_pick_new_book(): progress = load_progress() if progress is None: return True # never read before if progress["progress"]["status"] == "finished": return True # already finished return False # has an unfinished book ``` ### Pick Book From New Book Ranking ```python def pick_book_from_new_ranking(): # Navigate to New Book ranking new_tab("https://weread.qq.com/web/category/newbook") wait_for_load() time.sleep(2) # Scroll to top js("window.scrollTo(0, 0)") time.sleep(1) # Click the first book first_book = js(""" const allElements = document.querySelectorAll("[class*=title]"); for (const el of allElements) { const text = el.textContent.trim(); if (text.length > 3 && text.length < 50 && !text.includes("榜")) { const rect = el.getBoundingClientRect(); return { title: text, x: rect.x + rect.width / 2, y: rect.y + rect.height / 2 }; } } return null; """) if first_book: click_at_xy(first_book["x"], first_book["y"]) wait_for_load() time.sleep(1) return first_book["title"] if first_book else None ``` --- ## Step 3: Auto Reading ### Scroll Reading ```python import random def scroll_reading(duration=360): start_time = time.time() chapters_read = [] while time.time() - start_time < duration: # Scroll down 100px js("window.scrollBy(0, 100)") # Random wait 3-5 seconds wait_time = random.uniform(3, 5) time.sleep(wait_time) # Check if "Next Chapter" button is visible next_chapter = find_next_chapter_button() if next_chapter and next_chapter["visible"]: # Record current chapter current = get_current_chapter() chapters_read.append(current) # Click next chapter click_at_xy(next_chapter["x"], next_chapter["y"]) wait_for_load() time.sleep(1) continue # Check if "Book Complete" and "Mark as Finished" finished = check_book_finished() if finished: click_mark_finished() return chapters_read, True # True = book finished return chapters_read, False # False = time's up, book not finished ``` ### Find Next Chapter Button ```python def find_next_chapter_button(): buttons = js(""" const items = []; const elements = document.querySelectorAll("button, a, [role=button], [class*=next], [class*=Next]"); elements.forEach(el => { const text = el.textContent.trim(); if (text.includes("下一章")) { const rect = el.getBoundingClientRect(); items.push({ text: text, x: rect.x + rect.width/2, y: rect.y + rect.height/2, visible: rect.top < window.innerHeight && rect.bottom > 0 }); } }); return items; """) return buttons[0] if buttons else None ``` ### Get Current Chapter ```python def get_current_chapter(): return js(""" const title = document.title; const parts = title.split(" - "); return parts.length > 1 ? parts[1] : "未知章节"; """) ``` ### Check Book Finished ```python def check_book_finished(): return js(""" const elements = document.querySelectorAll("[class*=finish], [class*=complete], [class*=end]"); for (const el of elements) { const text = el.textContent.trim(); if (text.includes("全书完") || text.includes("已读完")) { return true; } } return false; """) ``` ### Click Mark As Finished ```python def click_mark_finished(): button = js(""" const elements = document.querySelectorAll("button, [role=button]"); for (const el of elements) { const text = el.textContent.trim(); if (text.includes("标记读完") || text.includes("标记已读")) { const rect = el.getBoundingClientRect(); return {x: rect.x + rect.width/2, y: rect.y + rect.height/2}; } } return null; """) if button: click_at_xy(button["x"], button["y"]) wait_for_load() time.sleep(1) ``` --- ## Gotchas - **Persistent login** — No need to re-login after first scan, unless browser data is cleared. - **"Next Chapter" button position** — The button is at the bottom of the page; scroll it into the viewport before clicking. - **Scroll interval** — 3-5s random interval simulates real reading; scrolling too fast may trigger detection. - **Chapter URL changes** — Each chapter has a unique URL hash; saving the full URL allows precise position restoration. - **Book finished detection** — Some books may lack a "Book Complete" prompt; adjust detection logic as needed. - **New Book ranking first pick** — Ranking order may change; always fetch the first book in real time.