提前祝贺大家元宵节快乐!> 送一波工具福利!今天带来一波超级简单适用的工具类,XJB好玩!
本文涉及到内容关联前面我的一篇文章
Kotlin+协程+Flow+Retrofit+OkHttp这么好用,不运行安装到手机可以调试接口吗?可以自己搭建一套网络请求工具
一、前言
本文涉及到内容:
- 简单网页网络抓取
- 使用:
Kotlin + 协程 + Flow + Retrofit + OkHttp
搭建一套PC端网络工具 - 使用
org.jsoup:jsoup:1.15.3
解析网页数据 - 使用
com.belerweb:pinyin4j:2.5.1
使用它汉字转拼音 - 手撸个简单下载逻辑
Kotlin , 协程 , Flow , Retrofit ,OkHttp,okio
的简单使用- 自己使用的工具,不考虑性能优化
不知道大家遇到这种事情没有:我自己研究点东西,想要一些同类型素材图片,比如,我想要N多个明星的一张代表图,或者旅游景点代表图,或者比如类似的XXX图
-
一般搞法 直接去网上单个单个搜索找到,然后单个单个另存为下载下来,这样10来个还可以,如果多了几十个上百个,得先搜索输入 上百次,点击搜索百次,然后再点击另存百次,中间还要找它个百八十次
-
我们不一样:为什么,我们是程序员,我们天生自带超能力,这种事情都不能一键搞定,还叫什么什么程序员,难道只会写公司产品需求出的功能吗?
下面我们来看看具体怎么实现
二、找到搜索目标源
1. 这里我找到百度百科为例,我们输入 深圳
然后点击进入词条
找到了我们搜索的接口api了:
https://baike.baidu.com/lemma/api/entry?word=深圳&fromModule=lemma_search-box
这里需要注意一下:上面链接地址栏敲一下,同一个搜索多次会让你手动滑动验证码校验,但大多数情况第一次不会,出现了手动校验后又可以了
2. 使用: Kotlin + 协程 + Flow + Retrofit + OkHttp
搭建一套PC端网络工具
参考我前面文章:
Kotlin+协程+Flow+Retrofit+OkHttp这么好用,不运行安装到手机可以调试接口吗?可以自己搭建一套网络请求工具
3. 上面搭建好了之后,调用那个接口api,返回数据为html
interface BaiDuResouApi {
@GET("https://baike.baidu.com/lemma/api/entry?fromModule=lemma_search-box")
suspend fun getBaiDuBaike(@Query("word") word: String, @HeaderMap map: Map<String, String>): String
}
4. 搭建好直接调用api.getBaiDuBaike(word, map)
,请带上浏览器中的cookie,我示例中批量调用了好几个 搜索词,来进行示例
class WXBaiDuBaiKeSouSouRepository private constructor() {
private val baseUrl = "https://baike.baidu.com/"
private val api = RetrofitUtils.getInstance(baseUrl).create(BaiDuResouApi::class.java)
private val gson = Gson()
companion object {
val instance by lazy { WXBaiDuBaiKeSouSouRepository() }
}
fun getBaiDuBaike() {
getBaiDuBaike(listOf("刘亦菲", "杨幂", "周杰伦", "林俊杰", "刘德华", "郭富城", "张学友","黎明"))
}
private fun getBaiDuBaike(list: List<String>, dir: String = "D:\git_project\APTNetWork\Network-API\asstes\download\") {
runBlocking {
val cookies ="请把cookie找到放在这里"
val map = mutableMapOf<String, String>()
map["Cookie"] = cookies
flow {
list.forEach { word ->
try {
val html = api.getBaiDuBaike(word,map)
val document = Jsoup.parse(html, baseUrl)
document.select("script")?.forEach {
var str = it.html()
if (str.contains("window.PAGE_DATA")) {
try {
str = str.replace("window.PAGE_DATA= ", "")
val data = gson.fromJson(str, BaiDuBaIkEData::class.java)
val key = ChineseCharacterUtil.getLowerCase(word, true, true)
File(dir)?.takeUnless { it.exists() }?.mkdirs()
val sb = StringBuilder(dir).append(key).append(".webp")
val file = File(sb.toString())
file.takeIf { it.exists() }?.delete()
val startTime = System.currentTimeMillis()
DownLoadUtils.instance.download(data.abstractAlbum.coverPic.url, file)
val endTime = System.currentTimeMillis()
println("${word}下载耗时: ${(endTime - startTime)}ms")
} catch (e: Exception) {
println("错误: $word")
}
return@forEach
}
}
} catch (e: Exception) {
println("错误: $word")
}
//休息2s防止出现验证码
delay(2000)
}
emit(0)
}.flowOn(Dispatchers.IO).catch {
println(it.parseErrorString())
}.collect()
delay(10000)
}
}
}
5. 返回的是html数据使用 Jsoup来解析,找到里面script
返回json数据,
上面箭头指向的图片是我们想要的,
找到 json里面的 abstractAlbum.coverPic.url
,即是我们想要的图片地址,这里面也有其他值,如果想要自己来找,我们把下载后的文件名按照搜索的 汉字 转化为拼音 (使用pinyin4j)
需要注意的是:下载出现错误的,需要打印出来,防止下载漏掉了,或者,被百度判为需要需要手动滑动验证码
6. 然后简单撸个下载图片方法,设置下载到PC目录为,当然这个目录完全可以自定义 ,使用都按了Okio读写文件
"D:\git_project\APTNetWork\Network-API\asstes\download\"
class DownLoadUtils {
companion object {
val instance by lazy { DownLoadUtils() }
private var mSSLSocketFactory: SSLSocketFactory? = null
/**
* 信任所有host
*/
private val hnv = HostnameVerifier { _, _ -> true }
/**
* 设置https
*
* @author :Atar
* @createTime:2015-9-17下午4:57:39
* @version:1.0.0
* @modifyTime:
* @modifyAuthor:
* @description:
*/
private fun trustAllHosts() {
try {
val trustAllCerts = arrayOf<TrustManager>(object : X509TrustManager {
override fun getAcceptedIssuers(): Array<X509Certificate> {
return arrayOf()
}
@Throws(CertificateException::class)
override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {
}
@Throws(CertificateException::class)
override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {
}
})
val sc = SSLContext.getInstance("TLS")
sc.init(null, trustAllCerts, SecureRandom())
if (mSSLSocketFactory == null) {
mSSLSocketFactory = sc.socketFactory
}
HttpsURLConnection.setDefaultHostnameVerifier(hnv)
HttpsURLConnection.setDefaultSSLSocketFactory(mSSLSocketFactory)
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun getHttpURLConnection(url: URL, connectTimeOut: Int): HttpURLConnection? {
return try {
if ("https" == url.protocol) {
trustAllHosts()
(url.openConnection() as HttpsURLConnection).apply {
HttpsURLConnection.setDefaultHostnameVerifier(hnv)
hostnameVerifier = hnv
HttpsURLConnection.setDefaultSSLSocketFactory(mSSLSocketFactory)
sslSocketFactory = mSSLSocketFactory
connectTimeout = 3 * connectTimeOut
readTimeout = 3 * connectTimeOut
}
} else {
(url.openConnection() as HttpURLConnection).apply {
connectTimeout = connectTimeOut
readTimeout = connectTimeOut
}
}
} catch (e: Exception) {
null
}
}
fun setConHead(httpConnection: HttpURLConnection) {
httpConnection.apply {
setRequestProperty("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8")
setRequestProperty("Upgrade-Insecure-Requests", "1")
setRequestProperty("User-Agent", "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Mobile Safari/537.36")
setRequestProperty("Accept-Language", "en-us,en;q=0.7,zh-cn;q=0.3")
// httpConnection.setRequestProperty("Accept-Encoding", "gzip, deflate, br")
setRequestProperty("Accept-Charset", "ISO-8859-1,utf-8;q=0.7,*;q=0.7")
setRequestProperty("Keep-Alive", "300")
setRequestProperty("Connection", "keep-alive")
setRequestProperty("Cache-Control", "max-age=0")
}
}
}
fun download(downUrl: String, file: File) {
var conn: HttpURLConnection? = null
try {
val url = URL(downUrl)
conn = getHttpURLConnection(url, 5000)!!
val inputStream = conn.inputStream
if (inputStream != null) {
val sinkBuffer = file.sink().buffer()
val bufferedSource = inputStream.source().buffer()
sinkBuffer.write(bufferedSource.readByteArray())
sinkBuffer.close()
bufferedSource.close()
inputStream.close()
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
conn?.disconnect()
}
}
}
三、运行main方法调用getBaiDuBaike():然后就批量下载我们想要的图片了,
fun main() {
WXBaiDuBaiKeSouSouRepository.instance.getBaiDuBaike()
}
四、查看执行结果:
想要的图片已经下载下来了
五、总结
本文简单介绍了,如何实现: 一键下载想要的同类型多个图片
你可以从中学到
- 简单网页网络抓取
- 使用:
Kotlin + 协程 + Flow + Retrofit + OkHttp
搭建一套PC端网络工具 - 使用
org.jsoup:jsoup:1.15.3
解析网页数据 - 使用
com.belerweb:pinyin4j:2.5.1
使用它汉字转拼音 - 手撸个简单下载逻辑
Kotlin , 协程 , Flow , Retrofit ,OkHttp,okio
的简单使用