用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>幸好,现在要与你团聚早已不像过去那般费劲,不需要有庞大的游戏机和卡带,我只要打开电脑或者滑开手机,就能跟你一起爬上升起的豆藤,在云端漫步。也能跟你潜入海洋,一边躲过不怀好意缓缓游来的章鱼,一边上下浮沉着揽入金币。而且你的世界越来越广阔,你在雪山上攀爬、在赛车道里奔驰、在银河中畅游,简直让我目眩神迷。但我仍旧不满足,都说这世界上最远的距离,是我在你身边,你却不知道我爱你。可对于我来说,这句话应该变成“我爱你,却没有办法跟你站在一起”。可恶的维度世界隔开了咱们,我无力冲破二维与三维的藩篱。</p>
<p>不过幸运的是,或许这事儿也不是完全没有指望。科技在发展,现实世界与数据宇宙的边界正在模糊。如果你注意到我们外界的新闻,就会发现当初坐在游戏机前的那些孩子已经变得与数据融为一体。手机就是我们延伸出去的另一个新的肢体,我们在网络上分享自己的生活,对着陌生人倾吐心声,入睡前双眼的最后一个视像是手机的屏幕,醒来后的第一个动作是滑开手机给朋友圈点赞。我们的一半行走于现实世界,却有另一半沉醉于虚拟空间。</p>
<p>我不知道这是好事还是坏事,毕竟我也是其中的一员。毕竟以我的浅薄之见,虚拟世界只会随着科技的发展而愈加融入日常生活,维度的距离不再是困扰物理学家的宇宙性难题,而人类的数据化指日可待。</p>
<div class="ads_croarc">
<script type="text/javascript">
BAIDU_CLB_fillSlot("361813");
</script>
</div>
<p>到了那一天,或许我只要启动植入神经的芯片,一段脑电波就会化为二进制的数据如长龙般急速奔入你的世界。然后等我一睁眼,就会发现自己站在蓝天绿地之间,眼前是竖起的水管与会飞的乌龟,你在我旁边,穿着那套标志性的背带裤对着我微笑。我们会肩并肩一起腾挪跳跃;我会感受到坏蘑菇在我的脚下吧唧一声被踩成扁片;会大笑着冲向一排排金币;会在乘坐云朵电梯时尖叫着抱紧你的腰,因为我恐高;最终我会跟你一起仰起头,目瞪口呆地注视着山一样的库克大魔王,看着他迈着步地动山摇而来,然后充满恐惧却坚定地打败他,为了我那早就想要一睹芳容的绝色公主。</p>
<p>另外悄悄问一句,你愿意把一关才有一个的加命蘑菇让给我吗?毕竟从小时候到现在,我的通关技术都实在是不咋样啊……</p>
<p style="display:none;"> </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/
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。