基于Nodejs的爬虫

简介

基于 Node.JS 爬取 博客园 1W+博文,对博文内容做关键词提取,生成词云。

演示

安装

安装 gitNode.JSMongoDBYarn

克隆代码

git clone [email protected]:ZhihaoJian/bokeyuan_spider.git

如果觉得安装速度慢,可将源切换到淘宝,cmd 或者 powershell 下执行

 yarn config set registry ‘https://registry.npm.taobao.org‘

进入bokeyuan_spider文件夹安装依赖

yarn install

目录结构

整个项目重要目录是publicserverpublic目录放置词云的前端代码,server目录放置后端代码。在项目中,server目录还放置了爬虫、数据库等相关代码。另外,根目录下的 word.txtjieba 分词结果。

基本工作原理

我们知道互联网是通过每一份HTML通过某种方式互相关联在一起,从而形成一个巨大的 。我们只要在其中一份页面就可以沿着 去到不同的页面。而页面和页面之间是通过 超链接 方式联系在一起,所以我们只要找到这个 超链接 就可以到达下一个页面。而爬虫就是这样的工作方式,找到 超链接,沿着超链接一直前进并记录下所到之处,就可以抵达互联网的任何一个角落。

核心功能

  • 抓取博文链接

spider.js 中我们将使用 Google Chrome 的 puppeteer,作为演示

打开server目录下的spider文件里的spider.jsspider.js的主要功能是使用 puppeteer 对博客园的 班级列表博文 链接进行爬取。

以下是spider.js的核心代码

/**
 *  spider.js
 */
toPage(page, URL).then(async (url) => {
    console.log(‘PAGE LOG‘.blue + ‘ Page has been loaded‘);

    //分页数量
    totalPages = await page.$eval(‘.last‘, el => Number.parseInt(el.textContent));
    console.log(`PAGE LOG`.blue + ` site:${URL} has ${totalPages} pages`);

    //抓取post文超链接
    for (let i = 1; i <= totalPages; i++) {
        url = getNextUrl(i);
        await toPage(page, url, 1500);
        let links = await parseElementHandle(page, url);
        let result = await getPostUrls(links);
        postUrls.push(result);
    }

    //保存到数据库
    saveToDB(postUrls);

    console.log(‘PAGE LOG : All tasks have been finished.‘.green);
    writeToFileSys();
    await broswer.close();
});

toPage方法是根据指定的URL跳转的相应页面,方法接收两个参数,page是经过 puppeteer 实例化的对象,URL 是我们指定爬虫的入口。待页面加载成功以后,响应回调函数,获取当前页面的最大分页数量,for 循环每隔 1500ms 跳转到下一页并抓取页面中所有博文链接。最后保存到数据库中。

  • 抓取博文内容

打开 content.js,在这里我们不用前面演示的 puppeteer 模块而使用 cheeriorequest模块。

安装 cheeriorequest 模块

yarn add cheerio request

cheerio可以简单看作是服务器端的jQuery,而request是一个高度封装好了的 nodejs http模块

以下是 content.js 的核心代码示例

    /* content.js
     * 根据post文链接抓取post文内容
     */
    getIPs().then(async ipTable => {
        for (let i = 0; i < postLen; i++) {
            let postUrl = docs[i];
            proxyIndex < ipTable.length ? proxyIndex : proxyIndex = 0;
            rq(postUrl, ipTable[proxyIndex++], (body) => parseBody(body, postUrl))
                .catch(async e => {
                    console.log(‘LOG‘.red + ‘: Request ‘ + postUrl + ‘ failed. Retrying...‘);
                    ipTable.splice(proxyIndex, 1);
                    await delay(3000);
                    getIPs().then(ips => ipTable = ipTable.concat(ips));
                    await rq(postUrl, ipTable[++proxyIndex], (body) => parseBody(body, postUrl));
                })
        }
    })

函数 getIps 用于获取三方代理IP,然后使用 request 模块对指定的博文链接发起http请求。函数 parseBody 使用 cheerio 模块解析博文内容,然后保存到数据库中。在 catch 块中我们将处理请求失败的情况,这里我们更换新的代理IP,针对请求失败的博文链接重新发起请求。

  • 分词

关于分词,我们选择 node-jieba,它是python jieba库的一个nodejs版本

安装 node-jieba,详细 API

yarn add node-jieba

核心代码如下

/* jieba.js
 * 分词,以txt形式保存到文件系统
 */
(() => {
    const jiebaResult = [];
    POST.find({}, async (err, docs) => {
        if (err) {
            throw new Error(err)
        }
        docs.forEach((v) => {
            jiebaResult.push(jieba(v.post));
        });
        await Promise.all(jiebaResult).then(() => {
            writeToFileSys();
        })
        console.log(‘end‘);
    })
})()

我们从数据库中取出所有的博文,循环依次对博文做一个关键词提取。因为文本量巨大,所以这里的重点是 异步分词。待所有 异步分词 结束以后,将分词结果写入文件系统。

下面给出异步分词的实现

/**
 * jieba异步分词
 */
function jieba(post) {
    return new Promise(resolve => {
        analyzer.tags(post, {
            top: 20,
            withWeight: false,
            textRank: false,
            allowPOS: [‘ns‘, ‘n‘, ‘vn‘, ‘v‘]
        }, (err, results) => {
            if (err) {
                console.log(err);
            }
            if (results) {
                results.forEach(word => {
                    if (wordMap.has(word)) {
                        let count = wordMap.get(word);
                        wordMap.set(word, ++count);
                    } else {
                        wordMap.set(word, 0);
                    }
                })
            }
            resolve(wordMap);
        })
    })
}

jieba 函数返回一个 PromisePromise 是 es6 新增的一种异步解决方案,比传统的解决方案,例如回调函数和事件更强大和合理。因为要对词频做统计,使用 Map 对象保存分词结果,这从查找性能或是可读性上解释都更加合理。

踩坑之路

  • 使用 cheerio 解析HTML,中文乱码

在使用 cheerio.html() 方法时候,发现多数博文内容都变成了 x56ED等 Unicode编码。经查阅,可以关闭这个转换实体编码的功能

const $ = cheerio.load(html)

改成

const $ = cheerio.load(html,{decodeEntities:false})
  • 代理问题

单IP爬取1W数据量,明显要被封号的。最佳的解决方式是买一堆的代理IP,配合 request 库,轮询使用代理IP进行请求,进行爬取。亲测使用得当的情况下,1W+博文可以在5min内爬取完毕。

示例代码如下

/**
*
* @param {string} REQUEST_URL 待爬取的URL
* @param {string} proxy 代理IP
* @param {fn} success 成功回调函数
* @param {fn} fail 失败回调函数
*/
function rq(REQUEST_URL, proxy, callback) {
    return rp({ ‘url‘: url.parse(REQUEST_URL), ‘proxy‘: `http://${proxy}` })
        .then(res => callback(res))
}

词频前200

原文地址:https://www.cnblogs.com/jianzhihao/p/8782661.html

时间: 2024-10-12 12:08:02

基于Nodejs的爬虫的相关文章

基于nodejs模拟浏览器post请求爬取json数据

今天想爬取某网站的后台传来的数据,中间遇到了很多阻碍,花了2个小时才请求到数据,所以我在此总结了一些经验. 首先,放上我所爬取的请求地址http://api.chuchujie.com/api/?v=1.0: 下面我们开始爬取数据. 一.写一个基于nodejs的爬虫 1.引入所需模块 这里需要引入http模块(nodejs用来向浏览器发送http请求的模块)和querystring模块(把前台传过来的对象形式的参数转化成字符串形式): var http = require("http"

基于Nodejs的Gulp LiveReload与VisualStudio完美结合。

本文版权桂博客园和作者吴双共同所有,转载和爬虫请注明原文地址 http://www.cnblogs.com/tdws/p/6016055.html 写在前面 大家好我是博客园的蜗牛,博客园的蜗牛就是我.学习这篇文章,你不需要任何nodejs基础,当然你有的话就更顺利了.园子里有很多全栈或者是前端后台数据库都有需要你发光发热的人.也许你很喜欢做着这样的事儿,也许你不喜欢但是难以逃脱这样的安排.但是,无论你是前端,还是后端,还是全栈,好的工具和方法总是很重要的.当你在VS里写前端页面时,一遍又一遍的

转-基于NodeJS的14款Web框架

基于NodeJS的14款Web框架 2014-10-16 23:28 作者: NodeJSNet 来源: 本站 浏览: 1,399 次阅读 我要评论暂无评论 字号: 大 中 小 摘要: 在几年的时间里,Node.js逐渐发展成一个成熟的开发平台,吸引了许多开发者.有许多大型高流量网站都采用Node.js进行开发,像PayPal,此外,开发人员还可以使用它来开发一些快速移动Web框架. 下面就介绍14款基于Node.js的Web应用框架,大家不... 在几年的时间里,Node.js逐渐发展成一个成

基于NodeJS的全栈式开发

随着不同终端(Pad/Mobile/PC)的兴起,对开发人员的要求越来越高,纯浏览器端的响应式已经不能满足用户体验的高要求,我们往往需要针对不同的终端开发定制的版本.为了提升开发效率,前后端分离的需求越来越被重视,后端负责业务/数据接口,前端负责展现/交互逻辑,同一份数据接口,我们可以定制开发多个版本. 这个话题最近被讨论得比较多,阿里有些BU也在进行一些尝试.讨论了很久之后,我们团队决定探索一套基于NodeJS的前后端分离方案,过程中有一些不断变化的认识以及思考,记录在这里,也希望看到的同学参

NodeJS制作爬虫全过程

这篇文章主要介绍了NodeJS制作爬虫的全过程,包括项目建立,目标网站分析.使用superagent获取源数据.使用cheerio解析.使用eventproxy来并发抓取每个主题的内容等方面,有需要的小伙伴参考下吧. 今天来学习alsotang的爬虫教程,跟着把CNode简单地爬一遍. 建立项目craelr-demo我们首先建立一个Express项目,然后将app.js的文件内容全部删除,因为我们暂时不需要在Web端展示内容.当然我们也可以在空文件夹下直接 npm install express

也谈基于NodeJS的全栈式开发(基于NodeJS的前后端分离)

随着不同终端(Pad/Mobile/PC)的兴起,对开发人员的要求越来越高,纯浏览器端的响应式已经不能满足用户体验的高要求,我们往往需要针对不同的终端开发定制的版本.为了提升开发效率,前后端分离的需求越来越被重视,后端负责业务/数据接口,前端负责展现/交互逻辑,同一份数据接口,我们可以定制开发多个版本. 这个话题最近被讨论得比较多,阿里有些BU也在进行一些尝试.讨论了很久之后,我们团队决定探索一套基于NodeJS的前后端分离方案,过程中有一些不断变化的认识以及思考,记录在这里,也希望看到的同学参

基于Nodejs的BigPipe实现

简介 BigPipe是facebook推出的用于优化网页加载速度的技术,它突破了传统网页的加载方式,通过把网页内容进行分块,然后对这些块进行并行传输从,使得浏览器的渲染无需等到整个页面加载完毕,大大提升网页加载速度.天猫上首页就有这种实现. Bigpie适用于网页分块清晰,且规模达到一定程度.简单说就是使用bigpipe要达到优化的效果才有意义. 实现原理 利用http1.1中的transfer-encoding:chunked头消息来进行分块传输,初始时只传输网页的骨架,待到具体分块到达后,利

基于NodeJS进行前后端分离

1.什么是前后端分离 传统的SPA模式:所有用到的展现数据都是后端通过异步接口(AJAX/JSONP)的方式提供的,前端只管展现. 从某种意义上来说,SPA确实做到了前后端分离,但这种方式存在两个问题: WEB服务中,SPA类占的比例很少.很多场景下还有同步/同步+异步混合的模式,SPA不能作为一种通用的解决方案. 现阶段的SPA开发模式,接口通常是按照展现逻辑来提供的,而且为了提高效率我们也需要后端帮我们处理一些展现逻辑,这就意味着后端还是涉足了view层的工作,不是真正的前后端分离. 现阶段

基于Nodejs构建属于自己的微信公众号

摘要: 微信,庞大的用户基数,极强的用户粘性,在近两年吸引了无数的开发者注意力. Nodejs,近两年发展非常快的开发工具,尤其适合构建移动后台.本文就以笔者自己开发的实例,来描述如何基于Nodejs开发属于自己的微信公众号.在这个实例中,主要使用到了express, wechat, mongodb, monk等模块. 前期准备: 1.   申请微信公众号, 前往 https://mp.weixin.qq.com/  申请,这里不做过多阐述. 2.   购买服务器, 这里推荐Amazon的EC2