ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

如何为Kotatsu-Redo编写新的漫画源解析器:Parsers库实战开发教程

如何为Kotatsu-Redo编写新的漫画源解析器:Parsers库实战开发教程

【免费下载链接】Kotatsu-Redo

Manga reader for Android

项目地址:https://gitcode.com/gh_mirrors/ko/Kotatsu-Redo
点击查看免费下载

Kotatsu-Redo是一款免费开源的 Android 漫画阅读器(Manga reader for Android),内置 1200+ 在线漫画源。它的核心设计是把"抓取网站数据"的工作从 App 本体中剥离出来,交给独立的Parsers 解析器库完成。想给这款阅读器新增一个漫画网站源吗?这篇教程将从架构、接口、开发到调试,带你完整走一遍。


1. 先搞懂架构:为什么解析器单独放一个仓库?

Kotatsu-Redo 采用"壳 + 解析器库"的两层结构:

层级位置职责
App 壳(本项目)Kotatsu-Redo阅读界面、书架、下载、同步、源管理等用户体验层
Parsers 解析器库kotatsu-parsers-redo独立仓库每个漫画网站的 HTML/JSON 抓取与解析逻辑

在 app/build.gradle 中可以看到,App 通过 JitPack 依赖引入解析器库:

def parsersVersion = libs.versions.parsers.get() implementation("com.github.clquwu:kotatsu-parsers-redo:$parsersVersion")

版本号定义在 gradle/libs.versions.toml 中。

💡这样做的好处:网站改版了只需更新解析器仓库,App 本身不用重新设计;解析器代码也能在 PC 上用 JVM 直接跑测试,不需要模拟器。

CONTRIBUTING.md 也明确写道:新增漫画源(add a new manga source)请去 parsers 仓库贡献,而不是改 App 主仓库。


2. App 如何"认识"一个新的解析器

新源写好后,App 侧需要三步才能生效,这套机制可以从源码中清晰看到:

① 在MangaParserSource枚举中注册

解析器库里的MangaParserSource是一个枚举,每个枚举值对应一个漫画网站源。App 的源管理模块 MangaSourcesRepository.kt 直接读取该枚举构建"全部可用源"列表:

val allMangaSources: Set<MangaParserSource> = ... MangaParserSource.entries.filterNotTo(it, MangaParserSource::isBroken)

所以你的新源只要出现在枚举里,就会自动被 App 发现、入库,并出现在"源管理"界面中。

② 工厂模式创建解析器实例

用户点击某个源时,App 通过 MangaRepository.kt 中的工厂为源创建对应的仓库对象:

is MangaParserSource -> ParserMangaRepository( parser = loaderContext.newParserInstance(source), cache = contentCache, mirrorSwitcher = mirrorSwitcher, )

newParserInstance会通过反射在解析器库中找到你的解析类并实例化。

③ 统一接口:MangaRepository

无论源多特殊,App 只认 MangaRepository.kt 定义的这套接口——这也是解析器最终要"回答"的问题清单:

方法作用
getList(offset, order, filter)按搜索词/筛选条件获取漫画列表
getDetails(manga)获取漫画详情(封面、作者、简介等)
getPages(chapter)获取某一章的所有页面
getPageUrl(page)获取单页图片直链
getFilterOptions()提供网站支持的高级筛选项(作者、状态等)
getRelated(seed)获取相关推荐(可选)

ParserMangaRepository.kt 展示了 App 如何调用解析器:每个方法都被withMirrors { }包裹——如果配置了多镜像域名,失败后会自动切换域名重试,你写解析器时不必关心这些细节。


3. 动手写一个解析器(核心 5 步)

以下示例代码基于 parsers 库的MangaParser基类风格,帮助你理解需要实现的内容(完整模板以 parsers 仓库为准):

第一步:定义源元信息

在MangaParserSource枚举中新增一个条目,声明源 ID、标题、主域名、支持的语言和类型:

// MangaParserSource.kt(parsers 库) MY_FIRST_SOURCE( id = 2024, // 唯一整数 ID,决定本地数据隔离 title = "My Manga Site", domain = "www.example-manga.com", locale = "en", contentType = ContentType.MANGA, ),

第二步:继承MangaParser,声明基本信息

class MyFirstParser : MangaParser() { override val source: MangaParserSource get() = MangaParserSource.MY_FIRST_SOURCE override val id: Int = 2024 override val name: String = "My Manga Site" override val domain: String get() = config[configKeyDomain] override val availableSortOrders = setOf(SortOrder.UPDATED) }

config/configKeyDomain由基类提供:源设置(如镜像域名、登录 Cookie)会被持久化,App 设置页 SourceSettingsFragment.kt 会自动渲染这些配置项。

第三步:实现漫画列表抓取getList

override suspend fun getList(offset: Int, order: SortOrder, filter: MangaListFilter): List<Manga> { val query = filter.query ?: return getListImpl(offset) val url = "https://$domain/search/?q=${URLEncoder.encode(query, "UTF-8")}" val doc = httpClient.get(url).document() return doc.select(".manga-item").map { Manga( source = source, url = it.select("a").attr("abs:href"), title = it.select(".title").text().trim(), cover = it.select("img").attr("abs:src"), ) } }

🎯httpClient是上下文注入的 OkHttpClient,自动携带 Cookie、User-Agent 和代理设置,直接拿来用即可。

第四步:实现详情与章节getDetails

override suspend fun getDetails(manga: Manga): Manga { val doc = httpClient.get(manga.url).document() val author = doc.select(".info .author").text().removePrefix("Author:").trim() val description = doc.select(".description").text() val status = if (doc.select(".status").text().contains("Finished")) Manga.Status.FINISHED else Manga.Status.ONGOING return manga.copy( author = author, description = description, status = status, tags = doc.select(".tags a").mapToSet { it.text() }, // 章节列表:每个 MangaChapter 对应一章 chapters = doc.select(".chapter-list li a").map { MangaChapter( source = source, url = it.attr("abs:href"), title = it.text().trim(), scanlator = "Example Scan", ) }, ) }

第五步:实现页面获取getPages与getPageUrl

override suspend fun getPages(chapter: MangaChapter): List<MangaPage> { val doc = httpClient.get(chapter.url).document() return doc.select(".reader img").mapToSequentialList { MangaPage( source = source, url = it.attr("abs:src"), scanlator = "Example Scan", ) } } // 若 page.url 已是图片直链,可不覆写(基类默认返回 page.url) override suspend fun getPageUrl(page: MangaPage): String = page.url

至此,一个最小可用的解析器就完成了。剩下的交给 App 的 ParserMangaRepository.kt 去缓存、切换镜像和渲染界面。


4. 用好MangaLoaderContext:解析器能调用的"工具箱"

MangaLoaderContext是 App 注入给解析器的能力集合。App 侧的实现类 MangaLoaderContextImpl.kt 展示了它背后接入了哪些系统能力,对应到解析器可调用的高阶方法:

上下文能力实现典型用途
httpClient/cookieJar自定义 OkHttpClient + Cookie 管理常规请求、登录态保持
evaluateJs(baseUrl, script)隐藏 WebView 执行 JS破解 JS 生成的页面数据、签名
interceptWebViewRequestsWebView 请求拦截嗅探真实图片 API 地址(MangaLoaderContextImpl.kt 可见完整参数)
requestBrowserAction抛出InteractiveActionRequiredException由 App 弹浏览器验证码、人机验证(配合 CloudFlareActivity.kt 处理 CF 挑战)
encodeBase64/getPreferredLocales编解码与本地化图片 Base64 解码、多语言页面选择
redrawImageResponse图片重绘去掉 CDN 防盗水印

⚠️ 遇到 CloudFlare 防护时不要硬抓,直接借助上下文的 CF 处理链路——App 已有现成的 CloudFlareInterceptor.kt 兜底。


5. 调试技巧:用内置 Test 源验证流程

App 内置了一个专为解析器开发准备的"试验田":

  • 调试入口类 TestMangaRepository.kt 的每个方法都留了TODO("...")注释,等于一份可运行的开发清单:先实现getList→ 再实现getDetails→ 最后补getPages;
  • 在 Debug/Nightly 构建中打开Settings → Debug,由 DebugSettingsFragment.kt 的test_parser选项打开TestMangaSource的列表页,即可不依赖真实网站测试整条数据链路;
  • 仓库还附带了 androidTest 测试资产(如 full.json),供集成测试校验列表、章节、边界的解析正确性。

推荐的开发循环:

  1. 在 parsers 仓库的 JVM 单元测试中跑通纯抓取逻辑(速度快,无需设备);
  2. 本地将 App 的parsersVersion指向你刚构建的 commit 版本进行真机验证;
  3. 按 CONTRIBUTING.md 的原则提交:性能优先于代码美观、避免新增不必要依赖(APK 体积很重要)。


6. 进阶:让解析器"更好用"的加分项

  • 多镜像域名:在onCreateConfig中为configKeyDomain提供多个候选域名,App 的 MirrorSwitcher.kt 会在主域失败时自动切换(逻辑见 ParserMangaRepository.kt 的withMirrors);
  • 登录源:实现MangaParserAuthProvider,App 会通过 SourceAuthActivity.kt 引导用户登录;
  • 高级筛选:覆写getFilterOptions与filterCapabilities,把网站上的"作者/状态/章节数"筛选同步到 App 的筛选面板(对应 FilterCoordinator.kt);
  • 相关推荐:覆写getRelatedManga点亮详情页的推荐模块。

小结

环节关键位置
源注册parsers 库MangaParserSource枚举 → MangaSourcesRepository.kt
实例化MangaRepository.kt
接口契约MangaRepository.kt
能力工具箱MangaLoaderContextImpl.kt
调试清单TestMangaRepository.kt
贡献规范CONTRIBUTING.md

写解析器的本质就是"把网站的漫画、章节、图片三样东西,稳定地翻译 App 认识的Manga/MangaChapter/MangaPage三个对象"。抓住这条主线,其余的缓存、镜像、防护拦截都会由框架替你处理。现在就去 parsers 仓库,写出属于你的第一个漫画源吧 🚀

【免费下载链接】Kotatsu-Redo

Manga reader for Android

项目地址:https://gitcode.com/gh_mirrors/ko/Kotatsu-Redo
点击查看免费下载
上一篇:聚类算法深度解析:使用scikit-learn进行无监督学习
下一篇:DailyNotes数据安全详解:AES加密与备份策略全解析

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进