使用 Node 爬取英文听力及字幕

综合编程 2017-04-24

本文将会讲解如何使用 Node 爬取人人听力网的音频以及字幕文件。如果你想批量下载点英语听力来练习,那么这个教程对你而言将会非常实用。你可以直接从这里获取源代码:

https://github.com/lewis617/practical-js/blob/master/rrting.js

如果你是想通过本文学习 JavaScript 或 Node,那么本文丰富的知识点也将让你满载而归。阅读本文,你将学习到这些知识点:

  • 如何使用 superagent 获取页面内容
  • 如何使用 superagent 获取 JS 文件的内容
  • 如何使用 superagent 下载文件
  • 如何使用 cheerio 操作页面 DOM
  • 如何正确设置字符编码来避免乱码
  • 如何使用正则表达式去除字符串中的多余信息

使用 superagent 获取页面内容

superagent+cheerio 是 Node 爬虫的经典组合。superagent 是一个发起 Ajax 请求的工具。我们使用它来请求各种网络资源。比如,我们想批量爬取人人听力网的音频文件和字幕,我们就必须先找到音频文件和字幕的 URl,想找到 URL 就必须先获取记录这些 URL 的页面,想获取页面内容,那么首先就应该使用 superagent 把页面给请求下来。基本的请求代码如下:

request
  .get('http://example.com/search')
  .end(function(err, res){
   // 将会打印页面的 HTML 字符串
      console.log(res.text);
  });

拿到了 HTMl 字符串,就可以使用 cheerio 进行解析了。关于 cheerio 我们等会介绍~

使用 superagent 获取 JS 文件的内容

使用 superagent 获取页面内容非常简单,但是当我请求完页面后发现,音频和字幕的 URL 不在页面上,而是通过 JS 动态渲染到页面上的,我的爬虫无法和 JS 通信,这该怎么办呢?其实非常简单,既然 URL 信息在 JS 文件中,那么我们直接获取 JS 文件,并使用 eval 方法解析不就行了?所以,我们来使用 superagent 获取 JS 文件的内容。与获取页面内容的方法不同,获取 JS 文件,需要加个 .buffer(true)

request.get(mainOrigin + partData.pathname + 'mp3para.js')
    .buffer(true)
    .end(function(err, res){
      ...
    }

为何要如此?具体原因可以看这个 Issue:

https://github.com/visionmedia/superagent/issues/523

使用 superagent 下载文件

前面两节介绍了使用 superagent 获取页面和 JS 文件,那么如何使用 superagent 下载保存文件到硬盘上呢?这里需要配合使用 Node 的 fs.createWriteStream
方法,代码如下:

functiondownload(url, localPath){
    var stream = fs.createWriteStream(localPath);
    stream.on('finish', function(){
        console.log('The download of ' + localPath + ' is complete!');
    });
    request.get(url).pipe(stream);
}

非常简单,不再赘述!

使用 cheerio 操作页面 DOM

在第一节,我们介绍了使用 superagent 获取页面内容,但是页面内容太多,我们想高效提取有用信息,该如何做呢?在浏览器中,我们通常使用 jQuery 来高效操作 DOM,在 Node 爬虫中,我们可以使用 cheerio 来模拟 jQuery 的操作方法。cheerio 的基本用法如下:

onst cheerio = require('cheerio')
const $ = cheerio.load('

Hello world

') $('h2.title').text('Hello there!') $('h2').addClass('welcome') $.html() //=>

Hello there!

通过 superagent 获取 html 字符串,然后使用 cheerio 解析,页面上的内容就尽在你的掌握中了!

更多 cheerio 的用法可以参考:

https://github.com/cheeriojs/cheerio

正确设置字符编码来避免乱码

在获取页面内容,并提取里面的中文信息时,我遇到了中文乱码。这是因为页面的 chartset 设置了 gb2312
的原因,那么想要正确解析设置 gb2312
编码的页面,就必须使用这个编码来解析。值得高兴的是,superagent 为我们提供了相关的插件来实现:

https://github.com/magicdawn/superagent-charset

基本用法如下:

request.get(mainOrigin + mainPathname)
    .charset('gb2312')
    .end(function(err, res){
    ...
  })

如何知道 .charset()
中填的是什么编码呢?你只需要看看页面的这行代码即可:


使用正则表达式去除字符串中的多余信息

最后,我们来讲一下如何使用使用正则表达式去除字符串中的多余信息。在下载音频以及字幕时,可能需要处理一些中文名称作为将来的文件名或目录名。页面中的中文名称往往不是我们想要的,那么如何处理名称中的多余信息呢?使用正则表达式就可以做到!比如:

如果你想将 '听电影学英语之海上钢琴师'
变为 '海上钢琴师'
,那么你可以这么做:

// str 就是'海上钢琴师'
var str = '听电影学英语之海上钢琴师'.replace(/听电影学英语之/g, '');

如果你想将 '听电影MP3学英语之海上钢琴师'
'听电影学英语之海上钢琴师'
变为 '海上钢琴师'
,那么你可以这么做:

// str 就是'海上钢琴师'
var str = '听电影学英语之海上钢琴师'.replace(/听电影(MP3)?学英语之/g, '');

注:小括号代表子表达式,问号代表前面的字符出现 0 或 1 次。

如果你想将以下字符串:

  • '听电影学英语之海上钢琴师'
  • '听电影MP3学英语之海上钢琴师'
  • '听电影MP3学英语之海上钢琴师中英双语MP3+LRC'
  • '听电影MP3学英语之海上钢琴师 中英双语MP3+LRC'
  • '听电影MP3学英语之海上钢琴师 中英双语MP3+LRC+文本'
  • '听电影MP3学英语之海上钢琴师 中英双语MP3+LRC+文本 '

都变为 '海上钢琴师'
,那么正则表达式应该这么写:

/(听电影(MP3)?学英语之)|(s?中英双语MP3+LRC(+文本)?)|(s?$)/g

把各种情况都考虑进去,然后用 |
隔开。其中, s
匹配任何空白字符,包括空格、制表符、换页符等等。

责编内容by:Lewis617的个人博客 (源链)。感谢您的支持!

您可能感兴趣的

利用js比较同一天的时间大小【原创】 在项目开发的过程中,有时候会遇到同一天内的时间大小比较,一般来说选择时间是通过插件实现的,但并不排除客户要求手动输入的情况。 那么,在应客户要求手动输入时间...
js: dependency injection in 11 lines of code Dependency Injection in 11 Lines of Code Though vanilla javascript and late...
Cross Browser Compatibility and React JS Web Apps Cross Browser Compatibility and React JS Web Apps ...
Better Currying in Javascript (2012) Better Currying in Javascript Posted on 2012-10-01 byChris Warburton ...