我就是来瞎写写,顺便写一下打算怎么实现 UOTAN 安卓端(这是一个很长的标题,别问为什么,因为设计规范里要求超过两行用省略号省略显示啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊)

  • 回复 2
  • 查看 177
  • #话题
    app
我突发奇想想要开发一个 柚坛社区 安卓 客户端(爬取的时候看看这段怎么加粗的)。

1738742288722.jpg


所以这边文章就是 水一水代码 ,顺便来 爬点东西.....(爬取的时候看颜色放到哪里了,red是什么值,RGB的话还得转)

不是,怎么还能自定义 font 哇。(这一段是自定义字体)

Kotlin 很好用, Jsoup 也是。 有 API 就更好了 。(这一段是斜体、下划线和中划线)

接下来我要说一说这个新项目的 实现逻辑难点。(字号,到时候看看吧,这应该是px还得转sp)

# 或许也支持 markdown?(看看有没有变为一级标题)

emmm,这是这个项目的开源地址(看一下超链接的包裹、类或标签和普通文本一样吗)

https://github.com/Uotan-Dev/UOTAN-for-Android

本项目开源地址 (文本超链接)

发现 bug 你可以发 issues 或者联系我(邮箱超链接):

mzz@zzhi.onmicrosoft.com

点此反馈

这是我的 UOTAN 账号:汩汩加热装置(主要是看发布后html把这个usernumber塞到哪里了)

这是一个 UOTAN logo

12x.png
(,,,,,应该会是img标签吧)

就先爬这些,接下来是我的一部分实现方法,因为接口很少,所以主要用爬虫,第一次用 Jsoup,希望有大佬指点

代码:
// 伴生对象
companion object {

suspend fun fetchRecommendData(pageCount: Int): FetchResult = withContext(Dispatchers.IO) {

        // 创建一个储存结果的对象
        val result = mutableListOf<ForumRecommendItem>()
var totalPage: Int = 1

        try {

// 设置一个变量存储柚坛社区的网址
            val basicUrl = "https://www.uotan.cn/"
            // 解析网页, document 返回的就是网页 Document 对象
            val document = Jsoup.parse(URL(basicUrl + "ewr-porta/page-$pageCount"), 30000)

/** 爬取总页数 **/
            // 获取总页数所在 class
            val pageNavS = document.getElementsByClass("pageNav-page ").toList()
val pageNav = pageNavS.last()
// 提取 a 标签数据, 即总页数
            totalPage = pageNav.getElementsByTag("a").first()!!.text().toInt()


// 查找 block  porta-masonry 类
            val content = document.getElementsByClass("block  porta-masonry").first()
// 爬取所有文章的 item
            val elements = content!!.getElementsByClass("porta-article-item")
// 获取内容,这里的 element 就是每一个 div 元素
            for (element in elements) {
// 爬取推荐每个元素

                /** 获取文章标题 **/
                /** 获取文章标题 **/

                // 获取标题所在 Element
                val titleElement = element.getElementsByClass("porta-header-text").first()
// 获取其中 span 包裹的标题
                val title = titleElement!!.getElementsByTag("span").first()!!.text()

/** 获取文章简介 **/
                /** 获取文章简介 **/

                // 获取简介所在 Element
                val describeElement = element.getElementsByClass("message-body").first()
// 获取其中 bbWrapper 类中包裹的简介
                val describe = describeElement!!.getElementsByClass("bbWrapper").first()!!
// 并将内容中的换行符替换为 Java 换行符
                    .text().replace("<br>", "\n")


/** 获取文章封面 **/
                /** 获取文章封面 **/

                // 获取封面所在 Element
                val coverElement = element.getElementsByClass("porta-header-image").first()
// 获取每个元素的 style 属性值, 这个属性值包含封面的 URL
                val styleAttr = coverElement!!.attr("style")
// 定义一个正则表达式,用来匹配 style 属性中的 URL
                val urlPattern = "url\\('(.*?)'\\)".toRegex()
// 在 style 属性值中查找符合正则表达式的内容
                val coverUrl = urlPattern.find(styleAttr)!!.groupValues[1]

/** 获取作者头像 **/
                /** 获取作者头像 **/

                // 获取头像所在的 Wrapper
                val avatarElement = element.getElementsByClass("avatarWrapper").first()
// 作者头像分为两种,一种用户自定义了头像,一种用户未自定义头像使用其昵称的第一个字符
                // 存储有头像用户的头像,无头像用户没有这个 tag
                val imgElement = avatarElement!!.getElementsByTag("img").first()
// 这个 span 中还有一个 span 包裹了用户昵称的第一个字符,但所有用户都有这个 span
                val spanElement = avatarElement.getElementsByTag("span").first()
// 建立一个储存头像的变量
                val avatar: String
                // 综上,我们判断用户是否有头像的条件是 imgElement 是否为空
                if (imgElement != null) {
// 如果不为空就获取其中的 attr : "srcset" (这里获取 src 可能会出错,因为采用的懒加载)
                    val src = imgElement.attr("srcset")
// 然后赋值到 avatar
                    avatar = basicUrl + src
                } else {
// 如果为空就获取 span 中的 span 所包裹的信息,然后把他赋值到 avatar
                    avatar = spanElement!!.getElementsByTag("span").first()!!.text()
                }

/** 获取作者头昵称 **/
                /** 获取作者头昵称 **/

                // 获取昵称所在的 Element
                val authorElement = element.getElementsByClass("contentRow-header").first()
// 取 Element 中 u-concealed 类包裹的昵称, 并赋值到 author
                val author = authorElement!!.getElementsByClass("u-concealed").first()!!.text()

/** 获取文章发布时间 **/
                /** 获取文章发布时间 **/

                // 获取发布时间所在的 Element
                val timeElement = element.getElementsByClass("contentRow-lesser").first()
// 取 Element 中 u-dt 类包裹的昵称, 并赋值到 time
                val time = timeElement!!.getElementsByClass("u-dt").first()!!.text()

/** 获取话题、浏览量和评论数 **/
                /** 获取话题、浏览量和评论数 **/

                // 获取话题、浏览量和评论数所在的 Element
                val cellElement = element.getElementsByClass("message-attribution").first()
/* 获取话题 */
                // 获取话题所在的 Element, 注意:并不是每一篇文章都有话题
                val topicElement =
cellElement!!.getElementsByClass("label label--uotan-threads").first()
// 如果有话题就赋值, 没有话题就赋值 "" 空字符串
                // 综上, 我们不能用 !! 断定非 null, 接下来我们鉴空
                // 创建一个储存话题的变量
                val topic: String = if (topicElement != null) topicElement.text() else ""
                /* 获取浏览量 */
                // 获取浏览量和评论数所在的 Element
                val otherCellElement =
cellElement.getElementsByClass("listInline listInline--bullet").first()
// 这里只能获取 li 标签, 因为浏览量的没有其他标识, 但是浏览量和评论数都是 li 标签, 所以获取到的是 "浏览量 评论数"
                val viewCCount = otherCellElement!!.getElementsByTag("li").text()
// 获取 class 为 before-display-none 的 li 标签, 这个里面包裹着评论数
                val commentCount =
otherCellElement.getElementsByClass("before-display-none").text()
// 刚刚获得了"浏览量 评论数", 和"评论数", 用 replace() 简单替换一下就可以得到浏览量
                val viewCount = viewCCount.replace(" $commentCount", "")

// 在结果中赋值
                result.add(
                    ForumRecommendItem(
 title,
 describe,
 coverUrl,
 avatar,
 author,
 time,
 topic,
 viewCount,
commentCount
                    )
                )
            }
} catch (e: IOException) {
 e.printStackTrace()
println("Error")
        }
return@withContext FetchResult(result, totalPage)
 }
}

APP DEMO:

1738742375312.jpeg
 
柚坛社区
你的玩机资源库
扫码用手机打开

-关注我们-

QQ Popup Image
WeChat Popup Image
Weibo Popup Image
Bilibili Popup Image
Douyin Popup Image
Kuaishou Popup Image