python爬表情包_【从零开始写爬虫一】批量下载表情包

打算写个关于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

至此第一部分代码已经完成了,完整代码如下: github

'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 = []; // 链接集合

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);

}

return linkAssemble;

} catch(err) {

// 错误则跳过当前页,继续抓取!

console.error(err.message);

return getLinksByPage(keyword, ++ page);

}

}

getLinksByPage(keyword, page);

/**

* @requestURL

* @param keyword {String} 搜索关键字

* @param page {String} 页数

* @return request

* */

function requestURL(keyword, page) {

let url = `${ SEARCH_URL }${ encodeURI(keyword) }/${ page }`; // 抓取地址

return request.get(url).then(res => res.text);

}

/**

* @requestURL

* @param html {String} 待解析的html

* @return links {Array}

* */

function selectLink (html) {

let $ = cheerio.load(html); // 加载html到cheerio

// 遍历所有的标签并获取href属性

return Array.from($('li .ver-middle').map(function () {

return $(this).find('img').attr('src');

}))

}

/**

* @cleanseLink

* @param links {Array} 待清洗的link

* @return links {Array}

* */

function cleanseLink (links) {

return links.filter((link) => link.includes('http://ubq.ubiaoqing.com/ubiaoqing'));

}

第一部分我们已经完成了表情包链接的获取,接下来我们开始批量下载表情包到本地。

下载表情包到本地

观察表情包地址我们发现表情包后面22位就是它完整且唯一的文件名。

我们首先判断本地是否存在这个文件,如果存在则跳过下载,如果不存在,

我们就创建一个可写的文件 stream ,然后请求表情包地址,并 pipe 到 stream,

监听 close 事件,触发时完成Promise。

function downloadMeMe (url) {

console.log(`下载: ${url}`);

let filePath = `./memes/${url.substr(-22)}`; // 取到后22位作为文件名

let stream = fs.createWriteStream(filePath); // 创建一个可写 stream 对象

// 请求表情包地址,并 pipe 到刚才创建的 stream 对象

request.get(url).pipe(stream);

}

限流器

假设我们打开表情包页面,他会同时请求一整页的表情包,所以我们只需要限制批量请求之间的间隔就好。

写个限流器,控制单次请求数,访问频率过快会导致爬虫被发现。你也可以设置随机延时。

function timerChunk(any, fn, limit, wait = 0) {

let run = async function () {

if (!any.length) {

return;

}

// 延时等待 这里是随机0到wait毫秒

await (new Promise((resolve, reject) => setTimeout(resolve, ~~(Math.random() * wait))));

let params = any.splice(0, limit); // 每次取出 limit 数量的任务

params.forEach((param) => fn(param));

return run();

}

return run();

}

组装函数

最后步骤就是搭积木把函数拼起来

(async function crawler() {

let keyword = '单身狗';

try {

// 获取该关键字所有的表情包链接

let links = await getLinksByPage(keyword, 1);

// 下载表情包到本地

await timerChunk(links, downloadMeMe, 5, 3000);

console.log('完成!');

} catch (err) {

console.error(err);

}

})();

我们来运行下我们的项目

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值