A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

绝意螺旋

初级黑马

  • 黑马币:23

  • 帖子:8

  • 精华:0

本帖最后由 绝意螺旋 于 2018-11-21 18:45 编辑

精简内容:
1.关于GoogleDrive大文件二次确认下载时无法下载重新跳转到安全扫描页面的解决:
缺少认证的cookie,确保开启cookie,手动添加cookie.
2.关于htmlunitWebClient无法添加自定义cookie的解决:确定cookie的正确性,确定cookie的有效时间没有过期.htmlunit添加cookie时对于错误和失效的cookie直接略过且无提示.
3.爬取时尝试用head请求快速获取response head,提取和分析head中有用信息进行初步筛选,一次提高爬取效率.
4.client.getOptions().setRedirectEnabled(false);可以关闭模拟客服端自动重定向功能,通过监听并拦截重定向可实现一些操作,如文件下载或筛选.
5.rclone可以挂载一些云盘为本地盘,云盘中内容可以用本地软件打开或运行.
正文:
因为还没学到,之前也没有接触过爬虫,使用HttpUnit爬取GoogleDrive的过程中踩了不少坑,这些坑和开发过程中的一些思路可以作为经验和大家分享一下.
先说下在写这个小工具的目的,是为了大批量下载自己GoogleDrive中的一些教程,因为不是压缩包,文件不少也不小,浏览器下载,谷歌的策略是下载文件夹是需要经过谷歌打包成大压缩包然后下载,文件夹总大小过大,基本都会请求超时,而且浏览器下载大文件问题也很多,GoogleDrive的桌面端也因为一些原因不能正常使用.所以选择使用IDMaria2一类进行批量下载.当然这个方法遇到的坑也是不少,比如如何批量获得下载地址,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;

进行常见一些配置,详见注释.经过一阵折腾是可以不用jscss,所以禁用掉增加爬取效率.开启忽略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内核的chromegecko内核的Firefox.有兴趣的同学可以尝试其他浏览器).
当然对于这些问题确实不好谷歌,谷歌也不知道该去写什么关键词.经过长时间的各种尝试,最终才将问题锁定到了cookie.(这里声明一下,一开始尝试爬取,肯定css,js,cookies这些都是打开的,cookie更是从来没有关掉过).在分析过程中我留意到了response head中有”set-cookie”这一段,并在比对各个情景下的浏览器cookie,发现能够正常下载的chrome中是有这一段附加的cookie.
于是在脚本工具中提取请求头中的set-cookie并手动创建对象cookie对象并添加.然后...很幸运地踩上了第二个坑[捂脸].创建的cookie对象在无错误的情况下无法添加到clientcookies.
[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文件.而且后面在文件大小的获取上是有坑的.文件大小的获取,意味着要开始下载这个文件,这是不可取的.一般是解析responsehead,获取其中Content-Length属性值,但是Content-Length并非一定存在,尤其是挂了代理后,请求和响应经过代理服务器后,原本存在且正确的Content-Length很可能会丢失.
(关于Content-Length原理和详解请谷歌,个人参考:https://blog.csdn.net/u013749540/article/details/52430791)
中也提到一个问题,在建立起下载后再读取下载地址有可能会出现一次性链接的问题.所以考虑使用监听重定向的方法,在建立起下载前拦截,并以此获取下载真实地址.详细可以看看博文中的具体实现.
对于问题2的解决,我也是采用拦截重定向的方法实现的.我并没有采用上面博主的写法,因为需要修改爬虫jar中的源码而且操作繁琐.在仔细翻阅htmlunit官方文档后,找到了一个方法
client.getOptions().setRedirectEnabled(false);
禁用自动重定向.以此拦截重定向,并从重定向的response头中直接提取重定向地址,作为下载的真实链接.上述子问题,也可以通过是否有重定向判断链接中的下载文件是否需要二次验证.至此,第二个问题也解决了.工具可以成功运行.
然后是一点小经验,head请求的使用.我们平时点击超链接是以get请求跳转链接.http请求中,除了getpost两种方式外,还有另外的方式.其中”head”请求可以仅仅只获取response head,而无需获取response body.通过提取分析response head中的信息,进行一定程度的筛选,例如比较常用的文件下载的具体大小等,而无需获取响应体,尤其在响应体非常庞大时(例如文件下载)大幅度增加爬取速度.
写得废话始终是太多了,有用的信息只有不到10%.对源码或者对谷歌云盘批量下载有兴趣的同学可以到github中看看.
其实当时设想考虑过后续填上模拟登录一类的,但目前这个项目,或者说这一套工具我都是弃坑了近半个月的[捂脸].因为换了电脑,系统也弃用了windows系统,IDM只有windows平台.目前改用更万金油的rclone管理云盘,免去许多麻烦.(当然速度和稳定性上IDM是更有优势也更方便的)

虽然rclone挺出名的,但还是顺带提一下.rclone可以将一些云盘挂在到本地当本地盘一样使用,对于像我这种有强迫症只用着120GSSD却坚决不拆光驱位装一个机械硬盘的人来说,可以把课程视频和资料等丢上云盘,然后挂载到本地用本地软件打开看(例如解密的播放器).支持GoogleDrive,OneDrive,DropBox,MEGA这些常见好用的云盘,国内的坚果云和城通网盘是支持webnav,也可以挂载.也算是看了我那么多废话的一点小补偿吧.
更多图片 小图 大图
组图打开中,请稍候......

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马