元宵节前福利,神之操作,一键下载想要的同类型多个图片??

1,221 阅读5分钟

提前祝贺大家元宵节快乐!> 送一波工具福利!今天带来一波超级简单适用的工具类,XJB好玩!

scz.jpg

本文涉及到内容关联前面我的一篇文章

Kotlin+协程+Flow+Retrofit+OkHttp这么好用,不运行安装到手机可以调试接口吗?可以自己搭建一套网络请求工具

一、前言

本文涉及到内容:

  1. 简单网页网络抓取
  2. 使用:Kotlin + 协程 + Flow + Retrofit + OkHttp 搭建一套PC端网络工具
  3. 使用 org.jsoup:jsoup:1.15.3 解析网页数据
  4. 使用 com.belerweb:pinyin4j:2.5.1 使用它汉字转拼音
  5. 手撸个简单下载逻辑
  6. Kotlin , 协程 , Flow , Retrofit ,OkHttp,okio 的简单使用
  7. 自己使用的工具,不考虑性能优化

不知道大家遇到这种事情没有:我自己研究点东西,想要一些同类型素材图片,比如,我想要N多个明星的一张代表图,或者旅游景点代表图,或者比如类似的XXX图

  • 一般搞法 直接去网上单个单个搜索找到,然后单个单个另存为下载下来,这样10来个还可以,如果多了几十个上百个,得先搜索输入 上百次,点击搜索百次,然后再点击另存百次,中间还要找它个百八十次

  • 我们不一样:为什么,我们是程序员,我们天生自带超能力,这种事情都不能一键搞定,还叫什么什么程序员,难道只会写公司产品需求出的功能吗?

下面我们来看看具体怎么实现

二、找到搜索目标源

1. 这里我找到百度百科为例,我们输入 深圳 然后点击进入词条

d5cc1e35-5504-4158-96a3-edebae927fa4.jpeg 找到了我们搜索的接口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数据,

img_v3_02jd_83d92e1c-bc02-4bec-92e1-6ac321477eeg.jpg 上面箭头指向的图片是我们想要的,
找到 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()
}

四、查看执行结果:

img_v3_02jo_6813578e-3649-43d1-954e-4a2c3400d66g.jpg

想要的图片已经下载下来了

image.png

五、总结

本文简单介绍了,如何实现: 一键下载想要的同类型多个图片
你可以从中学到

  1. 简单网页网络抓取
  2. 使用:Kotlin + 协程 + Flow + Retrofit + OkHttp 搭建一套PC端网络工具
  3. 使用 org.jsoup:jsoup:1.15.3 解析网页数据
  4. 使用 com.belerweb:pinyin4j:2.5.1 使用它汉字转拼音
  5. 手撸个简单下载逻辑
  6. Kotlin , 协程 , Flow , Retrofit ,OkHttp,okio 的简单使用

提前祝贺大家元宵节快乐

感谢阅读:

欢迎 关注,点赞、收藏

这里你会学到不一样的东西