用Scala打造精悍爬虫(二)视频篇

【项目简述】

抓取某学院视频网站的系列课程,相比于上一篇,这一次多了不少实用性。

 

【前提】

必须要有该网站的VIP账户,主要是Cookies要使用,否则无法获取一部分受限视频。当然有不少免费获取VIP账户的方法可以自行搜索。

 

【视频网站的特点】

1)这类网站一般都有不同程度的反扒措施,某学院采用的是动态缓存,即是有权限的会员打开网页才能获取该视频的Url,这个Url有一定时效性,不适合大量抓取Url后再下载(等全抓完前面的已失效了)。但是边抓取Url边下载是没有问题的,这是较容易爬取的一类。网页的分析和以前并无分别,用F12搞定。

2)由于目标文件数量多,文件的组织比较重要,url中的文件名并不适合于文件保存(人类无法理解。。。)。这里采用最符合阅读习惯的方式对于不同层级建目录,文件名直接采用页面标题命名。另外网页上有些层级的内容未带序号,为避免下载以后排序混乱的问题,需要在前面追加序号。

3)由于目录和文件名来自网页,就会有相对于操作系统的非法字符问题,比如windows下 \/:*?"<>| 这些字符是不能出现在目录文件中的。另外还有不少其他非法文字。

4)视频文件爬取比较耗时,不要期望一次性成功,所以必须要有重复检测,即遇到已爬取的文件直接跳过。

 

先上核心代码

for( i <- 1 to 5;
     e1 <- fCrawl(url.format(i)).select("div.lesson-card");
       path1 = e1.select("h2").text.stripIllegalChar;
     (e2,j)  <- fCrawl(e1.select("div.text a").attr("href")).select("div.lesson-item").zipWithIndex;
       path2 = j + 1 + "_" +e2.select("dt.title").text.stripIllegalChar;
     e3 <- fCrawl(e2.select("a").attr("href")).select("dl.lessonvideo-list a")){
  val file = e3.text.stripIllegalChar+".mp4"
  val path = BASE_PATH + "/" + path1 + "/" + path2
  if (!(new java.io.File(path+"/"+file)).exists){
    fDownload(path, file, fCrawl(e3.attr("href")).select("source").attr("src"))
    println(s"$path\t$file\t下载成功!")
  }else{
    println(s"$path\t$file\t已下载!")
  }
}

 

是的,你没有看错,得益于Scala强大的表现力,核心代码就只有这10几行。当然还没有把一些辅助型代码算在内。

 

说明:

  • 首先是Scala的for语句实际上是一个可用于多层次迭代的语法糖,上面的语句实际上产生了四层迭代。第一层 1 to 5 是对分页的迭代,后面每一层进入一次页面抓取,最后循环体内的fcrawl就可以抓取到视频Url了。类似如下结构:

(1 to 5)

  .foreach(i => fCrawl(url.format(i)).select("div.lesson-card") )

    .foreach( e1 => fCrawl(e1.select("div.text a").attr("href")).select("div.lesson-item").zipWithIndex)

      .foreach( (e2,j) => fCrawl(e2.select("a").attr("href")).select("dl.lessonvideo-list a")))))

 

  • e2级中,zipWithIndex这个方法是用来给迭代对象添加序号的,由于Scala的序号从0开始,所以使用的时候还需要加1。
  • 熟悉Scala的朋友一定会问,stripIllegalChar是什么鬼,居然用在了String类型上。这个其实是Scala的黑科技之一隐式类,很多时候,它能让代码更加优雅。

真相在这里

implicit class StringImprovement(val s : String){
    // 删除文件名中的非法字符
  def stripIllegalChar = s.replaceAll("???", "").replaceAll("\\\\|/|:|\\*|\\?|<|>|\\||\"", "")
}

 

现在String类型的对象就可以使用stripIllegalChar方法去删除那些非法字符了。???是特指一些无法显示的非法字符,这个只能遇到的时候,现去添加,由于该程序有重复文件判断,所以每次异常修改后,直接再次执行就好了。

 

fCrawl,fDownload这两个方法就是简单递归自身保证Url抓取和文件下载的成功。

def fCrawl(url: String): Document = 
  Try(Jsoup.connect(url).timeout(0).cookies(cookies).get()) match {
    case Failure(e) => println(e.getMessage); sleep(10000); fCrawl(url)
    case Success(d) => d
  }

def fDownload(path: String, file: String, url: String): Unit =
  Try(JavaHelper.download(path, file, url)) match {
    case Failure(e) => println(e.getMessage); sleep(10000); JavaHelper.download(path, file, url)
    case Success(_) =>
  }

 

异常的时候三步:异常信息打印,暂停10秒,再次执行。

 

 

最后是JavaHelper.download方法,由于jsoup下载文件的能力较差,自己写了一个java方法如下(实际运行下来发现还蛮健壮,极少出错):

public static void download(String path, String fileName, String fileUrl) throws IOException {
    File pathFile = new File(path); 
    if (!pathFile.exists()) {
        pathFile.mkdirs();
    }
      
    URL url = new URL(fileUrl);
    InputStream inStream = url.openConnection().getInputStream();
    FileOutputStream fs = new FileOutputStream(path + "/" + fileName);

    byte[] buffer = new byte[1204];
    int byteread = 0;
    while ((byteread = inStream.read(buffer)) != -1) {
        fs.write(buffer, 0, byteread);
    }
    fs.close();
}

 

其他代码

// Url源
val url = "http://ke.jikexueyuan.com/xilie/?page=%d"
// 存储路径
val BASE_PATH = "G:/video/Series"

def sleep(i: Long) = Thread.sleep(i)
// 这里设置你的vip账户的cookies信息,用F12你应该懂的
val cookies = new java.util.HashMap[String,String]
cookies.put("uname", "???") 
cookies.put("authcode", "???") 

 

经过累计时间差不多1天的半值守运行,成功下载了带有良好目录结构的3000+视频文件。

 

小结: Scala的很多特性(如类型推断,隐式转换,众多语法糖等等),让它变成一门极高“信噪比”的语言。显然,更少的代码,会让维护都变得更容易。但同时,灵活掌握这些特性也有一定难度,我想这也许是它始终无法普及的原因吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值