首页
版块
资源库
社区
工具箱
WiKi
搜索
仅搜索标题
用户:
仅搜索标题
用户:
登录
搜索
仅搜索标题
用户:
仅搜索标题
用户:
菜单
安装应用
安装
回复主题
首页
版块
系统软件
Android
我就是来瞎写写,顺便写一下打算怎么实现 UOTAN 安卓端(这是一个很长的标题,别问为什么,因为设计规范里要求超过两行用省略号省略显示啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊)
禁用JavaScript。为了获得更好的体验,请在运行之前启用浏览器中的JavaScript。
您正在使用一款已经过时的浏览器!部分功能不能正常使用。
请使用Chrome或其他浏览器。
信息
[QUOTE="汩汩加热装置, post: 87027, member: 3059"] 我突发奇想想要开发一个 柚坛社区 [B]安卓[/B] 客户端(爬取的时候看看这段怎么加粗的)。 [ATTACH type="full"]4257[/ATTACH] 所以这边文章就是[COLOR=red] 水一水代码 [/COLOR] ,顺便来 [COLOR=#0000cc]爬点东西[/COLOR].....(爬取的时候看颜色放到哪里了,red是什么值,RGB的话还得转) 不是,怎么还能自定义 [FONT=Courier New] font [/FONT] 哇。(这一段是自定义字体) [I] Kotlin [/I] 很好用,[U] Jsoup [/U] 也是。[S] 有 API 就更好了 [/S]。(这一段是斜体、下划线和中划线) 接下来我要说一说这个新项目的 [SIZE=1]实现逻辑[/SIZE] 和 [SIZE=7]难点[/SIZE]。(字号,到时候看看吧,这应该是px还得转sp) # 或许也支持 markdown?(看看有没有变为一级标题) emmm,这是这个项目的开源地址(看一下超链接的包裹、类或标签和普通文本一样吗) [URL]https://github.com/Uotan-Dev/UOTAN-for-Android[/URL] [URL=https://github.com/Uotan-Dev/UOTAN-for-Android]本项目开源地址[/URL] (文本超链接) 发现 bug 你可以发 issues 或者联系我(邮箱超链接): [EMAIL]mzz@zzhi.onmicrosoft.com[/EMAIL] [EMAIL=mzz@zzhi.onmicrosoft.com]点此反馈[/EMAIL] 这是我的 UOTAN 账号:[USER=3059]汩汩加热装置[/USER](主要是看发布后html把这个usernumber塞到哪里了) 这是一个 UOTAN logo [IMG]https://www.uotan.cn/data/assets/logo/12x.png[/IMG](,,,,,应该会是img标签吧) 就先爬这些,接下来是我的一部分实现方法,因为接口很少,所以主要用爬虫,第一次用 Jsoup,希望有大佬指点 [CODE] // 伴生对象 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) } } [/CODE] APP DEMO: [ATTACH type="full"]4259[/ATTACH] [/QUOTE]
发送
+
文明上网,理性发言