使用NodeJs爬取数据

使用NodeJs爬取数据

前言

最近因为一个外行朋友让我帮忙整理一个网站的数据,我第一时间就想到了就写爬虫去爬取,相对于NodeJs来,我更熟练一点python,但是我也不会专业的爬虫,只会简单的请求和文本分析这样。所以我找到了这个网站的数据页面,然后简单的使用python写了三行代码:导入request包、请求网页、打印结果,如下:

import requests 
page = requests.get("http://xxx.com")
print(page.content)

然而,可怕的事情发生了,我发现输出的结果和我看到的不太一样,我又用浏览器访问那个那个url,我大吃一惊,网页上显示说监测到我使用了爬虫工具,然后对于这个页面,我的IP被封了,o(╥﹏╥)o。

也许对于专业些的人来说,这些都是常事,这些应该是叫反爬措施(好像是这样叫)吧,但是对于我来说就一脸茫然与恐惧。

不过我分析觉得解决办法应该也不难,可能他们的反爬措施就是根据header或者cookie来判断的,也许是因为pythonrequest的请求里面包含了什么标识符什么的,因为我使用postman是可以拿到那个网页的,但是,说的简单,我也不熟悉哇o(╥﹏╥)o,最后我尝试使用了NodeJs去请求那个URL,发现可以,没有将我封掉,而且chrome那里可以针对请求 copy as NodeJs fetch ✌️,我仿佛找到了新大陆,NodeJS之前也会玩下,但是因为用的很少,所以这次因为使用NodeJS解决了我的问题,我对NodeJs也是更加喜爱,所以为了熟练使用,于是,就有了这篇文章记录我使用NodeJs爬取数据时的一些步骤以及方法。

网络请求

原始方法

NodeJs 对于GET/POST的元素请求方法是使用http包中的request方法来进行请求,数据通过querystring来序列化等,下面是一个简单的GET请求:

var http = require('http'); 
var qs = require('querystring'); 
var data = { 
    a: 123, 
    time: new Date().getTime()
};//这是需要提交的数据 
var content = qs.stringify(data);  
var options = { 
    hostname: '127.0.0.1', 
    port: 10086, 
    path: '/getTest/?' + content, 
    method: 'GET' 
};  
var req = http.request(options, function (res) { 
    console.log('STATUS: ' + res.statusCode); 
    console.log('HEADERS: ' + JSON.stringify(res.headers)); 
    res.setEncoding('utf8'); 
    res.on('data', function (chunk) { 
        console.log('BODY: ' + chunk); 
    }); 
});  
req.on('error', function (e) { 
    console.log('problem with request: ' + e.message); 
});  
req.end();

(¦3」∠) 对于习惯了用第三方包的我来说,看到这样一个简单的请求还需要这么繁琐就觉得非常难受,所以这种方式我也是不建议使用的。推荐的方式是使用axiosnode-fetch

Axios

axios我最开始接触是那个时候在做vue开发的时候接触的,怎么说呢,它的api通俗易懂、简单易用,非常容易上手,所以我在使用NodeJs做网络请求的时候第一时间就想到了这个包。

安装方式也非常简单:

npm install axios | bower install axios | yarn add axios

它的使用也是非常的简单的,比如一个POST请求:

const axios = require('axios');
axios.post('/user', {
  firstName: 'Fred',
  lastName: 'Flintstone'
})
  .then(response=> {
  console.log(response);
})
  .catch(error=> {
  console.log(error);
});

是否非常简单?😸 更多使用方式可以参考主页地址

Node-fetch

好吧,fetch我之前是听过,只是因为那个时候在用axios用的挺舒服的,所以也就没有去专研过它,这次用到他其实也是因为chrome上面的 copy as NodeJs fetch 才接触,感觉其实也挺好的,不过遇到个问题就是:如果这个请求不是json格式的返回数据只是html页面的时候该如何获取?

fetch是一种HTTP数据请求的方式,是XMLHttpRequest的一种替代方案。fetch不是ajax的进一步封装,而是原生js。Fetch函数就是原生js,没有使用XMLHttpRequest对象。

node-fetch你可以认为是NodeJS版的fetch

安装方式也非常简单:

npm install node-fetch
  • 请求text或者html页面

    const fetch = require('node-fetch'); 
    (async () => {
      const response = await fetch('https://baidu.com');
      const body = await response.text(); 
      console.log(body);
    })();
    
  • 请求JSON

    const fetch = require('node-fetch'); 
    (async () => {
      const response = await fetch('https://api.github.com/users/github');
      const json = await response.json();
    
      console.log(json);
    })();
    
  • POST请求

    const fetch = require('node-fetch'); 
    (async () => {
      const body = {a: 1}; 
      const response = await fetch('https://httpbin.org/post', {
          method: 'post',
          body: JSON.stringify(body),
          headers: {'Content-Type': 'application/json'}
      });
      const json = await response.json(); 
      console.log(json);
    })();
    

更多详细说明请参考 官方网站

网页分析工具 cheerio

有的时候我们去网上爬取数据的时候,不一定有现成的数据接口,这个时候你只有获取到一个html的文本数据,这个时候你就需要通过一些方式去分析这个文本里面拿你想要的数据了,在我知道的方法里有两个方法:一是通过正则的方式去匹配,这种方式感觉好像效率不是很高,不推荐用,但是是万能的。二是通过DOM的方法根据节点来查找。

但是网页里面可以通过Jquery或者Document的方式来获取节点,面对一个html文本的时候NodeJs该如何去分析出数据呢?通过网上的查找我发现了可以使用 cheerio这个库,可以加载html文本,然后像jquery一样来操作即可。

安装方式:

npm install cheerio

使用示例:

const cheerio = require('cheerio'),
let html = '
    <ul id="fruits">
  <li class="apple">Apple</li>
  <li class="orange">Orange</li>
  <li class="pear">Pear</li>
    </ul>
';
$ = cheerio.load(html);
// 使用选择器去获取数据
$('.apple', '#fruits').text()
//=> Apple

$('ul .pear').attr('class')
//=> pear

$('li[class=orange]').html()
//=> Orange

cheerio是一个非常强大的工具,感兴趣的话可以去参考其 官网

文件系统

有的时候,我们数据处理好了之后,需要写入到文件中,这个时候我们就要使用到NodeJS的文件系统了,因为NodeJs的文件系统的接口非常的好用,我也没有去找过有没有第三方相关的包了,这里也推荐使用NodeJs的内置文件系统的Api

Node.js 文件系统(fs 模块)模块中的方法均有异步和同步版本,例如读取文件内容的函数有异步的 fs.readFile() 和同步的 fs.readFileSync()。异步的方法函数最后一个参数为回调函数,回调函数的第一个参数包含了错误信息(error)。

使用示例:

const fs = require('fs')
// 异步读取
function readTest() {
    console.log("开始读取...");
    fs.readFile("./temp.txt",{encoding:'utf-8'},(err,data)=>{
        if (err) {
            console.log("err:"+err);
        }
        console.log("文件内容为:"+data); 
    })
    console.log("程序执行完毕!!!");
}
// 同步读取
function readSyncTest() {
    console.log("开始读取...");
    let data = fs.readFileSync("./temp.txt",'utf-8')
    console.log("文件内容为:"+data); 
    console.log("程序执行完毕!!!");
}
// 异步写入
function writeTest() {
    console.log("文件开始写入...");
    let content = "Hello world"
    fs.writeFile('./temp2.txt',content,err=>{
        if (err) {
            console.log("err:"+err);
        }
        console.log("文件写入完毕");
    })
    console.log("程序执行完毕");
} 
// 同步写入
function writeSyncTest() {
    console.log("文件开始写入...");
    let content = "Hello world"
    fs.writeFileSync('./temp2.txt',content) 
    console.log("程序执行完毕");
} 

一些接口:

序号 方法 & 描述
1 fs.rename(oldPath, newPath, callback)异步 rename().回调函数没有参数,但可能抛出异常。
2 fs.ftruncate(fd, len, callback)异步 ftruncate().回调函数没有参数,但可能抛出异常。
3 fs.ftruncateSync(fd, len)同步 ftruncate()
4 fs.truncate(path, len, callback)异步 truncate().回调函数没有参数,但可能抛出异常。
5 fs.truncateSync(path, len)同步 truncate()
6 fs.chown(path, uid, gid, callback)异步 chown().回调函数没有参数,但可能抛出异常。
7 fs.chownSync(path, uid, gid)同步 chown()
8 fs.fchown(fd, uid, gid, callback)异步 fchown().回调函数没有参数,但可能抛出异常。
9 fs.fchownSync(fd, uid, gid)同步 fchown()
10 fs.lchown(path, uid, gid, callback)异步 lchown().回调函数没有参数,但可能抛出异常。
11 fs.lchownSync(path, uid, gid)同步 lchown()
12 fs.chmod(path, mode, callback)异步 chmod().回调函数没有参数,但可能抛出异常。
13 fs.chmodSync(path, mode)同步 chmod().
14 fs.fchmod(fd, mode, callback)异步 fchmod().回调函数没有参数,但可能抛出异常。
15 fs.fchmodSync(fd, mode)同步 fchmod().
16 fs.lchmod(path, mode, callback)异步 lchmod().回调函数没有参数,但可能抛出异常。Only available on Mac OS X.
17 fs.lchmodSync(path, mode)同步 lchmod().
18 fs.stat(path, callback)异步 stat(). 回调函数有两个参数 err, stats,stats 是 fs.Stats 对象。
19 fs.lstat(path, callback)异步 lstat(). 回调函数有两个参数 err, stats,stats 是 fs.Stats 对象。
20 fs.fstat(fd, callback)异步 fstat(). 回调函数有两个参数 err, stats,stats 是 fs.Stats 对象。
21 fs.statSync(path)同步 stat(). 返回 fs.Stats 的实例。
22 fs.lstatSync(path)同步 lstat(). 返回 fs.Stats 的实例。
23 fs.fstatSync(fd)同步 fstat(). 返回 fs.Stats 的实例。
24 fs.link(srcpath, dstpath, callback)异步 link().回调函数没有参数,但可能抛出异常。
25 fs.linkSync(srcpath, dstpath)同步 link().
26 fs.symlink(srcpath, dstpath[, type], callback)异步 symlink().回调函数没有参数,但可能抛出异常。 type 参数可以设置为 'dir', 'file', 或 'junction' (默认为 'file') 。
27 fs.symlinkSync(srcpath, dstpath[, type])同步 symlink().
28 fs.readlink(path, callback)异步 readlink(). 回调函数有两个参数 err, linkString。
29 fs.realpath(path[, cache], callback)异步 realpath(). 回调函数有两个参数 err, resolvedPath。
30 fs.realpathSync(path[, cache])同步 realpath()。返回绝对路径。
31 fs.unlink(path, callback)异步 unlink().回调函数没有参数,但可能抛出异常。
32 fs.unlinkSync(path)同步 unlink().
33 fs.rmdir(path, callback)异步 rmdir().回调函数没有参数,但可能抛出异常。
34 fs.rmdirSync(path)同步 rmdir().
35 fs.mkdir(path[, mode], callback)S异步 mkdir(2).回调函数没有参数,但可能抛出异常。 访问权限默认为 0777。
36 fs.mkdirSync(path[, mode])同步 mkdir().
37 fs.readdir(path, callback)异步 读取目录的内容。
38 fs.readdirSync(path)同步 readdir() 返回文件数组列表。
39 fs.close(fd, callback)异步 close().回调函数没有参数,但可能抛出异常。
40 fs.closeSync(fd)同步 close().
41 fs.open(path, flags[, mode], callback)异步打开文件。
42 fs.openSync(path, flags[, mode])同步 version of fs.open().
43 fs.utimes(path, atime, mtime, callback)
44 fs.utimesSync(path, atime, mtime)修改文件时间戳,文件通过指定的文件路径。
45 fs.futimes(fd, atime, mtime, callback)
46 fs.futimesSync(fd, atime, mtime)修改文件时间戳,通过文件描述符指定。
47 fs.fsync(fd, callback)异步 fsync.回调函数没有参数,但可能抛出异常。
48 fs.fsyncSync(fd)同步 fsync.
49 fs.write(fd, buffer, offset, length[, position], callback)将缓冲区内容写入到通过文件描述符指定的文件。
50 fs.write(fd, data[, position[, encoding]], callback)通过文件描述符 fd 写入文件内容。
51 fs.writeSync(fd, buffer, offset, length[, position])同步版的 fs.write()。
52 fs.writeSync(fd, data[, position[, encoding]])同步版的 fs.write().
53 fs.read(fd, buffer, offset, length, position, callback)通过文件描述符 fd 读取文件内容。
54 fs.readSync(fd, buffer, offset, length, position)同步版的 fs.read.
55 fs.readFile(filename[, options], callback)异步读取文件内容。
56 fs.readFileSync(filename[, options])
57 fs.writeFile(filename, data[, options], callback)异步写入文件内容。
58 fs.writeFileSync(filename, data[, options])同步版的 fs.writeFile。
59 fs.appendFile(filename, data[, options], callback)异步追加文件内容。
60 fs.appendFileSync(filename, data[, options])The 同步 version of fs.appendFile.
61 fs.watchFile(filename[, options], listener)查看文件的修改。
62 fs.unwatchFile(filename[, listener])停止查看 filename 的修改。
63 fs.watch(filename[, options][, listener])查看 filename 的修改,filename 可以是文件或目录。返回 fs.FSWatcher 对象。
64 fs.exists(path, callback)检测给定的路径是否存在。
65 fs.existsSync(path)同步版的 fs.exists.
66 fs.access(path[, mode], callback)测试指定路径用户权限。
67 fs.accessSync(path[, mode])同步版的 fs.access。
68 fs.createReadStream(path[, options])返回ReadStream 对象。
69 fs.createWriteStream(path[, options])返回 WriteStream 对象。
70 fs.symlink(srcpath, dstpath[, type], callback)异步 symlink().回调函数没有参数,但可能抛出异常。

更多详情参考 官网说明

Demo

可参考我的另一篇文章:使用TypeScript写node脚本爬取小说

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,179评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,229评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,032评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,533评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,531评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,539评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,916评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,813评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,568评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,654评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,354评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,937评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,918评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,152评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,852评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,378评论 2 342