本帖最后由 绝意螺旋 于 2018-11-21 18:45 编辑
精简内容: 1.关于GoogleDrive大文件二次确认下载时无法下载重新跳转到安全扫描页面的解决: 缺少认证的cookie,确保开启cookie,手动添加cookie. 2.关于htmlunit中WebClient无法添加自定义cookie的解决:确定cookie的正确性,确定cookie的有效时间没有过期.htmlunit添加cookie时对于错误和失效的cookie直接略过且无提示. 3.爬取时尝试用head请求快速获取response head,提取和分析head中有用信息进行初步筛选,一次提高爬取效率. 4.client.getOptions().setRedirectEnabled(false);可以关闭模拟客服端自动重定向功能,通过监听并拦截重定向可实现一些操作,如文件下载或筛选. 5.rclone可以挂载一些云盘为本地盘,云盘中内容可以用本地软件打开或运行. 因为还没学到,之前也没有接触过爬虫,使用HttpUnit爬取GoogleDrive的过程中踩了不少坑,这些坑和开发过程中的一些思路可以作为经验和大家分享一下. 先说下在写这个小工具的目的,是为了大批量下载自己GoogleDrive中的一些教程,因为不是压缩包,文件不少也不小,浏览器下载,谷歌的策略是下载文件夹是需要经过谷歌打包成大压缩包然后下载,文件夹总大小过大,基本都会请求超时,而且浏览器下载大文件问题也很多,GoogleDrive的桌面端也因为一些原因不能正常使用.所以选择使用IDM或aria2一类进行批量下载.当然这个方法遇到的坑也是不少,比如如何批量获得下载地址,IDM下载时如何按文件夹本身的结构批量创建下载任务等. 其实有点不是太好解释,但是没用的话就不多说了.简而言之,微观上来讲,就是实现批量转换地址.对于已知的一个谷歌云标准地址 https://drive.google.com/uc?id=XXX&export=download 的下载地址入口转换成 的真实下载地址,一个能直接导入下载器直接下载的地址. 期间,按照GoogleDrive的策略,小于约200MB的文件,可以直接通过第一个地址重定向可直接下载,而大于200MB的文件,需要用户手动确认下载大文件才能进行下载. 为了实现批量下载功能,需要实现自动处理第二种情况. 需求很简单,实现方式也很明朗,用爬虫工具可以很简单地解决.但对第一次接触爬虫的我来讲,踩到了不少坑,要独立去探索和解决,也花了不短时间才实现. 我选用的是HtmlUnit这个爬虫包.优势一类的Google一下很多介绍,我也就不贴了. 首先是创建一个模拟浏览器客服的——WebClient. [Java] 纯文本查看 复制代码 WebClient client=new WebClient(BrowserVersion.getDefault());[/font][/align] //关闭js
client.getOptions().setJavaScriptEnabled(false);
client.setJavaScriptTimeout(5000);
//忽略ssl认证
client.getOptions().setUseInsecureSSL(true);
//禁用Css,可避免自动二次请求CSS进行渲染
client.getOptions().setCssEnabled(false);
//运行错误时,不抛出异常
client.getOptions().setThrowExceptionOnScriptError(false);
//设置超时时间
client.getOptions().setTimeout(60000);
// 设置Ajax异步
client.setAjaxController(new NicelyResynchronizingAjaxController());
//开启cookie
client.getCookieManager().setCookiesEnabled(true);
Set<Cookie> cookies=client.getCookieManager().getCookies();
//根据配置设置是否使用代理
client=getProxy(client);
return client;
进行常见一些配置,详见注释.经过一阵折腾是可以不用js和css的,所以禁用掉增加爬取效率.开启忽略ssl可以绕过许多拦截,尤其在要使用代理的情况下.用到登录的话都是需要开启cookie的,这里虽然不模拟登录,但是后面下载是需要用到cookie.设置代理抽取出来了,采用文件配置式所以有多事IO操作代码,核心的只是 [Java] 纯文本查看 复制代码 ProxyConfig proxyConfig = client.getOptions().getProxyConfig();
proxyConfig.setProxyHost(proxyHost);
proxyConfig.setProxyPort(proxyPort); 接下来就可以开始爬取了.爬取过程需要解决的两个问题: ①如何解决大文件的验证问题. ②如何获得并记录真实下载链接. 第一个问题用爬虫很好解决,模仿浏览器,访问这个路径,并且按一下页面里的”确定下载”按钮.而且分析这个页面发现,这个按钮是一个a标签,也不需要js,是属于比较好爬的.实际最后实现时也是直接获得a标签的href属性值然后拼上域名实现的. 但是,有坑.而且为了填这个坑花费了很长时间 这个地址是get请求,传入这个地址get请求,传入参数有”confirm=四位验证码”,”id=文件id值”,是一个可以作为确认验证的链接,验证码是访问这个页面时才生成的.但是即使用浏览器打开,偶尔会出现一个奇怪的现象:点击”仍要下载”后会重新跳转到这个页面,并且会一直循环下去.如果用chrome,那么大概率能正常下载,但是用Firefox,我没有成功过,当然,也包括用爬虫包爬取的时候.(因为我本身只装了两个浏览器,webkit内核的chrome和gecko内核的Firefox.有兴趣的同学可以尝试其他浏览器). 当然对于这些问题确实不好谷歌,谷歌也不知道该去写什么关键词.经过长时间的各种尝试,最终才将问题锁定到了cookie上.(这里声明一下,一开始尝试爬取,肯定css,js,cookies这些都是打开的,cookie更是从来没有关掉过).在分析过程中我留意到了response head中有”set-cookie”这一段,并在比对各个情景下的浏览器cookie后,发现能够正常下载的chrome中是有这一段附加的cookie的. 于是在脚本工具中提取请求头中的set-cookie并手动创建对象cookie对象并添加.然后...很幸运地踩上了第二个坑[捂脸].创建的cookie对象在无错误的情况下无法添加到client的cookies中. [Java] 纯文本查看 复制代码 String[] split = page.getWebResponse().getResponseHeaderValue("Set-Cookie").split("; ");//获得response head里的Set-Cookie
String[] content=split[0].split("=");//第一个是name=value的键值对,以'='分割开来
String _name=content[0];//获得cookie的name
String _value=content[1];//获得cookie的value
String _domain=split[1].substring(7);//获得cookie的domain
String _Expires=split[2].substring(8);//获得cookie的有效时间文本
Date _Expires_date=new Date(_Expires);//根据文本转成Date格式
String _path=split[3].substring(5);//获得cookie的path
//根据获得的参数创建cookie
Cookie cookie = new Cookie(_domain, _name, _value, _path, _Expires_date, true, true);
//添加到client的cookies中
client.getCookieManager().addCookie(cookie); 原因是set-cookie中的cookie有效时间是过期的,这里个人猜测不同浏览器中,缺失cookie不能正常下载的原因也是因为响应头的set-cookie指令里cookie是已经失效的.失效原因个人认为是因为代理或服务器时间差,导致这个本就不长的有效期传回来时已经失效.(而htmlunit在添加一个错误或失效的cookie时没有任何提示,导致我在这个坑了呆了不短时间[捂脸]) _Expires_date.setYear(_Expires_date.getYear()+1); 简单粗暴的形式将这个cookie延长,至此,第一个问题解决. 第二个问题在解决上其实还涉及第一个中链接内容判断的子问题,如何判断下载链接是否需要二次认证.在我所使用的谷歌云盘支持的第三方应用”Download Link Generator For Google Drive”的作者在github中提供了一份C#代码的参考方案.(下面说的不一定对,毕竟真的对C#真的不熟悉) 大意是将链接均视为文件进行下载,通过判断文件大小判断.如果需要二次认证,下载文件必然是html文件,并且文件很小,则认为需要二次认证.个人当时并没有采用,因为云盘中也可能有html文件.而且后面在文件大小的获取上是有坑的.文件大小的获取,意味着要开始”下载”这个文件,这是不可取的.一般是解析response的head,获取其中Content-Length属性值,但是Content-Length并非一定存在,尤其是挂了代理后,请求和响应经过代理服务器后,原本存在且正确的Content-Length很可能会丢失. 中也提到一个问题,在建立起下载后再读取下载地址有可能会出现”一次性”链接的问题.所以考虑使用监听重定向的方法,在建立起下载前拦截,并以此获取下载真实地址.详细可以看看博文中的具体实现. 对于问题2的解决,我也是采用拦截重定向的方法实现的.我并没有采用上面博主的写法,因为需要修改爬虫jar中的源码而且操作繁琐.在仔细翻阅htmlunit官方文档后,找到了一个方法 client.getOptions().setRedirectEnabled(false); 禁用自动重定向.以此拦截重定向,并从重定向的response头中直接提取重定向地址,作为下载的真实链接.上述子问题,也可以通过是否有重定向判断链接中的下载文件是否需要二次验证.至此,第二个问题也解决了.工具可以成功运行. 然后是一点小经验,head请求的使用.我们平时点击超链接是以get请求跳转链接.在http请求中,除了get和post两种方式外,还有另外的方式.其中”head”请求可以仅仅只获取response head,而无需获取response body.通过提取分析response head中的信息,进行一定程度的筛选,例如比较常用的文件下载的具体大小等,而无需获取响应体,尤其在响应体非常庞大时(例如文件下载)大幅度增加爬取速度. 写得废话始终是太多了,有用的信息只有不到10%吧.对源码或者对谷歌云盘批量下载有兴趣的同学可以到github中看看. 其实当时设想考虑过后续填上模拟登录一类的,但目前这个项目,或者说这一套工具我都是弃坑了近半个月的[捂脸].因为换了电脑,系统也弃用了windows系统,而IDM只有windows平台.目前改用更万金油的rclone管理云盘,免去许多麻烦.(当然速度和稳定性上IDM是更有优势也更方便的)
虽然rclone挺出名的,但还是顺带提一下.rclone可以将一些云盘挂在到本地当本地盘一样使用,对于像我这种有强迫症只用着120G的SSD却坚决不拆光驱位装一个机械硬盘的人来说,可以把课程视频和资料等丢上云盘,然后挂载到本地用本地软件打开看(例如解密的播放器).支持GoogleDrive,OneDrive,DropBox,MEGA这些常见好用的云盘,国内的坚果云和城通网盘是支持webnav,也可以挂载.也算是看了我那么多废话的一点小补偿吧. |