使用Node.js作为后台进行爬虫

看了一遍又一遍Node.js但是没过多久就又忘了,总想找点东西来练练手,就发现B站首页搜索框旁边的GIF图特别有意思,想着是不是可以写一个小Node.js项目把这些图全部扒下来,于是带着复习、预习与探索的姿态就开始吧~

步骤记录

爬取目标

B站首页右上角搜索框下面的GIF图片

初步流程图

初步流程图

一开始的想法很单纯,既然 每次刷新首页都会随机得到一张GIF动图,这些动图的url地址都没有规律可循,但我可以不断去请求首页URL并将目标图片的地址和标题扒下来,再次请求图片存在本地就好了,再考虑上 随机次数 和 判断重复 ,理论上只要请求次数足够多,总能把所有图片搞到手
但很快就发现了很多 局限性

初步问题

图片资源一般不会在页面响应时就获取到,一般图片资源较大,都会采用异步加载手段来优化页面,也就是说并不是一次首页请求就能弄到图片地址的,因为 图片地址都在js脚本里面
那么 去js文件当中找到动态添加图片元素的代码 ,不就能知道图片的地址了吗?于是我将页面上所有js代码都查询了个遍,终于在一个叫base.core.js里面找到了存放图片元素的容器标记(id=random_p,还好这个容器不是动态生成的…)
但是有了新的问题,又是页面优化, js代码是经过压缩过的 (去掉不必要的换行空格以及注释、变量名缩短等),所以即使我用自动排版插件将js文件重新排版,我仍然不知道这些abcdefg变量都是些啥
于是我尝试换一种思路,浏览器对用户来说,也仅是发送了url这一个请求,但是浏览器会自动执行js代码、进行CSS样式渲染,那么我们如果模拟浏览器的实现,就可以得到一个”包含所有图片的最终页面”了,于是我又翻到PhantomJS、scrapy-splash、NW.js(node-webkit)、crawer等一系列的解决方案,但觉得和我的初衷有出入,也就放弃了
打了把游戏回来,仔细想了下,既然图片地址没有规律可循,那么B站又怎么确定精确地址的呢?于是我又扎入了非人类可阅读的base.core.js里面了…终于,我找到了一个叫index-icon.json的json文件,用浏览器打开一看,这不就是我要找的图片地址和图片标题吗?!
后期流程图

后期流程图

所以说根本用不上cheerio模块…

最后的问题

如果直接获取JSON文件,得到的数据都是乱码,结果发现,又!是!页!面!优!化!连JSON这么精简的文件都用 gzip压缩 了,但如果去设置request的参数encoding: gzip会报错,因为gzip压缩编码并不属于常规编码。一开始以为需要用到Node.js自带的zlib模块去解码,但仔细研究后发现request可以设置参数gzip: true就能对数据进行gzip解码了,不愧是家喻户晓的模块,这都考虑了…

总结

这次自发小项目最后其实只有20行不到的代码,但感觉自己学到的不仅仅是这一小段代码,从 js模块化的三个规范 、 Node.js和npm的复习预习与实践 、 各种模块的使用 到 简单的爬虫思想与抓取动态数据 、 页面优化与异步请求对爬虫(SEO)的直接影响 、 gzip原来是一种编码而不仅仅只是打包 等,真的收获不小
一个简单的爬虫程序,给我最大的感悟就是, 不同的爬虫需求需要从不同的角度出发 ,对症下药才有用,比如我如果只是获取首页导航栏分类或者要获取到每个模块下的视频预览图片与相关信息,肯定又需要从另一个角度去分析这个网页了

项目结构与源码

crawler_bilibili
|--index.js // 主入口程序
|--package.json // 项目信息
|--gif // 存放爬取到的图片
|--node_modules // 额外包
|--request // request模块
1
2
3
4
5
6
source

Node.js简介与常见用法记录

个人看法,Node.js就是使用javascript语言来搭建后台(充当服务器,处理请求以及响应请求)
Node.js遵循CommonJS规范

关于CommonJS、AMD、CMD规范

这三个规范都是针对 javascript模块化 加载而制定的,js并不是一个模块化的语言,这种语言不支持类(class)的概念,而现今的开发对js的需求越来越复杂,迫切的希望js的可以模块化

理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块
ES6中,将正式支持”类”和”模块”,但还需要很长时间才能投入实用
CommonJS

一个文件就是一个单独的模块
使用require方法加载模块
使用exports或module.exports定义模块接口(前者就如obj.prototype.sayName = ...而后者就如obj.prototype = function(){...})
同步加载模块(会产生阻塞,适用于服务器端)
AMD规范

一个文件就是一个单独的模块
由requireJS推广并产出
模块定义仅一个接口define([id],[deps],factory(deps))(其中id是模块名,可省;deps是依赖其他模块的一个数组,并自动成为后面factory函数的参数,可省;factory是模块具体内容,必须)
异步加载模块(适用于浏览器端)
需要提前写好依赖模块
CMD规范

一个文件就是一个单独的模块
由seaJS定义推广并产出(淘宝的玉伯大牛弄出来的)
通过define定义模块,通过require引入依赖模块,通过exports或exports提供接口
异步加载模块(适用于浏览器端)
在使用过程中引入依赖,不用提前写
个人小结

CommonJS、AMD、CMD都是对js模块化的规范,并都遵循一个文件一个模块
CommonJS适用于服务器端而AMD、CMD适用于浏览器端(主要同异步相关)
CMD需要事先写好依赖模块,而CMD可以在模块使用过程中再加载依赖模块
Node.js & npm命令行命令

npm是node包(模块)管理和分发的工具

node -v 或 node --version - 获得node的版本号,可用于检查node是否安装
node -h 或 node --help - 调用node命令行简易帮助
node script.js - 执行script.www.yxin7.com js文件
npm -v - 查看npm版本号
npm install <name> -g - 安装模块到全局
npm install <name> --save-dev - 安装模块到当前文件夹,并写入到package.json里(–save),并至于开发者依赖向devDependencies(-dev,若没有-dev,则写入dependencies)
npm update <name> - 更新模块
npm uninstall <name> - 卸载模块
npm list - 当前目录下已安装的模块
npm init - 项目初始化向导
Node.js自带模块

Node.js遵循CommonJS规范,并且提供了很多基础的自带模块,这些模块的文档都可以在官网找到
下面对一些常见模块进行简介

Events事件模块

仅提供一个对象 - eventEmitter,Node.js的大部分核心API都是由异步事件驱动的
下面对常见用法进行简介

eventEmitter.emit(eventName[, arg1][, arg2][, ...]) - 触发事件,事件名称eventName是必须的,后面跟上任意个参数用于传递
eventEmitter.on(eventName, listener) - 监听事件,eventName是监听的事件名称,listener是事件触发函数,函数的参数即是触发事件时所定义的参数
fs文件系统模块

模块中提供的所有方法都有同步和异步两种模式(同步模式名字带有’Sync’后缀,例如读取文件: [异步]readFile -> [同步]readFileAync)

fs.readFile(file[, options], callback) - 读取文件,选项(options)可以定义编码(例如’utf8’),回调函数中有(err, data)两个参数,代表错误信息和读取到的数据
fs.writeFile(file, data[, options], callback) - 写入文件,data为待写入数据,回调函数仅有err一个参数表示错误信息
fs.open(path, flags[, mode], callback www.zgktv.cn ) - 打开文件,打开行为(flag)为必须,详见官网,mode为文件权限,默认为可读可写(0666),回调函数中有(err, fd)两个参数,后者是文件标识
fs.close(fd, callback) - 关闭文件,第一个参数为文件标识(由fs.open返回),回调函数只有错误信息(err)参数
fs.stat(path, callback) - 获取文件信息,回调函数带有两个参数(err, stat),其中stat是fs.Stats对象,此对象有很多属性(创建时间、文件大小等)和很多方法(是否是文件、是否是目录等)
fs.unlink(path, callback) - 删除文件,回调函数中只有错误信息(err)参数
http模块

http模块主要用于搭建HTTP服务端和客户端,使用HTTP服务器或客户端功能必须调用http模块

http.createServer([requestListener]).listen(port) - 创建服务器并监听某端口
http.request(options[, callback]) - 发送客户端请求
stream流模块

流是可读也是可写的,所有的流都是eventEmitter的实现,并且也有多种实现形式,比如http模块中、fs模块中等
流操作主要针对文件数据的处理和传递,其中最离不开 pipe(导流)方法 了,可以将这个方法分解为 不断从可读的流中获得指定长度的数据 => 将获取到的数据写入可写流中 <=> 并平衡读取与写入速度,防止滞留数据

使用的其他Node.js模块

Node.js中只提供了很多基础模块,虽然功能齐全,但是使用起来非常不方便
需要借助一些别人写好的封装好的模块,使我们的开发更加便捷

request模块

request模块让http请求更加简单,且支持https和重定向

常用方法

request.post(url, callback)或request(url, callback) - 一个POST的请求
request.put(url, callback) - 一个GET的请求
request(options, callback) - 带有选项和回调的请求,可以在选项中设置method、www.yyzx66.cn url、http headers等;回调函数有三个参数(err, res, body),分别是错误信息、http.IncomingMessage对象、返回数据
流操作

request(url).pipe(fs.createWriteStream(tar)) - 输出到文件流
fs.createReadStream(tar).www.yigouyule.cn pipe(request.put(url)) - 将文件流作为请求发送
cheerio模块

专为服务器定制,小而快的jQuery的核心,用cheerio来解析html或者xml文档,专注于DOM解析,并且有着和jQuery一致的语法,

$ = cheerio.load(html[, opt]) - 将html文档加载进cheerio,传入参数可以更改解析选项
$(selector[, context][, root]) - 与jQuery非常相似的选择器,selector在context中搜索,而context又在root中搜索
其他包括对类(class)、属性(attr/data)、内容(text/html)、DOM树(parents/find/next/prev)等的操作都与jQuery一致

时间: 2024-12-22 18:21:00

使用Node.js作为后台进行爬虫的相关文章

[js高手之路]Node.js实现简易的爬虫-抓取博客所有文章列表信息

抓取目标:就是我自己的博客:http://www.cnblogs.com/ghostwu/ 需要实现的功能: 抓取博客所有的文章标题,超链接,文章摘要,发布时间 需要用到的库: node.js自带的http库 第三方库:cheerio,这个库就是用来处理dom节点的,他的用法几乎跟jquery用法一模一样,所以有了这个利器,写一个爬虫就非常简单 准备工作: 1,npm init --yes 初始化package.json 2,安装cheerio:npm install cheerio --sav

Node.js之网络小爬虫

使用JavaScript在前端访问跨域页面常常用到Ajax,后端Node.js抓取网页信息就容易得多. 下面是一个最简单的例子,抓取我的博客主页信息,显示首页博客标题. 1 var http = require('http') 2 var cheerio = require('cheerio') 3 4 var url = 'http://www.cnblogs.com/feitan/' 5 6 function filterHtml(html) { //使用cheerio处理DOM 7 var

Node.js(九)——HTTP小爬虫

HTTP爬虫 网络上每时每刻都有海量的请求,有从客户端到服务器端的,也有服务器端到服务器端的 一般在浏览器里,我们是通过ajax来完成表单的提交或者是数据的获取, 那在http模块里呢get和request这两个接口来完成数据的获取或者是提交. 来个实例,爬一下51cto博客的数据 先从最简单的爬虫开始,爬源码 示例代码如下: var http = require('http') var url = 'http://mazongfei.blog.51cto.com/3174958/1909817

Node.js(十三)——Promise重构爬虫代码

在重构代码之前,先要了解下什么是https? https协议:基于ssl/tls的http协议,所有的数据都是在 ssl/tls协议的封装之上传输的,也就是说https协议是在http协议基础上 添加了ssl/tls握手以及数据加密传输,因此这就是两者之间最大的区别. https模块专门处理加密访问的,区别在于搭建https服务器的时候需要有ssl证书. 模拟搭建https服务器 var https = require('https') var fs = require('fs')//文件系统模

Node.js实现单页面爬虫

在imooc网上跟着老师写了两个爬虫,一个最简单的直接爬整个页面,一个完善版把章节标题和对应编号打出来了. 看完之后,自己也想写一个爬虫,用自己的博客做测试,虽然结果并没有很成功- -,还是把代码放上来. 目标是抓取章节的标题. 博客页面: 对应标签: 页面源代码: 经过分析,我们应该要抓取class=artHead的<div>,往下还有一个没有类的<div>,然后找到它的<h3>标签下子标签<a>的内容,就是章节的名字. 上代码: //引入http模块 v

node.js web实时消息服务器后台推送技术方案---GoEasy

Goeasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送!个人感觉goeasy推送更稳定,推送速度快,代码简单易懂上手快浏览器兼容性:GoEasy推送支持websocket 和polling两种连接方式,从而可以支持IE6及其以上的所有版本,同时还支持其它浏览器诸如Firefox, Chrome, Safari 等等.支 持不同的开发语言:   GoEasy推送提供了Restful API接口,无论你的后台程序用的是哪种语言都可以通过RestfulAPI来实现后台实时推送.

node.js Web实时消息后台服务器推送技术---GoEasy

越来越多的项目需要用到实时消息的推送与接收,怎样用node.js实现最方便呢?我这里推荐大家使用GoEasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送! 浏览器兼容性:GoEasy推送 支持websocket 和polling两种连接方式,从而可以支持IE6及其以上的所有版本,同时还支持其它浏览器诸如Firefox, Chrome, Safari 等等. 支持不同的开发语言:    GoEasy推送 提供了Restful API接口,无论你的后台程序用的是哪种语言都可以通

《Node.js实战(双色)》作者之一——吴中骅访谈录

1 请和大家介绍下您及所从事的工作. 我目前在苏州唐人数码工作,是一家本地的网络游戏公司,主要经营棋牌游戏,最近自主研发了一款3D的网路游戏--争渡三国,我在公司负责游戏平台工作,对公司自营的游戏以及联运游戏提供Web支持,比如用户登录,游戏充值,平台接口服务等. 2 为什么会想到写这样一本书? 自从Node.js问世以来,它的发展速度相当迅猛,最早只有英文资料可以学习,现在中文的书籍也慢慢多了起来,但是这些学习教材绝大部分都是从基础的安装,hello world开始,然后进行对Node.js各

node.js Websocket消息推送---GoEasy

Goeasy, 它是一款第三方推送服务平台,使用它的API可以轻松搞定实时推送!个人感觉goeasy推送更稳定,推送速度快,代码简单易懂上手快浏览器兼容性:GoEasy推送支持websocket 和polling两种连接方式,从而可以支持IE6及其以上的所有版本,同时还支持其它浏览器诸如Firefox, Chrome, Safari 等等.支 持不同的开发语言:   GoEasy推送提供了Restful API接口,无论你的后台程序用的是哪种语言都可以通过RestfulAPI来实现后台实时推送.