序
打算写个关于node的爬虫菜鸟教程,由于第一次写文章, 请各位多多指教,给我一些意见。接下来将带大家一步一步写一个表情包爬虫,从获取页面,解析表情包链接, 清洗脏数据,下载表情包到本地。开始之前你需要有对chrome调试工具和ES6有一定了解,包括
async/await
的使用。
获取页面地址
我们打开 U表情搜索地址,如下图
我们可以看到 http://www.ubiaoqing.com/search/
加上关键字
, 就是完整的搜索结果地址。
准备工作
你需要先安装 node7.x
以上版本,如果没装请自行下载安装,这里就不做说明了。
我们新建一个js文件 memes.js
和存放表情包的文件夹 memes
;
接下来安装我们需要的模块
npm install cheerio --save
npm install superagent --save
cheerio node版的jQuery,用法和jQuery一样。中文资料
superagent 一个轻量的、渐进式的请求模块。中文资料
正式开始
我们先请求地址拿到HTML
'use strict'
const request = require('superagent');
const cheerio = require('cheerio');
const SEARCH_URL = 'http://www.ubiaoqing.com/search/';
const keyword = '单身狗';
let page = 1;
let linkAssemble = []; // 链接集合
function requestURL(keyword, page) {
let url = `${ SEARCH_URL }${ encodeURI(keyword) }/${ page }`; // 抓取地址
return request.get(url).then(res => res.text);
}
我们可以看到表情包的链接在 li
下 class
为 ver-middle
的 div
-> a
-> img
的href
属性,用jQuery选择器即为div.ver-middle>a
;
我们要拿到所有 li
下的表情包
function selectLink (html) {
let $ = cheerio.load(html); // 加载html到cheerio
// 遍历所有的标签并获取href属性
return Array.from($('li .ver-middle').map(function () {
return $(this).find('img').attr('src');
}))
}
解析后返回的数据如下:
[
"http://ubq.ubiaoqing.com/ubiaoqing18891b279231433893c19bc0a7507f5a.jpg",
"http://ubq.ubiaoqing.com/ubiaoqingb8b5240336c953316b99d3e4963b13b6.jpg",
"http://ubq.ubiaoqing.com/ubiaoqing50ff2027dd2e0b257da02fe6cb364ea5.gif",
"http://ubq.ubiaoqing.com/ubiaoqing1ad82a6cf881cd0205765b69a5073188.jpg",
"http://ubq.ubiaoqing.com/ubiaoqing57e7ad299029e31406.gif",
"http://ubq.ubiaoqing.com/ubiaoqingcadd8ff3468c4c03a052e55b1a7ad825.gif",
"https://img.alicdn.com/imgextra/i2/3161190279/TB2kn9rdMRkpuFjy1zeXXc.6FXa_!!3161190279.jpg",
"http://ubq.ubiaoqing.com/ubiaoqing7a8e3432684650ab2e12bf3a234e4203.jpg",
"http://file.ubiaoqing.com/mp-weixin.jpg"
]
但是我们取到的数据并不干净,中间包括了一些广告,这显然不符合我们的期望,我们需要过滤掉不包含 http://ubq.ubiaoqing.com/ubiaoqing
前缀的链接
function cleanseLink (links) {
return links.filter((link) => link.includes('http://ubq.ubiaoqing.com/ubiaoqing'));
}
清洗后返回
[
"http://ubq.ubiaoqing.com/ubiaoqing18891b279231433893c19bc0a7507f5a.jpg",
"http://ubq.ubiaoqing.com/ubiaoqingb8b5240336c953316b99d3e4963b13b6.jpg",
"http://ubq.ubiaoqing.com/ubiaoqing50ff2027dd2e0b257da02fe6cb364ea5.gif",
"http://ubq.ubiaoqing.com/ubiaoqing1ad82a6cf881cd0205765b69a5073188.jpg",
"http://ubq.ubiaoqing.com/ubiaoqing57e7ad299029e31406.gif",
"http://ubq.ubiaoqing.com/ubiaoqingcadd8ff3468c4c03a052e55b1a7ad825.gif",
"http://ubq.ubiaoqing.com/ubiaoqing7a8e3432684650ab2e12bf3a234e4203.jpg",
]
我们把整个流程整理下
async function getLinksByPage (keyword, page) {
// step1 获取页面
let html = await requestURL(keyword, page);
// step2 解析数据
selectLink(html);
// step3 清洗脏数据
cleanseLink(html);
}
接着我们要递归获所有页面的表情包
我们翻到最后一页可以看到他是没有 下一页
的按钮的, 我们可以根据这个条件来判断是否是最后一页
async function getLinksByPage (keyword, page) {
try {
// step1 获取页面
console.log(`获取页面 -> 关键字: ${keyword} 第${page}页`);
let html = await requestURL(keyword, page);
// step2 解析数据
console.log(`解析数据...`);
let links = selectLink(html);
// step3 清洗脏数据
console.log('清洗数据...');
let result = cleanseLink(links);
// 将结果添加到linksAssemble
Array.prototype.push.apply(linkAssemble, result);
// 如果有下一页继续抓取下页表情包链接
if ( html.includes('下一页') ) {
return getLinksByPage(keyword, ++ page);
}
console.log(linkAssemble);
return linkAssemble;
} catch(err) {
console.error(err.message);
// 错误则跳过当前页,继续抓取!
return getLinksByPage(keyword, ++ page);
}
}
运行测试
然后我们来运行下,由于使用了 async/await
我们运行时需要加 --harmony
参数
node --harmony memes.js