ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Python爬虫实战:从微信读书API导出个人笔记与书架数据

Python爬虫实战:从微信读书API导出个人笔记与书架数据 简介这是一套面向Python初学者与爬虫爱好者的微信读书数据导出工具解决个人学习场景下书籍列表与阅读笔记难以批量保存的问题。资源包共8个文件含3个核心Python脚本GUI界面、主爬虫逻辑、Excel处理、2个文本配置文件、2张界面演示图及1份Markdown说明文档整体仅277KB轻量易部署。已有2168人下载学习适合希望快速上手网页数据抓取、理解登录模拟与API逆向分析的实践者。读者可直接运行pyqt_gui.py启动图形化界面通过内置流程完成微信读书账号登录、书架拉取、笔记提取及Excel一键导出配套requirement.txt确保依赖环境可复现README详述调试要点与法律声明强调仅限学习交流使用。1. 项目缘起为什么我们需要一个自己的“书库”作为一名重度阅读爱好者我几乎把所有的碎片时间都泡在了微信读书上。它确实方便书库庞大社区氛围也不错。但时间久了问题就来了我做的几百条笔记、划的上千条重点都牢牢地锁在平台的服务器里。有一天我突然想离线整理一下某本书的读书笔记或者想用自己习惯的笔记软件比如 Obsidian、Notion来管理这些知识时发现除了手动一条条复制粘贴几乎没有别的办法。这太不“数字游民”了。更现实的是万一哪天某本书因为版权或其他原因下架了或者平台规则变动我们辛苦积累的阅读痕迹就可能面临风险。数据只有掌握在自己手里才是最安心的。于是自己动手丰衣足食——用 Python 写一个爬虫把我在微信读书上的书籍信息和笔记“搬”出来就成了一个非常实际的需求。这不仅仅是技术上的折腾更是对个人数字资产的一种主动管理。2. 核心思路拆解爬虫如何与微信读书“对话”在动手写代码之前我们必须先搞清楚目标对象。微信读书是一个成熟的 App其数据交互主要依靠手机客户端与后端 API应用程序编程接口进行。我们写的爬虫本质上就是模拟一个微信读书 App去和后端服务器“对话”请求那些原本展示在 App 界面上的数据。这个过程可以分解为几个关键步骤身份认证这是第一道也是最关键的一道坎。服务器需要知道你是谁才能决定给你看什么数据。微信读书通常使用基于 Cookie 或 Token 的认证机制。我们需要先登录获取到代表我们身份的“通行证”Cookie。数据定位我们需要找到存储“我的书架”和“我的笔记”的 API 地址。这些地址不会明晃晃地写在网页上需要通过抓包工具如 Fiddler、Charles或浏览器开发者工具的 Network 面板来捕获手机 App 或网页版发出的网络请求。请求与解析用 Python 的requests库携带上一步获取的“通行证”向找到的 API 地址发送 HTTP 请求。服务器会返回数据通常是 JSON 格式。我们需要用 Python 的json库解析这些结构化的数据提取出我们需要的书名、作者、笔记内容、划线段落等信息。数据存储与导出将解析出来的数据按照我们想要的格式如 Markdown、HTML、Excel保存到本地文件完成“导出”的最终步骤。整个流程的核心在于“模拟”和“解析”。下面我们就一步步来实现它。2.1 环境准备与工具选择工欲善其事必先利其器。这里列出本次项目所需的核心工具和库并解释为什么选择它们。Python 3.7这是我们的编程语言。选择较新的版本可以避免一些库的兼容性问题。requestsPython 社区最受欢迎的 HTTP 库用于发送网络请求简单易用且功能强大。我们将用它来模拟浏览器/App 访问 API。pip install requestsjsonPython 标准库无需安装。用于解析服务器返回的 JSON 格式数据。pandas (可选但推荐)强大的数据分析库。在我们需要将书籍和笔记整理成表格如 Excel时它能极大地简化操作。pip install pandas抓包工具这是逆向分析 API 的“眼睛”。我强烈推荐使用Charles Proxy或Fiddler。它们可以截获手机或电脑上所有应用程序的网络流量让我们清晰地看到微信读书 App 具体向哪些网址发送了请求以及请求和响应的具体内容。配置抓包工具需要将手机和电脑设置在同一网络并在手机端安装并信任抓包工具的 CA 证书这是一个标准操作网上教程很多。注意使用爬虫获取数据必须遵守法律法规和网站的robots.txt协议。本项目仅用于个人学习、研究和对自身数据的备份严禁用于商业用途、大量抓取或对服务器造成压力。请合理使用尊重平台。2.2 关键突破口如何获取身份凭证Cookie没有合法的身份服务器不会搭理我们。获取 Cookie 有多种方法这里介绍两种最实用的方法一通过网页版微信读书获取推荐更稳定在电脑浏览器Chrome/Firefox中打开微信读书网页版https://weread.qq.com。使用微信扫码登录。登录成功后按下F12打开开发者工具切换到Network网络标签页。刷新页面在 Network 面板中找到任意一个来自weread.qq.com域名的请求通常是第一个document类型的请求。点击该请求在右侧的Headers选项卡中找到Request Headers请求头部分里面有一行叫做Cookie。这一长串字符就是我们的“通行证”。复制整个Cookie字符串的值。它看起来像这样wr_vidxxx; wr_skeyyyy; wr_rtzzz; ...方法二通过抓包工具获取手机 App 的请求按照抓包工具如 Charles的教程配置好手机代理。在手机上打开微信读书 App进行任意操作如刷新书架。在 Charles 的抓包记录中找到主机名Host为i.weread.qq.com或weread.qq.com的请求。查看该请求的Headers同样可以找到Cookie字段。获取到 Cookie 后我们需要在 Python 代码中用它来构建请求头。import requests # 将你复制的 Cookie 字符串粘贴在这里 MY_COOKIE wr_vidxxx; wr_skeyyyy; wr_rtzzz; ... # 构建请求头模拟一个真实的浏览器请求 headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36, Cookie: MY_COOKIE, Referer: https://weread.qq.com/, # 表明请求来源有时服务器会检查 Accept: application/json, text/plain, */* # 声明我们接受 JSON 格式的响应 }实操心得Cookie 是有有效期的。如果长时间未使用或者你在其他地方修改了密码、退出了登录这个 Cookie 就会失效。代码运行时报错401或403或者返回的数据为空首先就要检查 Cookie 是否还有效。一个简单的验证方法是用这个 Cookie 在浏览器中访问微信读书网页版看是否仍处于登录状态。3. 逆向工程定位核心数据 API有了“通行证”我们还需要知道“金库”的地址。这就需要分析微信读书的网络请求。打开抓包工具或浏览器开发者工具登录微信读书网页版然后进行以下操作并观察网络请求获取书架书籍列表刷新“我的书架”页面。你应该能看到一个类似https://i.weread.qq.com/user/books的请求。它的响应体就是一个 JSON里面包含了书架上的所有书籍基本信息如bookId,title,author,cover等。获取某本书的笔记和划线在网页版打开一本书点击侧边栏的“笔记”图标。网络面板中会出现一个关键请求其 URL 模式通常为https://i.weread.qq.com/book/bookmarklist?bookIdxxxxxx。其中bookId对应具体的书籍。这个接口返回的就是这本书里你所有的笔记、划线和想法。通过这种方式我们可以确定两个最核心的 API书架API:https://i.weread.qq.com/user/books书籍笔记API:https://i.weread.qq.com/book/bookmarklist?bookId{bookId}其中{bookId}需要从书架 API 的响应中获取。这就是我们爬虫的数据流先调用书架 API 拿到所有书的 ID再遍历这些 ID逐个调用笔记 API 获取每本书的详细笔记。3.1 编写代码获取书架列表让我们开始编写第一段核心代码获取完整的书架信息。import requests import json import time # 使用之前构建的 headers def get_bookshelf(headers): 获取微信读书书架列表 url https://i.weread.qq.com/user/books try: response requests.get(url, headersheaders, timeout10) response.raise_for_status() # 如果状态码不是200抛出异常 data response.json() # 打印一下原始数据结构方便理解 # print(json.dumps(data, indent2, ensure_asciiFalse)) books [] # 根据实际API返回结构解析这里是一个常见结构示例 if books in data: for book in data[books]: book_info { bookId: book.get(bookId), title: book.get(title), author: book.get(author), cover: book.get(cover), category: book.get(category), readUpdateTime: book.get(readUpdateTime) # 最后阅读时间 } books.append(book_info) print(f成功获取到 {len(books)} 本书籍信息。) return books except requests.exceptions.RequestException as e: print(f请求书架失败: {e}) return [] except json.JSONDecodeError as e: print(f解析JSON失败: {e}) return [] # 调用函数 books get_bookshelf(headers) if books: for book in books[:3]: # 打印前3本看看 print(f书名{book[title]}, ID: {book[bookId]})这段代码会向书架 API 发送请求并将返回的 JSON 数据解析成一个 Python 字典列表。每个字典代表一本书的基本信息。bookId是后续获取笔记的关键。3.2 编写代码获取单本书的笔记与划线拿到bookId后我们就可以获取这本书里所有的“宝藏”了。def get_book_notes(headers, book_id): 获取指定书籍的所有笔记和划线 url fhttps://i.weread.qq.com/book/bookmarklist?bookId{book_id} try: response requests.get(url, headersheaders, timeout10) response.raise_for_status() data response.json() # 笔记和划线数据通常在 updated 字段下的 bookmarks 列表中 notes [] if updated in data and isinstance(data[updated], list): for item in data[updated]: # item 可能包含多种类型划线(highlight)、笔记(note)、想法(abstract) note_type item.get(type, ) content item.get(markText, ).strip() # 划线的文本 note_content item.get(content, ).strip() # 写的想法/笔记 chapter_title item.get(chapterTitle, 未知章节) create_time item.get(createTime, 0) # 只收集有内容的条目 if content or note_content: note_info { type: note_type, highlight: content, note: note_content, chapter: chapter_title, createTime: time.strftime(%Y-%m-%d %H:%M:%S, time.localtime(createTime)) if create_time else 未知时间 } notes.append(note_info) print(f书籍ID {book_id} 获取到 {len(notes)} 条笔记/划线。) return notes except requests.exceptions.RequestException as e: print(f请求笔记失败 (BookId: {book_id}): {e}) return [] except json.JSONDecodeError as e: print(f解析笔记JSON失败 (BookId: {book_id}): {e}) return [] # 示例获取第一本书的笔记 if books: first_book_id books[0][bookId] notes get_book_notes(headers, first_book_id) if notes: for note in notes[:2]: print(f章节{note[chapter]}) print(f划线{note[highlight][:50]}...) # 只打印前50字符 print(f笔记{note[note]}) print(- * 30)这段代码解析了笔记 API 的返回数据。需要注意的是数据结构可能随着微信读书的更新而变化。上述代码基于一个常见的结构编写你可能需要根据实际抓包看到的数据格式进行微调比如字段名可能是abstract而不是content或者数据藏在另一个嵌套层级里。踩坑实录API 返回的数据结构不是一成不变的。我在不同时期抓包发现笔记列表的路径有时在data.updated有时在data.chapters下的每个章节对象里。最可靠的方法是在编写代码前先用抓包工具捕获一次真实的请求响应然后用json.dumps(data, indent2, ensure_asciiFalse)打印出来仔细研究其结构。这是爬虫开发中至关重要的一步。4. 数据整合与导出打造个人知识库现在我们有了两个核心函数一个获取所有书一个获取某本书的所有笔记。接下来我们需要将它们串联起来并把数据保存成有用的格式。4.1 构建完整的数据流程一个健壮的脚本应该能处理整个书架并考虑到网络请求的礼貌性避免请求过快被封。import time import json def export_all_notes(headers, books, delay1): 导出所有书籍的笔记 all_data {} for i, book in enumerate(books): book_id book[bookId] book_title book[title] print(f正在处理 [{i1}/{len(books)}] {book_title}...) notes get_book_notes(headers, book_id) if notes: all_data[book_id] { bookInfo: book, notes: notes } # 礼貌性延迟避免请求过于频繁 time.sleep(delay) print(所有书籍笔记获取完成) return all_data # 执行导出 all_notes_data export_all_notes(headers, books, delay0.5) # 每本书请求间隔0.5秒4.2 导出为多种格式数据在内存里不算拥有保存到本地硬盘才算。下面提供几种常见的导出格式。格式一JSON结构化便于程序后续处理def save_as_json(data, filenameweread_notes_backup.json): 保存为JSON文件 with open(filename, w, encodingutf-8) as f: # ensure_asciiFalse 确保中文正常显示 json.dump(data, f, indent2, ensure_asciiFalse) print(f数据已保存为 {filename}) save_as_json(all_notes_data)格式二Markdown便于阅读和导入笔记软件Markdown 格式非常灵活我们可以设计成每本书一个章节每条笔记一个列表项。def save_as_markdown(data, filenameweread_notes.md): 保存为Markdown文件 with open(filename, w, encodingutf-8) as f: f.write(# 微信读书笔记导出\n\n) f.write(f导出时间{time.strftime(%Y-%m-%d %H:%M:%S)}\n\n) for book_id, book_data in data.items(): book_info book_data[bookInfo] notes book_data[notes] if not notes: continue f.write(f## {book_info[title]}\n) f.write(f**作者** {book_info.get(author, 未知)}\n\n) current_chapter None for note in notes: # 按章节分组 if note[chapter] ! current_chapter: current_chapter note[chapter] f.write(f### {current_chapter}\n\n) # 写入划线 if note[highlight]: f.write(f {note[highlight]}\n\n) # 写入笔记/想法 if note[note]: f.write(f{note[note]}\n\n) f.write(f*时间{note[createTime]}*\n\n) f.write(---\n\n) f.write(\n\n) print(fMarkdown文件已保存为 {filename})格式三CSV/Excel便于表格化管理和分析使用pandas库可以非常方便地生成表格。import pandas as pd def save_as_excel(data, filenameweread_notes.xlsx): 保存为Excel文件每个Sheet是一本书 # 创建一个Excel写入器 with pd.ExcelWriter(filename, engineopenpyxl) as writer: for book_id, book_data in data.items(): book_info book_data[bookInfo] notes book_data[notes] if not notes: continue # 将笔记列表转换为DataFrame df_notes pd.DataFrame(notes) # 添加书籍信息作为列 df_notes[书名] book_info[title] df_notes[作者] book_info.get(author, ) # 调整列顺序 cols [书名, 作者, chapter, createTime, highlight, note, type] df_notes df_notes[cols] # 将书名作为Sheet名Excel Sheet名有长度和字符限制需要处理 sheet_name book_info[title][:30] # 截取前30个字符 # 移除Sheet名中的非法字符 sheet_name .join(char for char in sheet_name if char.isalnum() or char in ( , _)).strip() if not sheet_name: sheet_name fBook_{book_id[:5]} df_notes.to_excel(writer, sheet_namesheet_name, indexFalse) print(fExcel文件已保存为 {filename}) # 如果安装了pandas则调用 # save_as_excel(all_notes_data)你可以根据需求选择一种或多种格式进行导出。我个人最喜欢的是 Markdown JSON 组合Markdown 用于日常翻阅JSON 作为原始数据备份方便未来进行其他处理。5. 进阶优化与实战避坑指南一个能跑通的脚本只是开始一个健壮、好用、可持续的脚本才是目标。以下是基于我多次实战后总结的进阶技巧和常见问题。5.1 处理分页与大量数据上面的示例假设笔记 API 一次返回所有数据。但有些接口特别是对于笔记非常多的书可能会分页。你需要检查 API 响应中是否有hasMore、next或total这样的字段以及是否可以通过page或limit参数来翻页。如果遇到分页就需要写一个循环直到hasMore为False为止。def get_book_notes_with_pagination(headers, book_id): 处理可能存在的分页 all_notes [] page 1 limit 100 # 假设每页100条 has_more True while has_more: url fhttps://i.weread.qq.com/book/bookmarklist?bookId{book_id}page{page}limit{limit} # ... 发送请求并解析 ... # 假设响应中有 hasMore 和 items 字段 # data response.json() # all_notes.extend(data.get(items, [])) # has_more data.get(hasMore, False) # page 1 # time.sleep(0.5) # 页间延迟 return all_notes5.2 异常处理与重试机制网络请求充满不确定性。必须添加完善的异常处理和重试逻辑。import requests from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry def create_session_with_retry(): 创建一个带重试机制的requests Session session requests.Session() retries Retry(total3, # 总重试次数 backoff_factor0.5, # 重试间隔因子 status_forcelist[500, 502, 503, 504]) # 遇到这些状态码才重试 session.mount(https://, HTTPAdapter(max_retriesretries)) return session # 使用 session 代替 requests.get session create_session_with_retry() response session.get(url, headersheaders, timeout15)5.3 Cookie 失效的自动检测与提醒在脚本开始或请求失败时可以添加一个简单的检测。def check_cookie_validity(headers): 检查Cookie是否有效 test_url https://i.weread.qq.com/user/books try: response requests.get(test_url, headersheaders, timeout5) if response.status_code 200: data response.json() # 有效响应通常包含用户信息或书籍列表 if books in data or user in data: return True return False except: return False if not check_cookie_validity(headers): print(警告Cookie可能已失效请重新获取) # 可以在这里尝试自动打开浏览器指引用户或者直接退出 exit(1)5.4 增量导出与数据去重如果你定期运行这个脚本不希望每次都从头导出所有数据可以实现增量导出。思路是每次导出后记录下每本书最后一条笔记的createTime。下次运行时只获取这个时间点之后的新笔记。这需要你将之前导出的数据如JSON也读入内存进行比对。5.5 关于“爬虫”与“API”的伦理思考最后必须再次强调技术伦理。我们调用的是微信读书提供给其官方客户端使用的 API。虽然我们模拟了客户端的行为但这依然处于一个灰色地带。因此务必做到仅用于个人导出的数据仅供自己备份、整理、学习使用。控制频率在代码中设置合理的延迟time.sleep不要以极快的频率请求避免对服务器造成不必要的负担。尊重版权导出的书籍内容划线文本是用于个人复习切勿公开传播或用于商业用途。关注变化平台 API 随时可能变更。如果某天脚本突然不能用了请首先检查 Cookie 是否有效然后重新抓包分析 API 结构是否发生了变化。这个项目不仅仅是一段爬虫代码它更是一个桥梁连接了你与那些散落在云端的思想碎片。通过技术手段将它们汇聚到本地你才能真正开始消化、连接、构建属于自己的知识体系。希望这份详细的指南和代码能帮你踏出建立个人数字图书馆的第一步。本文还有配套的精品资源点击获取
返回列表