这里研究了两天,终于是找到了别人的博文中找到了答案,原因是请求并没有携带请求头,所以导致蓝奏云返回一个验证的页面,有请求头的话, 就会重定向到真实的地址。
这里,我使用了okhttp这个开源库,实现添加了请求头,最后获得了真实的下载地址(代码详情请见关键代码5),由此地址我们再调用Java中的下载即可成功下载该文件
关键功能代码及说明1、2、3这三个部分的代码都是在MainController类中的download方法中
4、5部分在MainController类中的getDownloadLink方法中
这里我只抽取关键部分来进行讲解
1.模拟用户提交表单注意,提交表单之后需要等待2s来等待js执行完毕以显示出文件列表
//这个readyNodes包含所有id为ready的div一个列表 val readyNodes = if (password.isNotBlank()) { //有密码的情况 val pwdInput = page.getElementByName<HtmlTextInput>("pwd") val button = page.getElementsById("sub")[0] as HtmlSubmitInput pwdInput.valueAttribute = password//输入提取码 val finishPage = button.click<HtmlPage>()//提交表单 webClient.waitForBackgroundJavaScript(2000)//等待2s finishPage.getElementsById("ready") } else { //无密码的情况 webClient.waitForBackgroundJavaScript(2000) page.getElementsById("ready") } 2.自动点击加载更多按钮 //文件可能不止一页,为了防止被封IP,限定最大翻页数,由用户输入 for (i in 0 until pageCount) { if (page.getElementById("filemore") != null) { page = page.getElementById("filemore").click() } else { break } } 3.解析列表获得各文件对应地址为了方便,我直接把文件名、日期等参数也一并获取了,用了一个ItemData的bean类进行数据的存储
//初始化列表(分享的蓝奏云地址中的所有文件及相关信息) val itemDatas = arrayListOf<ItemData>() //选择器进行网页的解析获取数据 for (readyNode in readyNodes) { val childNodes = readyNode.getElementsByTagName("div") val nameNode = childNodes[0].lastElementChild val sizeNode = childNodes[1] val timeNode = childNodes[2] val name = nameNode.textContent val link = nameNode.getAttribute("href")//单个文件的蓝奏云地址 val size = sizeNode.textContent val time = timeNode.textContent itemDatas.add(ItemData(name, link, "", size, time)) } 4.获得伪直链地址 //url是单个文件的蓝奏云地址 val page = webClient.getPage<HtmlPage>(url) val srcText = page.getElementsByTagName("iframe")[0].getAttribute("src") //拼接字符串,得到另外页面的url val downloadHtmlUrl = "https://www.lanzous.com$srcText" val downloadPage = webClient.getPage<HtmlPage>(downloadHtmlUrl) //等待js加载完毕 webClient.waitForBackgroundJavaScript(1000) //获得伪直链地址(a标签的href属性值) val address = downloadPage.getElementById("go").firstElementChild.getAttribute("href") 5.使用okhttp获得蓝奏云真实地址 HttpUtil.sendOkHttpRequest(address, object : Callback { override fun onFailure(p0: Call?, p1: IOException?) { println("error") } override fun onResponse(p0: Call?, response: Response?) { itemData.downloadLink = response?.request()?.url().toString() response?.close() } }) //补充的okhttp的工具类HttpUtilsendOkHttpRequest的方法 fun sendOkHttpRequest(address: String, callback: Callback) { val client = OkHttpClient() val control = CacheControl.Builder().build() //添加请求头user-agent和accept-language val request = Request.Builder() .addHeader("user-agent","Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36") .addHeader("accept-language","zh-CN,zh;q=0.9") .cacheControl(control) .url(address) .build() client.newCall(request).enqueue(callback) } 6.下载功能虽然和之前的工具一样,但是我还是把代码贴出来吧
/** * 下载文件到本地 * @param url 网址 * @param file 文件 */ private fun downloadFile(url: String, file: File) { if (!file.exists()) { val conn = URL(url).openConnection() conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 5.0; Windows NT; DigExt)") val bytes = conn.getInputStream().readBytes() file.writeBytes(bytes) } }PS:我在解析过程和下载过程中使用了多线程下载,提高了速度。具体实现思路请参考之前我的这一篇文章打造m3u8视频(流视频)下载解密合并器(kotlin)的第4部分
参考链接Java实现网页自动登录和表单自动填写提交研究
Python爬取蓝奏云直链(获取真实文件地址)