用nodejs做简单的爬虫,其实是一件简单的事情。
我们以 http://www.ledu365.com/ 做一个简单的例子。
在此之前,你需要掌握 :es6, async /await 和简单的 express;

分析页面

我们发现它的列表页地址栏 
http://www.ledu365.com/shehui/list_4_9.html
http://www.ledu365.com/shehui/list_4_13.html

大概是这样 的

http://www.ledu365.com/(栏目的中文拼音)/list_(栏目的id)_(栏目的页数).html

=> http://www.ledu365.com/${url_type}/list_${type_id}_${i}.html

于是我们可以得到

let typeArray = [
     {
        url_type: 'redu',
        type: '热读',
        type_num: 1,
    },
     {
        url_type: 'wenyuan',
        type: '文苑',
        type_num: 2,
    },
     {
        url_type: 'qinggan',
        type: '情感',
        type_num: 3,
    },
   {
        url_type: 'shehui',
        type: '社会',
        type_num: 4,
    },
     {
        url_type: 'shenghuo',
        type: '生活',
        type_num: 5,
    },
     {
        url_type: 'rensheng',
        type: '人生',
        type_num: 6,
    },
    {
        url_type: 'renwu',
        type: '人物',
        type_num: 7,
    },
    lizhi = {
        url_type: 'lizhi',
        type: '励志',
        type_num: 8,
    },
    {
        url_type: 'shiye',
        type: '视野',
        type_num: 9,
    },
     {
        url_type: 'xinling',
        type: '心灵',
        type_num: 10,
    },
     {
        url_type: 'xiaoyuan',
        type: '校园',
        type_num: 11,
    },
    {
        url_type: 'zhichang',
        type: '职场',
        type_num: 12,
    }
]

我们查看列表页的源代码,分析一下

<div class="listbox">
    <ul class="e2">
        <li>
            <a href='/a/wenyuan/53992.html' class='preview'>
                <img src='http://uploads-images.b0.upaiyun.com/uploads/allimg/160404/1_160404172439_1.jpg!cut' />
            </a>
            <a href="/a/wenyuan/53992.html" class="title">
                <b>喝古龙的酒,还是下金庸的棋</b>
            </a>
            <p class="intro">
                古龙爱写中年人。 李寻欢,四五十岁;傅红雪,近四十;楚留香,一路从少年写到中年…… 不过,古龙笔下的中年人,一点都不“中年”,反而很“中二”。 比如,被...
            </p>
        </li>
        <li>
            <a href='/a/wenyuan/53980.html' class='preview'>
                <img src='http://uploads-images.b0.upaiyun.com/uploads/allimg/160404/1_160404164017_1.jpg!cut' />
            </a>
            <a href="/a/wenyuan/53980.html" class="title">
                <b>曾醉</b>
            </a>
            <p class="intro">
                山深问桃花 染过几度春 哪一枝勾住过客衣上风尘 有倦意三分 要行路千程 怎记这山水静默待离人 轻舟邀斜月 听岸上孤筝 回旋处 恰趁醉 踏青石前门 有余音两声 共与...
            </p>
        </li>
        <li>
            <a href='/a/wenyuan/53973.html' class='preview'>
                <img src='http://uploads-images.b0.upaiyun.com/uploads/allimg/160330/1_160330214104_1.jpg!cut' />
            </a>
            <a href="/a/wenyuan/53973.html" class="title">写给超级马里奥的信</a>
            <p class="intro">
                嗨,马里奥老兄,停下你不止息的奔跑与跳跃吧,让金币先在天空闪亮一会儿,让蘑菇先在城墙里躲藏片刻。找块软软的草地悠闲地躺下,对着蓝天白云读读我的来信怎么...
            </p>
        </li>
        <li>

            <a href="/a/wenyuan/53961.html" class="title">
                <b>不能说的秘密_by尤妮妮</b>
            </a>
            <p class="intro">
                不过很快,简慧就被带到了李宽的粉丝见面会上。 车中,简慧接过瓶子,有些犹豫:“那我要是失败了呢,我……”“不,如果那个人是你的话,就不会失败,”他打断...
            </p>
        </li>
        <li>

            <a href="/a/wenyuan/53953.html" class="title">微写作7篇:201605</a>
            <p class="intro">
                星,无眠,伴长夜。薄雾起于南山。半月隐于柳枝间,愁怨。此日思月圆。轻起罗裳倚栏杆。恨不甘,当时情未言。若相逢,折花送,独钟。月圆星儿众。...
            </p>
        </li>
        <li>
            <a href='/a/wenyuan/53951.html' class='preview'>
                <img src='http://uploads-images.b0.upaiyun.com/uploads/allimg/160329/1_160329210348_1.jpg!cut' />
            </a>
            <a href="/a/wenyuan/53951.html" class="title">第三把刀</a>
            <p class="intro">
                风岚帝国是整片大陆上最强盛的国家,如果单论武力,没有一个国家能与之相提并论。风岚族人人以习武为荣。风岚人十分讲究江湖道义,国内大小教派林立,一派欣欣向...
            </p>
        </li>
        <li>
            <a href='/a/wenyuan/53943.html' class='preview'>
                <img src='http://uploads-images.b0.upaiyun.com/uploads/allimg/160328/1_160328165144_1.jpg!cut' />
            </a>
            <a href="/a/wenyuan/53943.html" class="title">贾宝玉和西门庆的共同爱好</a>
            <p class="intro">
                贾宝玉与西门庆有什么共同点? 这个问题一出,一定会让许多贾宝玉的粉丝立刻炸裂!虽然他俩都在女人圈里混,可是我们玉兄明明只得“意淫”二字,毕生致力于为年...
            </p>
        </li>
        <li>
            <a href='/a/wenyuan/53942.html' class='preview'>
                <img src='http://uploads-images.b0.upaiyun.com/uploads/allimg/160328/1_160328164845_1.jpg!cut' />
            </a>
            <a href="/a/wenyuan/53942.html" class="title">割雪的季节</a>
            <p class="intro">
                札幌眼下正是割冰的盛时。不,也许应该说是割雪吧。 今年的札幌,比起历年来雪都下得多。这不,进入二月之后,又忽然下起来了。 讽刺的是,往年,一到二月初旬的...
            </p>
        </li>
        <li>

            <a href="/a/wenyuan/53935.html" class="title">
                <b>犬人</b>
            </a>
            <p class="intro">
                有一妇人,中年得子,视若掌珍。凡诸百事,均不使为。及至弱冠,衣食起居,须人料理,如襁褓然。或有老者,劝妇人曰:“当教使言语。”妇人答曰:“我在,彼何必...
            </p>
        </li>
        <li>
            <a href='/a/wenyuan/53927.html' class='preview'>
                <img src='http://uploads-images.b0.upaiyun.com/uploads/allimg/160328/1_160328160924_1.jpg!cut' />
            </a>
            <a href="/a/wenyuan/53927.html" class="title">谈一场情商高的恋爱</a>
            <p class="intro">
                金庸小说里情商排名前三的人里,定有任盈盈一席之地。说到任大小姐的情商,那些聪明伶俐的女主角们都要靠边站。所有人都知道,令狐冲心里有个念兹在兹的小师妹,...
            </p>
        </li>
        <li>
            <a href='/a/wenyuan/53921.html' class='preview'>
                <img src='http://uploads-images.b0.upaiyun.com/uploads/allimg/160328/1_160328154330_1.jpg!cut' />
            </a>
            <a href="/a/wenyuan/53921.html" class="title">
                <b>《红楼梦》里的鄙视链</b>
            </a>
            <p class="intro">
                和地区鄙视链内陆—沿海—海外从里往外走模式不同,恰恰相反,红楼鄙视链是从外往里走,看大门.底层,其次守二门—扫院子—进房铺床叠被,最高端是屋里人,也即...
            </p>
        </li>
        <li>

            <a href="/a/wenyuan/53914.html" class="title">以己养鸟的悲剧</a>
            <p class="intro">
                庄子是个很有意思的人,经常讲一些很有寓意的小故事。他有个故事叫《鲁王养鸟》:昔者海鸟止于鲁郊,鲁侯御而觞之于庙,奏九韶为乐,具太牢以为膳。鸟乃眩视忧悲...
            </p>
        </li>
        <li>
            <a href='/a/wenyuan/53913.html' class='preview'>
                <img src='http://uploads-images.b0.upaiyun.com/uploads/allimg/160326/1_160326212839_1.jpg!cut' />
            </a>
            <a href="/a/wenyuan/53913.html" class="title">致妖猴:我凭什么要帮你</a>
            <p class="intro">
                孙悟空最让猴粉失望之处,就是他在打怪时,不断地向天庭或西方势力求助。 现在都流行大数据,我们就用数据来说话:西游里的所谓九九八十一难,其实有很大的水分...
            </p>
        </li>
        <li>
            <a href='/a/wenyuan/53910.html' class='preview'>
                <img src='http://uploads-images.b0.upaiyun.com/uploads/allimg/160326/1_160326205243_1.jpg!cut' />
            </a>
            <a href="/a/wenyuan/53910.html" class="title">生死搏杀</a>
            <p class="intro">
                藏医散木旦除了给人看病,还得到山林里采药。每次出门,他都要带上牦牛扎科阿尼和藏獒柔桑吉。扎科阿尼是他的坐骑,柔桑吉可替他带路,万一遇到危险还能成为帮手...
            </p>
        </li>
        <li>
            <a href='/a/wenyuan/53907.html' class='preview'>
                <img src='http://uploads-images.b0.upaiyun.com/uploads/allimg/160325/1_160325011752_1.jpg!cut' />
            </a>
            <a href="/a/wenyuan/53907.html" class="title">
                <b>我不想自己看这落寞的人世间</b>
            </a>
            <p class="intro">
                《海贼王》开始看的是娜美的故事。一个小孩子,倔强地看着凶恶的鱼人,要攒到1亿贝利来解放村子。 即使是第一次接触这动画,也被感动得一塌糊涂。一个人在寝室里...
            </p>
        </li>
    </ul>
</div>
<!-- /listbox -->

这个时候我们可以通过dom操作 获取 相应 的 标题、缩略图和摘要

分析内容页

<div class="viewbox">
    <div class="title">
        <h2>写给超级马里奥的信</h2>
    </div>
    <!-- /title -->
    <div class="info">
        <small>时间:</small>2016-03-30 21:38
        <small>来源:</small>网络
        <small>作者:</small>郝小靖
        <!-- /info -->
        <span style="float:right;">
            <iframe src="http://open.qzone.qq.com/like?url=http%3A%2F%2Fuser.qzone.qq.com%2F84778935&type=button_num&width=400&height=30"
                allowtransparency="true" border="0" style="width: 120px; height: 24px; border: medium none; overflow: hidden;"
                scrolling="no" frameborder="0"></iframe>
        </span>
    </div>
    <div class="intro" style="text-align:center;">
        <!-- 广告位:普通内容页C1 -->
        <script type="text/javascript">
            BAIDU_CLB_fillSlot("79358");
        </script>
    </div>
    <div class="content">
        <table width='100%'>
            <tr>
                <td>
                    <p>嗨,马里奥老兄,停下你不止息的奔跑与跳跃吧,让金币先在天空闪亮一会儿,让蘑菇先在城墙里躲藏片刻。找块软软的草地悠闲地躺下,对着蓝天白云读读我的来信怎么样?</p>
                    <p>我明白这对你来说或许很新奇,毕竟已经这个年代了,没有谁还执着于用信来交流的形式。</p>
                    <p>在我小时候,要与你相遇是很麻烦的事情。首先要有一台体积不算小的游戏机,还要有一个像是录音带般大小的游戏卡带,将卡带插进游戏机里再连接上电视,我才能看到你那令人魂牵梦萦的敏捷身影。但是我们团聚的时间总是如此短暂,还没等我沉浸在幽黑的水管世界多久,甚至还没有来得及跟随你一起跳过火焰海、躲过长长的旋转不停的火焰棒将公主救出来,我那比库克大魔王还凶猛的妈妈就会大吼着拆散我们俩,并指着钟表上的时间对我怒目而视。</p>
                    <div style="text-align: center;">
                        <img src="http://uploads-images.b0.upaiyun.com/uploads/allimg/160330/1_160330214104_1.jpg" width="520" border="0" height="376"
                            alt="写给超级马里奥的信" />
                    </div>
                    <p>回想那时候为了沉浸入你那神奇的世界,我做了多少练习题,又缠着父母撒了多少娇,简直就无法统计。有人说,求之不得才会念念不忘。我想说这句话的一定是个哲人,要不就是心理学家,不然我怎么会在高中、大学、工作之后仍向往着你的世界,就算是已过而立之年也不放弃对你的执着。</p>
                    <p>幸好,现在要与你团聚早已不像过去那般费劲,不需要有庞大的游戏机和卡带,我只要打开电脑或者滑开手机,就能跟你一起爬上升起的豆藤,在云端漫步。也能跟你潜入海洋,一边躲过不怀好意缓缓游来的章鱼,一边上下浮沉着揽入金币。而且你的世界越来越广阔,你在雪山上攀爬、在赛车道里奔驰、在银河中畅游,简直让我目眩神迷。但我仍旧不满足,都说这世界上最远的距离,是我在你身边,你却不知道我爱你。可对于我来说,这句话应该变成&ldquo;我爱你,却没有办法跟你站在一起&rdquo;。可恶的维度世界隔开了咱们,我无力冲破二维与三维的藩篱。</p>
                    <p>不过幸运的是,或许这事儿也不是完全没有指望。科技在发展,现实世界与数据宇宙的边界正在模糊。如果你注意到我们外界的新闻,就会发现当初坐在游戏机前的那些孩子已经变得与数据融为一体。手机就是我们延伸出去的另一个新的肢体,我们在网络上分享自己的生活,对着陌生人倾吐心声,入睡前双眼的最后一个视像是手机的屏幕,醒来后的第一个动作是滑开手机给朋友圈点赞。我们的一半行走于现实世界,却有另一半沉醉于虚拟空间。</p>
                    <p>我不知道这是好事还是坏事,毕竟我也是其中的一员。毕竟以我的浅薄之见,虚拟世界只会随着科技的发展而愈加融入日常生活,维度的距离不再是困扰物理学家的宇宙性难题,而人类的数据化指日可待。</p>
                    <div class="ads_croarc">
                        <script type="text/javascript">
                            BAIDU_CLB_fillSlot("361813");
                        </script>
                    </div>
                    <p>到了那一天,或许我只要启动植入神经的芯片,一段脑电波就会化为二进制的数据如长龙般急速奔入你的世界。然后等我一睁眼,就会发现自己站在蓝天绿地之间,眼前是竖起的水管与会飞的乌龟,你在我旁边,穿着那套标志性的背带裤对着我微笑。我们会肩并肩一起腾挪跳跃;我会感受到坏蘑菇在我的脚下吧唧一声被踩成扁片;会大笑着冲向一排排金币;会在乘坐云朵电梯时尖叫着抱紧你的腰,因为我恐高;最终我会跟你一起仰起头,目瞪口呆地注视着山一样的库克大魔王,看着他迈着步地动山摇而来,然后充满恐惧却坚定地打败他,为了我那早就想要一睹芳容的绝色公主。</p>
                    <p>另外悄悄问一句,你愿意把一关才有一个的加命蘑菇让给我吗?毕竟从小时候到现在,我的通关技术都实在是不咋样啊&hellip;&hellip;</p>
                    <p style="display:none;">&nbsp;</p>
                    <p>
                        <a href="http://leduapp.duapp.com/download.html?uri=down" target="_blank" title="乐读APP">点击下载__乐读APP(Android)__</a>每天更新好文章</p>
                    <p>
                        <a href="http://leduapp.duapp.com/download.html?uri=down" target="_blank" title="乐读APP">
                            <img src="http://geili365.b0.upaiyun.com/leduapp/ad_itembody.png" width="450" height="200" alt="乐读APP二维码">
                        </a>
                    </p>
                </td>
            </tr>
        </table>
    </div>
    <!-- /content -->
    <div class="qqdigg">
        <div class="qqdigg_con">
            <a version="1.0" class="qzOpenerDiv" href="http://sns.qzone.qq.com/cgi-bin/qzshare/cgi_qzshare_likeurl" target="_blank">赞</a>
            <script src="http://qzonestyle.gtimg.cn/qzone/app/qzlike/qzopensl.js#jsdate=20110603&style=3&showcount=1&width=130&height=30"
                charset="utf-8" defer="defer"></script>
        </div>
        <div class="arc_editer">
            (责任编辑:Mickey)
        </div>
    </div>
</div>

同理我们可以获取时间、来源、作者、文章标题和文章内容。

不多说,我们看一下具体怎么做
首先安装需要的模块,大致如下

"babel-polyfill": "^6.26.0",
"cheerio": "^1.0.0-rc.1",
"ejs": "~2.5.6",
"express": "~4.15.2",
"iconv-lite": "^0.4.17",
"mysql": "^2.13.0",
"promise-mysql": "^3.0.1",
"request-promise": "^4.2.1"

其中 cheerio 是用来抓取网页数据的,iconv-lite 用来解决中文乱码, request-promise 是一个异步请求模块,跟fetch的差不多,只不过是用于服务端。

babel-polyfill 用来编译 es6

index.js

require('babel-polyfill');
const express = require('express');
const router = express.Router();
const conn = require('../server/db_connection');
const fs = require('fs');
//爬取新闻
const cheerio = require('cheerio');
const request = require('request-promise');
const iconv = require('iconv-lite');

let delayTime = 1000; //每轮间隔的时间
let currentUrl = 0; //成功写入的文章数数目
let noImgNum = 0; //剔除的没有图片的文章数目
let damagedArticle = 0; //无效链接文章数目
let noList = 0; //无效的链接列表数目
let writerFail = 0; //写入数据库失败的文章数目


router.getArticleUrl = async (req, res, next)=>{
    let startTime = getNow();
    let start_page = 1;
    let end_page = 2;
    //getListPage(start_page, end_page, "校园"); 有兴趣的话里面的参数可以配置成客户端传进来,那样就可以动态爬取,用req.query来获取,
    let listPages = getListPage(start_page, end_page);
    let listUrlsArr = await asyncControl(listPages, getListUrl, 10, delayTime);
    let listUrls = listUrlsArr.reduce((a,b)=>a.concat(b));
    let listLen = listUrls.length;
    console.log('爬取列表页完成,共有文章:'+ listLen );
    let PageContent = await asyncControl(listUrls, getPageContent, 10, delayTime);
    let endTime = getNow();
    console.log('开始时间:' + startTime);
    console.log('结束时间:' + endTime);
    console.log('原有数据:' + ' ' + listLen);
    console.log('剔除没有图片或者获取图片失败的文章数:' + ' ' + noImgNum);
    console.log('文章获取失败数:' + ' ' + damagedArticle);
    console.log('文章写入数据库失败数:' + ' ' + writerFail);
    console.log('共爬取有效文章数:' + ' ' + currentUrl);
    console.log('爬虫结束');
    res.send('爬虫结束');
}

//取得所有的列表页
/**
* 
* @param {*} start_page 起始页面
* @param {*} end_page 结束页面
* @param {*} type 爬取的类型
*/
const getListPage = (start_page, end_page, type)=>{
    let listPages = [];
    let typeArray = [
        {
            url_type: 'redu',
            type: '热读',
            type_id: 1,
            start_page,
            end_page
        },
        {
            url_type: 'wenyuan',
            type: '文苑',
            type_id: 2,
            start_page,
            end_page
        },
        {
            url_type: 'qinggan',
            type: '情感',
            type_id: 3,
            start_page,
            end_page
        },
    {
            url_type: 'shehui',
            type: '社会',
            type_id: 4,
            start_page,
            end_page
        },
        {
            url_type: 'shenghuo',
            type: '生活',
            type_id: 5,
            start_page,
            end_page
        },
        {
            url_type: 'rensheng',
            type: '人生',
            type_id: 6,
            start_page,
            end_page
        },
        {
            url_type: 'renwu',
            type: '人物',
            type_id: 7,
            start_page,
            end_page
        },
        lizhi = {
            url_type: 'lizhi',
            type: '励志',
            type_id: 8,
            start_page,
            end_page
        },
        {
            url_type: 'shiye',
            type: '视野',
            type_id: 9,
            start_page,
            end_page
        },
        {
            url_type: 'xinling',
            type: '心灵',
            type_id: 10,
            start_page,
            end_page
        },
        {
            url_type: 'xiaoyuan',
            type: '校园',
            type_id: 11,
            start_page,
            end_page
        },
        {
            url_type: 'zhichang',
            type: '职场',
            type_id: 12,
            start_page,
            end_page
        }
    ];
    if(type){
        let match = typeArray.filter(item=>item.type === type);
        if(match.length) {
            let {url_type, type, type_id } = match[0];
            for(let i = start_page; i< end_page; i++){
                listPages.push({
                    url_type,
                    type,
                    link: `http://www.ledu365.com/${url_type}/list_${type_id}_${i}.html`
                })
            }
            return listPages;
        }
    }
    for (let n = 0; n < typeArray.length; n++) {
        for (let i = typeArray[n].start_page; i < typeArray[n].end_page; i++) {
            let url_type = typeArray[n].url_type;
            let type_id = typeArray[n].type_id;
            let type = typeArray[n].type;
            listPages.push({
                url_type,
                type,
                link: `http://www.ledu365.com/${url_type}/list_${type_id}_${i}.html`
            })
        }
    }
    return listPages;
}


//取得所有的列表页的所有列表链接
const getListUrl = async (liItem, count)=>{
    //爬取文章链接
    let articleUrl = [];
    let listOption = {
        url: liItem.link,
        headers: {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8',
        },
        encoding: null
    }
    let result = null;
    try {
        result = await request(listOption);
    } catch (error) {
        ++noList;
        console.log('错误链接列表地址:'+ liItem.link );
        console.log('错误链接列表:'+noList );
        return false;
    }
    console.log('请求列表页成功')
    console.log('数目:' +( count+1))
    let body = iconv.decode(result, 'gb2312');
    let $ = cheerio.load(body, {
        decodeEntities: false
    });
    let links = $('.listbox .title')
    links.map(function (index, item) {
        if(!$('.listbox .preview img')[index]){
            return false;
        }
        let imgSrc = $('.listbox .preview img')[index].attribs.src || '';
        if (!imgSrc) {
            ++noImgNum;
            return false;
        }
        if (imgSrc.indexOf('http') == -1) {
            imgSrc = 'http://www.ledu365.com' + imgSrc;
        }
        articleUrl.push({
            link:$('.listbox .title')[index].attribs.href,
            type: liItem.type,
            url_type: liItem.url_type,
            imgSrc
        });
        return true;
    })
    return articleUrl;
}

//取得所有的文章
const getPageContent = async(article, count)=>{
    let articleOption = {
        url: 'http://www.ledu365.com' + article.link,
        headers: {
            'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrom' +
                    'e/58.0.3029.110 Safari/537.36',
            'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'
        },
        encoding: null
    }
    //爬取文章
    let articleBody = null;
    try {
        articleBody = await request(articleOption);
    } catch (error) {
        ++damagedArticle;
        return false;
    }
    let body = iconv.decode(articleBody, 'gb2312');
    let $ = cheerio.load(body, {decodeEntities: false});
    let title = $('.title h2').text().trim();
    let info = $('.info').text();
    let pArray = $('p').toArray()
    let splitArray = info.split(' ');
    let date = '';
    let source = '';
    let writer = '';
    let type = article.type;
    let url_type = article.url_type;
    let cutImgUrl = article.imgSrc;
    console.log( `当前进行:${++count}, 文章:${title} `)
    if (splitArray[0] && splitArray[0].indexOf('时间:') != -1) {
        date = splitArray[0].split('时间:')[1]
    }
    if (splitArray[1] && splitArray[1].indexOf('来源:') != -1) {
        source = splitArray[1].split('来源:')[1]
    }
    if (splitArray[2] && splitArray[2].indexOf('作者:') != -1) {
        writer = splitArray[2].split('作者:')[1]
    }

    let summary = $('.content p').text();
    let contenText = '';

    $('.content p').each(function (index, item) {
        let trimText = $(this).text().trim();
        if (trimText != '' && (trimText != ('点击下载__乐读APP(Android)__每天更新好文章'))) {
            contenText += `<p>${trimText}</p>`;
        }
    })

    if (!cutImgUrl) {
        ++noImgNum;
        return false;
    }
    let cutExtName = ('.' + cutImgUrl.substr(-10).split('.')[1]).replace('!cut', '');
    let cutImgSrc = url_type + '_' + new Date().getTime().toString()  + '!cut' + cutExtName;
    let folder_exists = fs.existsSync('public/images/cut/');
    if(!folder_exists) fs.mkdirSync('public/images/cut/');
    let cutOutResult = await writeImg(cutImgUrl, 'cut/'+cutImgSrc);
    if(!cutOutResult) {
        ++noImgNum;
        return false;
    }


    let imgSrc = $('.content div img').attr('src') || '';
    if (!imgSrc) {
        ++noImgNum;
        return false;
    }
    if (imgSrc.indexOf('http') == -1) {
        imgSrc = 'http://www.ledu365.com' + imgSrc;
    }
    let extName = imgSrc.substr(-4);
    let downImgSrc = url_type + '_' + new Date().getTime().toString()  + extName;
    let imgOutResult = await writeImg(imgSrc, downImgSrc);
    if(!imgOutResult) {
        ++noImgNum;
        return false;
    }

    //写入数据库
    let sql = `INSERT INTO article(article_title ,article_date ,article_source ,article_writer ,article_img, article_content, article_type, article_url_type, article_summary,article_cutImg) VALUES(${conn.escape(title)},${conn.escape(date)},${conn.escape(source)},${conn.escape(writer)},${conn.escape(downImgSrc)},${conn.escape(contenText)},${conn.escape(type)},${conn.escape(url_type)},${conn.escape(summary)},${conn.escape(cutImgSrc)})`;

    let insertRes = null;
    try {
        insertRes = await conn.query(sql);
        console.log('写入数据库:' + (++currentUrl));
    } catch (error) {
        console.log("写入数据库:失败:" + err);
        ++writerFail;
        return false;
    }
}

//写入图片
const writeImg = async(url, name)=>{
    let imgHeader = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrom' +
                'e/58.0.3029.110 Safari/537.36',
        'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'
    }
    let cutImgOption = {
        url: url,
        headers: imgHeader,
        encoding: null
    }
    let cutResponse = null;
    try {
        cutResponse = await request(cutImgOption);
    } catch (error) {
        console.log('读取图片失败' ,error)
        return false;
    }
let cutOutResult = null;
try {
        let cutOut = fs.createWriteStream('public/images/' + name);
        cutOut.write(cutResponse);
        cutOutResult = await new Promise(resolve=>{
            cutOut.end('写入完成',  ()=> {
                if (cutOut.bytesWritten < 1) {
                    fs.unlink('public/images/' + name);
                    return resolve(false)
                }
                return resolve(true)
            });
        })
    } catch (error) {
        console.log('写入图片失败' ,error)
        return false;
    }
    return true;
}

//获取当前时间
const getNow = () => {
    let now = new Date().getHours() + '时' + new Date().getMinutes() + '分' + new Date().getSeconds() + '秒';
    return now;
}
/**
*
* @param {*} arr 传入需要控制的数组
* @param {*} todo 单个元素需要执行的函数 function(item, index)
* @param {*} limit 并发数
* @param {*} delay 每轮并发的间隔时间
*/
const asyncControl = async(arr, todo, limit, delay) => {
    let count = arr.length
    let results = []
    let fn = async(arr, todo, limit, delay, results) => {
        if (!arr.length) 
            return results;
        let current = arr.splice(0, limit);
        let itemResults = await Promise.all(current.map((item, index) => todo(item, count - arr.length - (current.length - 1 - index) - 1)))
        results = results.concat(itemResults.filter(item => !!item));
        if (arr.length) {
            await new Promise(resolve => setTimeout(() => resolve(true), delay));
            return fn(arr, todo, limit, delay, results);
        } else {
            return results;
        }
    }
    return fn(arr, todo, limit, delay, results);
}


module.exports = router;

db_connection.js

require('babel-polyfill');
const mysql = require('mysql');
const pool  = mysql.createPool({
connectionLimit : 10,
host: 'localhost',
port: 3306,
user: 'root',
password : '',
database: 'newsdemo',
});

const query = (sql)=>{
return new Promise((resolve, reject)=>{
    return pool.query(sql, (error, results, fields)=>{
    if(error)  resolve(error, fields);
    resolve(results, fields);
    })
})
}

module.exports.query = query;
module.exports.escape = mysql.escape;

article 表

图片描述

github地址 https://github.com/m-Ryan/an_...

git clone git@github.com:m-Ryan/an_example_of_a_reptile.git
运行:
npm install
npm start
打开网页 http://localhost:3001/

图片描述

图片描述


Ryan
91 声望3 粉丝

引用和评论

0 条评论