不知道大家在日常工作和学习生活中有没有遇到过这样一个场景,就是需要将某些网站上面的数据(比如说图片、文字或者是其他某些格式的数据)复制下来,保存到本地Excel表、本地数据库或者是将其下载下来(如果是图片、音频或视频)。
看到这样的场景,我想大多数人第一反应就是爬虫。爬虫,顾名思义就是利用写好的一个程序,像一个虫子一样在蜘蛛网一样的网站上去爬取我们的想要的数据。
![0d03de6ce74d57b6a742aa9150dde369.png](https://i-blog.csdnimg.cn/blog_migrate/cbab1dc64dec7407c855d93afe0827e3.jpeg)
根据爬虫的自动爬取能力,笔者经常将爬虫分为以下三个等级:
1、手动:完全依靠手动复制粘贴;
2、半自动:需要针对不同地方手动为程序设置相关参数,一次只能爬取某个部分的数据;
3、全自动:程序一旦跑起来,知道程序爬取完成,整个过程不需人为干涉。
当然,第一种其实算不上真正意义上的爬虫;第二种,需要人为的辅助,但是开发起来比较简单;第三种,他的爬取过程是非常的智能化,但是写这一个爬虫需要有相当的功底。
第二种与第三种,孰优孰劣其实没有定数,根据不同的场合,两种都有各自适应的地方。比如第二种,如果要爬取的数据非常单一、简单,那么根本就没有必要写一个相当完备的程序,可以直接写一个非常简单的,然后手动的设置一下即可;但是如果这个程序需要爬取的是大范围的数据,这时候如果你对每一个关键点都需要人为去设置的话,那将会非常的恐怖和耗时。
![d51d7184667070c397f458c38558ad19.png](https://i-blog.csdnimg.cn/blog_migrate/1bb67f70dc96a6075b1f0ba33019611e.jpeg)
好了,说了这么多,现在我们来看一下这个案例,笔者这几天在找一个关于PR软件的视频教程,通过搜索引擎找到了一个视频教程的网站。
![64ec7eab8d1a81589b7b44450a5a38a6.png](https://i-blog.csdnimg.cn/blog_migrate/4c816434539e979214e913e56768ee17.jpeg)
这套教程分为很多章,每一章又分为很多小节,每一个小节对应一个视频。笔者的想法是将每一个小节的名称(如:1.1 Premiere 2017的下载与安装)以及视频地址提取出来。
名称比较好说,表格中最左侧一列就是小节名称。通过查看网页源代码,可以看出源代码中table标签就有小节名称的存在,可是问题的关键是如何把这个table标签下的a标签里面的小节名称给提取出来,因为这里是网页源代码,无法简单地通过js发送HTTP请求获取源代码进行解析(因为存在跨域的问题)。
![bb2c6fb6c8bde8ec2d4db8b6eb620534.png](https://i-blog.csdnimg.cn/blog_migrate/0f003733741b550b613b1374224936ce.jpeg)
这可怎么办呢?
不急,只要思想不滑坡,办法总比问题多。是的,此时笔者想到了浏览器也有一个Console可以运行js代码,而且更好的是在这里可以操作在审查元素里看到的那些元素。于是便可以使用下面的一句代码得到所有的包含小节名称的a标签。
document.querySelectorAll('.news_list table div[align="left"] a');
![ade5552bce0b4c549c9773153aa9ce92.png](https://i-blog.csdnimg.cn/blog_migrate/55864c315432008bb9b25d99ebba83ac.jpeg)
但是,这里所获取来的A标签,它包含章标题所在的那一个A标签,而笔者只需要小节标签。 通过比较章标题和小节标题,可以发现小节标题都有一个固定格式的开头,而章标题没有。这个固定格式的开头是“数字.数字.数字”,可以表示为下面的正则表达式:
^d+.d+(.d+)?
于是便可以使用该正则表达式的条件进行过滤,代码如下:
var regex = /^d+.d+(.d+)?/;var aTags = document.querySelectorAll('.news_list table div[align="left"] a');var list = [];aTags.forEach(function(a) { var text = a.innerText; if (regex.test(text)) { list.push({title: a.innerText}); }});
![bc78026dc6d80be0fa13f7f533e82bfb.png](https://i-blog.csdnimg.cn/blog_migrate/df2e22e661bbf817368f1f62f4f1d405.jpeg)
至此,我们便把小节标题提取出来了!接下来我们便需要来提取视频的地址了。首先,我们点开某一个小节,可以看到,页面中有一个视频在播放:
![391707720c1443b887dfeba795974ccd.png](https://i-blog.csdnimg.cn/blog_migrate/6556a00208546cd441a49ae049d7e840.jpeg)
通过审查元素,可以看到有一个Video标签,该标签中的SRC属性刚好给出了视频地址。但是笔者不可能通过点击每一个小节来查看Video标签并提取视频地址。此时笔者通过对比不同的小节发现了视频地址具有一个规律:
![83857d2d73c5e409d0fff0114937d0ef.png](https://i-blog.csdnimg.cn/blog_migrate/0c30896c0135e89552ab524e40fa2170.jpeg)
所有的视频地址只有{1}这里是不一样的,而{1}这里与小节编号又有关联:{1}的部分是由小节编号将句点符号转为了横杠,只需要将标题编号提取出来然后替换一下就可以了。
![011423f82a3f4884ed69631e178f24c0.png](https://i-blog.csdnimg.cn/blog_migrate/289b3361327b55584133d261bdd273d5.jpeg)
到此为止,可以完整的写出所有的代码了,根据该代码便可以实现所需的抓取任务。
var urlTemplate = 'http://www1.51shiping.com/pr2017/mp9/{1}.mp4';var regex = /^d+.d+(.d+)?/;var aTags = document.querySelectorAll('.news_list table div[align="left"] a');var list = [];aTags.forEach(function(a) { var text = a.innerText; if (regex.test(text)) { var no = regex.exec(text)[0].replace(/./g, '-'); var url = urlTemplate.replace('{1}', no); list.push({title: a.innerText, url: url}); }});console.log(JSON.stringify(list));
![34f9d8573973a1c0f8505134d54aa45e.png](https://i-blog.csdnimg.cn/blog_migrate/12b3d798b8cea7ee48a8f4eb9cab18e9.jpeg)
可以看出,这个需求并不是很难,所以我们也并不需要写一个非常完整或者说功能强大的爬虫,只需要写十来行的JS代码,便可以实现功能。这样做的好处就是方便快捷,但是扩展性非常差。所以我们在日常的开发和工作中,要懂得权衡。
如果你刚好也碰到和我相同的问题,那么这个案例将会给你一个启发,如果启发有用,请关注并为我点赞。如果您是一位大神,也请在下方评论区写出您的好想法。