├── .gitignore ├── img ├── img1.png ├── img2.png ├── img3.png └── main.png ├── package.json ├── app.js ├── README.MD └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | Data 3 | .vscode 4 | img-* 5 | json-* 6 | -------------------------------------------------------------------------------- /img/img1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nieheyong/HanhandeSpider/HEAD/img/img1.png -------------------------------------------------------------------------------- /img/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nieheyong/HanhandeSpider/HEAD/img/img2.png -------------------------------------------------------------------------------- /img/img3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nieheyong/HanhandeSpider/HEAD/img/img3.png -------------------------------------------------------------------------------- /img/main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nieheyong/HanhandeSpider/HEAD/img/main.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hanhandespider", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "app.js", 6 | "author": "nie", 7 | "license": "", 8 | "repository": {}, 9 | "scripts": { 10 | "start": "node app.js" 11 | }, 12 | "dependencies": { 13 | "async": "~2.1.4", 14 | "cheerio": "^0.22.0", 15 | "superagent": "^3.4.1", 16 | "superagent-charset": "^1.1.1" 17 | } 18 | } -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | let fs = require("fs"); 3 | let cheerio = require('cheerio'); 4 | let asyncQuene = require("async").queue; 5 | let request = require('superagent'); 6 | require('superagent-charset')(request); 7 | 8 | const Config = { 9 | startPage: 1, //开始页码 10 | endPage: 1, //结束页码,不能大于当前图片类型总页码 11 | downloadImg: true, //是否下载图片到硬盘,否则只保存Json信息到文件 12 | downloadConcurrent: 10, //下载图片最大并发数 13 | currentImgType: "scy" //当前程序要爬取得图片类型,取下面AllImgType的Key。 14 | }; 15 | 16 | const AllImgType = { //网站的图片类型 17 | ecy: "http://tu.hanhande.com/ecy/ecy_", //二次元 总页码: 50 18 | scy: "http://tu.hanhande.com/scy/scy_", //三次元 总页码: 64 19 | cos: "http://tu.hanhande.com/cos/cos_", //cosPlay 总页码: 20 20 | }; 21 | 22 | let getHtmlAsync = function (url) { 23 | return new Promise(function (resolve, reject) { 24 | request.get(url).charset('gbk').end(function (err, res) { 25 | err ? reject(err) : resolve(cheerio.load(res.text)); 26 | }); 27 | }); 28 | } 29 | 30 | let getAlbumsAsync = function () { 31 | return new Promise(function (resolve, reject) { 32 | console.log('Start get albums .....'); 33 | let albums = []; 34 | let q = asyncQuene(async function (url, taskDone) { 35 | try { 36 | let $ = await getHtmlAsync(url); 37 | console.log(`download ${url} success`); 38 | $('.picList em a').each(function (idx, element) { 39 | albums.push({ 40 | title: element.children[1].attribs.alt, 41 | url: element.attribs.href, 42 | imgList: [] 43 | }); 44 | }); 45 | } catch (err) { 46 | console.log(`Error : get Album list - download ${url} err : ${err}`); 47 | } 48 | finally { 49 | taskDone();// 一次任务结束 50 | } 51 | }, 10);//html下载并发数设为10 52 | /** 53 | * 监听:当所有任务都执行完以后,将调用该函数 54 | */ 55 | q.drain = function () { 56 | console.log('Get album list complete'); 57 | resolve(albums);//返回所有画册 58 | } 59 | 60 | let pageUrls = []; 61 | let imageTypeUrl = AllImgType[Config.currentImgType]; 62 | for (let i = Config.startPage; i <= Config.endPage; i++) { 63 | pageUrls.push(imageTypeUrl + `${i}.shtml`); 64 | } 65 | q.push(pageUrls); 66 | } 67 | ); 68 | } 69 | 70 | let getImageListAsync = function (albumsList) { 71 | return new Promise(function (resolve, reject) { 72 | console.log('Start get album`s imgList ....'); 73 | let q = asyncQuene(async function ({ url: albumUrl, title: albumTitle, imgList }, taskDone) { 74 | try { 75 | let $ = await getHtmlAsync(albumUrl); 76 | console.log(`get album ${albumTitle} image list done`); 77 | $('#picLists img').each(function (idx, element) { 78 | imgList.push(element.attribs.src); 79 | }); 80 | } catch (err) { 81 | console.log(`Error :get image list - download ${albumUrl} err : ${err}`); 82 | } 83 | finally { 84 | taskDone();// 一次任务结束 85 | } 86 | }, 10);//html下载并发数设为10 87 | /** 88 | * 监听:当所有任务都执行完以后,将调用该函数 89 | */ 90 | q.drain = function () { 91 | console.log('Get image list complete'); 92 | resolve(albumsList); 93 | } 94 | 95 | //将所有任务加入队列 96 | q.push(albumsList); 97 | }); 98 | } 99 | 100 | function writeJsonToFile(albumList) { 101 | let folder = `json-${Config.currentImgType}-${Config.startPage}-${Config.endPage}` 102 | fs.mkdirSync(folder); 103 | let filePath = `./${folder}/${Config.currentImgType}-${Config.startPage}-${Config.endPage}.json`; 104 | fs.writeFileSync(filePath, JSON.stringify(albumList)); 105 | } 106 | 107 | function downloadImg(albumList) { 108 | console.log('Start download album`s image ....'); 109 | const folder = `img-${Config.currentImgType}-${Config.startPage}-${Config.endPage}`; 110 | fs.mkdirSync(folder); 111 | let downloadCount = 0; 112 | let q = asyncQuene(async function ({ title: albumTile, url: imageUrl }, taskDone) { 113 | request.get(imageUrl).end(function (err, res) { 114 | if (err) { 115 | console.log(err); 116 | taskDone(); 117 | } else { 118 | fs.writeFile(`./${folder}/${albumTile}-${++downloadCount}.jpg`, res.body, function (err) { 119 | err ? console.log(err) : console.log(`${albumTile}保存一张`); 120 | taskDone(); 121 | }); 122 | } 123 | }); 124 | }, Config.downloadConcurrent); 125 | /** 126 | * 监听:当所有任务都执行完以后,将调用该函数 127 | */ 128 | q.drain = function () { 129 | console.log('All img download'); 130 | } 131 | 132 | let imgListTemp = []; 133 | albumList.forEach(function ({ title, imgList }) { 134 | imgList.forEach(function (url) { 135 | imgListTemp.push({ title: title, url: url }); 136 | }); 137 | }); 138 | q.push(imgListTemp);//将所有任务加入队列 139 | } 140 | 141 | async function spiderRun() { 142 | let albumList = await getAlbumsAsync();//获取所有画册URL 143 | albumList = await getImageListAsync(albumList);//根据画册URL获取画册里的所有图片URL 144 | writeJsonToFile(albumList);//将画册信息保存为JSON 145 | if (Config.downloadImg) { 146 | downloadImg(albumList);//下载画册里面的所有图片 147 | } 148 | } 149 | 150 | spiderRun(); 151 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | #### 注意! 爬取目标网站 [http://tu.hanhande.com](http://tu.hanhande.com) 已经失效,打开提示"因版权问题我们已删除内容,感谢大家一直以来对我们的支持" 。 2 | --- 3 | 4 | # 运行 5 | #### 由于使用了Async Await,需要Node.js 7.6及以上版本! 6 | #### 需要Node.js 7.6及以上版本! 7 | #### 需要Node.js 7.6及以上版本! 8 | ``` 9 | $ git clone https://github.com/nieheyong/hanhandeSpider.git 10 | $ cd hanhandeSpider 11 | $ npm install 12 | $ npm start 13 | ``` 14 | 15 | # 为什么有它 16 | 17 | 那天晚上,打完LOL后,电脑右下角弹出了一个小框:"超越完美比例的诱惑 LOL大尺度同人手绘" 18 | 。年轻气盛的我点进去一口气看了十几个图册后,觉得一个页面只能看一张不过瘾,于是就有了它! 19 | ![All image](img/main.png) 20 | 21 | 22 | # 使用的库 23 | ```javascript 24 | "dependencies": { 25 | "async": "^2.1.4",//并发控制库 26 | "cheerio": "^0.22.0",//node.js端的jquery 27 | "superagent": "^3.4.1",//http请求下载 28 | "superagent-charset": "^1.1.1"//superagent GBK编码支持 29 | } 30 | ``` 31 | 32 | # 实现过程 33 | 34 | 35 | ### 1 : 页码分析 36 | 以三次元图片为例,首先分析页码结构。图册的页码非常有规律,例如第2页,URL是:[http://tu.hanhande.com/scy/scy_2.shtml](http://tu.hanhande.com/scy/scy_2.shtml)(点走了的别忘了回来看代码),只要改变数字就是不同的页码。以下还有二次元和Cosplay的页码前缀信息 37 | ```javascript 38 | const AllImgType = { //网站的图片类型 39 | ecy: "http://tu.hanhande.com/ecy/ecy_", //二次元 总页码: 50 40 | scy: "http://tu.hanhande.com/scy/scy_", //三次元 总页码: 64 41 | cos: "http://tu.hanhande.com/cos/cos_", //cosPlay 总页码: 20 42 | }; 43 | ``` 44 | ![All image](img/img1.png) 45 | ### 2 : 图册获取 46 | 获取页码之后开始获取每个图册的URL,每页的所有图册都在 class 为 .picList的ul元素里面的li元素里面。图册地址是a标签的Herf属性,图册标题是img元素的alt属性。 47 | ![All image](img/img2.png) 48 | 首先准备获取指定Url的HTML内容函数,由于爬取的网站是GBK编码需要所以需要使用指定编码为GBK。 49 | ```javascript 50 | let getHtmlAsync = function (url) { 51 | return new Promise(function (resolve, reject) { 52 | request.get(url).charset('gbk').end(function (err, res) { 53 | err ? reject(err) : resolve(cheerio.load(res.text)); 54 | }); 55 | }); 56 | } 57 | ``` 58 | 以下是获取指定页码范围内所有图册的代码: 59 | ```javascript 60 | let getAlbumsAsync = function () { 61 | return new Promise(function (resolve, reject) { 62 | console.log('Start get albums .....'); 63 | let albums = []; 64 | let q = asyncQuene(async function (url, taskDone) { 65 | try { 66 | let $ = await getHtmlAsync(url); 67 | console.log(`download ${url} success`); 68 | $('.picList em a').each(function (idx, element) { 69 | albums.push({ 70 | title: element.children[1].attribs.alt, 71 | url: element.attribs.href, 72 | imgList: [] 73 | }); 74 | }); 75 | } catch (err) { 76 | console.log(`Error : get Album list - download ${url} err : ${err}`); 77 | } 78 | finally { 79 | taskDone();// 一次任务结束 80 | } 81 | }, 10);//html下载并发数设为10 82 | /** 83 | * 监听:当所有任务都执行完以后,将调用该函数 84 | */ 85 | q.drain = function () { 86 | console.log('Get album list complete'); 87 | resolve(albums);//返回所有画册 88 | } 89 | 90 | let pageUrls = []; 91 | let imageTypeUrl = AllImgType[Config.currentImgType]; 92 | for (let i = Config.startPage; i <= Config.endPage; i++) { 93 | pageUrls.push(imageTypeUrl + `${i}.shtml`); 94 | } 95 | q.push(pageUrls); 96 | } 97 | ); 98 | } 99 | 100 | ``` 101 | ### 3 : 获取图册图片列表 102 | 获取到图册的地址后,需要获取到图册里面所有图片的URL,进入图册URL查看图册,图册里面的所有图片都在ID为picLists的UL元素中,图片的URL为img元素的src属性。 103 | ![All image](img/img3.png) 104 | 以下是获取所有图册的所有图片URL代码: 105 | ```javascript 106 | let getImageListAsync = function (albumsList) { 107 | return new Promise(function (resolve, reject) { 108 | console.log('Start get album`s imgList ....'); 109 | let q = asyncQuene(async function ({ url: albumUrl, title: albumTitle, imgList }, taskDone) { 110 | try { 111 | let $ = await getHtmlAsync(albumUrl); 112 | console.log(`get album ${albumTitle} image list done`); 113 | $('#picLists img').each(function (idx, element) { 114 | imgList.push(element.attribs.src); 115 | }); 116 | } catch (err) { 117 | console.log(`Error :get image list - download ${albumUrl} err : ${err}`); 118 | } 119 | finally { 120 | taskDone();// 一次任务结束 121 | } 122 | }, 10);//html下载并发数设为10 123 | /** 124 | * 监听:当所有任务都执行完以后,将调用该函数 125 | */ 126 | q.drain = function () { 127 | console.log('Get image list complete'); 128 | resolve(albumsList); 129 | } 130 | 131 | //将所有任务加入队列 132 | q.push(albumsList); 133 | }); 134 | } 135 | ``` 136 | ### 4 : 保存图册信息到JSON文件 137 | ```javascript 138 | function writeJsonToFile(albumList) { 139 | let folder = `json-${Config.currentImgType}-${Config.startPage}-${Config.endPage}` 140 | fs.mkdirSync(folder); 141 | let filePath = `./${folder}/${Config.currentImgType}-${Config.startPage}-${Config.endPage}.json`; 142 | fs.writeFileSync(filePath, JSON.stringify(albumList)); 143 | } 144 | ``` 145 | 146 | ### 5 : 下载图片 147 | 当图册包含的图片信息都获取完成后,开始下载图片。 148 | 149 | ```javascript 150 | function downloadImg(albumList) { 151 | console.log('Start download album`s image ....'); 152 | const folder = `img-${Config.currentImgType}-${Config.startPage}-${Config.endPage}`; 153 | fs.mkdirSync(folder); 154 | let downloadCount = 0; 155 | let q = asyncQuene(async function ({ title: albumTile, url: imageUrl }, taskDone) { 156 | request.get(imageUrl).end(function (err, res) { 157 | if (err) { 158 | console.log(err); 159 | taskDone(); 160 | } else { 161 | fs.writeFile(`./${folder}/${albumTile}-${++downloadCount}.jpg`, res.body, function (err) { 162 | err ? console.log(err) : console.log(`${albumTile}保存一张`); 163 | taskDone(); 164 | }); 165 | } 166 | }); 167 | }, Config.downloadConcurrent); 168 | /** 169 | * 监听:当所有任务都执行完以后,将调用该函数 170 | */ 171 | q.drain = function () { 172 | console.log('All img download'); 173 | } 174 | 175 | let imgListTemp = []; 176 | albumList.forEach(function ({ title, imgList }) { 177 | imgList.forEach(function (url) { 178 | imgListTemp.push({ title: title, url: url }); 179 | }); 180 | }); 181 | q.push(imgListTemp);//将所有任务加入队列 182 | } 183 | ``` 184 | 185 | 186 | 187 | [完整代码 https://github.com/nieheyong/hanhandeSpider/blob/master/app.js](https://github.com/nieheyong/hanhandeSpider/blob/master/app.js) 188 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | async@~2.1.4: 6 | version "2.1.5" 7 | resolved "https://registry.yarnpkg.com/async/-/async-2.1.5.tgz#e587c68580994ac67fc56ff86d3ac56bdbe810bc" 8 | dependencies: 9 | lodash "^4.14.0" 10 | 11 | asynckit@^0.4.0: 12 | version "0.4.0" 13 | resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" 14 | 15 | boolbase@~1.0.0: 16 | version "1.0.0" 17 | resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" 18 | 19 | buffer-shims@^1.0.0: 20 | version "1.0.0" 21 | resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" 22 | 23 | cheerio@^0.22.0: 24 | version "0.22.0" 25 | resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" 26 | dependencies: 27 | css-select "~1.2.0" 28 | dom-serializer "~0.1.0" 29 | entities "~1.1.1" 30 | htmlparser2 "^3.9.1" 31 | lodash.assignin "^4.0.9" 32 | lodash.bind "^4.1.4" 33 | lodash.defaults "^4.0.1" 34 | lodash.filter "^4.4.0" 35 | lodash.flatten "^4.2.0" 36 | lodash.foreach "^4.3.0" 37 | lodash.map "^4.4.0" 38 | lodash.merge "^4.4.0" 39 | lodash.pick "^4.2.1" 40 | lodash.reduce "^4.4.0" 41 | lodash.reject "^4.4.0" 42 | lodash.some "^4.4.0" 43 | 44 | combined-stream@^1.0.5: 45 | version "1.0.5" 46 | resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" 47 | dependencies: 48 | delayed-stream "~1.0.0" 49 | 50 | component-emitter@^1.2.0: 51 | version "1.2.1" 52 | resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.2.1.tgz#137918d6d78283f7df7a6b7c5a63e140e69425e6" 53 | 54 | cookiejar@^2.0.6: 55 | version "2.1.0" 56 | resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.0.tgz#86549689539b6d0e269b6637a304be508194d898" 57 | 58 | core-util-is@~1.0.0: 59 | version "1.0.2" 60 | resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" 61 | 62 | css-select@~1.2.0: 63 | version "1.2.0" 64 | resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" 65 | dependencies: 66 | boolbase "~1.0.0" 67 | css-what "2.1" 68 | domutils "1.5.1" 69 | nth-check "~1.0.1" 70 | 71 | css-what@2.1: 72 | version "2.1.0" 73 | resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" 74 | 75 | debug@^2.2.0: 76 | version "2.6.2" 77 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.2.tgz#dfa96a861ee9b8c2f29349b3bcc41aa599a71e0f" 78 | dependencies: 79 | ms "0.7.2" 80 | 81 | delayed-stream@~1.0.0: 82 | version "1.0.0" 83 | resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" 84 | 85 | dom-serializer@0, dom-serializer@~0.1.0: 86 | version "0.1.0" 87 | resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" 88 | dependencies: 89 | domelementtype "~1.1.1" 90 | entities "~1.1.1" 91 | 92 | domelementtype@1, domelementtype@^1.3.0: 93 | version "1.3.0" 94 | resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" 95 | 96 | domelementtype@~1.1.1: 97 | version "1.1.3" 98 | resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" 99 | 100 | domhandler@^2.3.0: 101 | version "2.3.0" 102 | resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" 103 | dependencies: 104 | domelementtype "1" 105 | 106 | domutils@1.5.1, domutils@^1.5.1: 107 | version "1.5.1" 108 | resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" 109 | dependencies: 110 | dom-serializer "0" 111 | domelementtype "1" 112 | 113 | entities@^1.1.1, entities@~1.1.1: 114 | version "1.1.1" 115 | resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" 116 | 117 | extend@^3.0.0: 118 | version "3.0.0" 119 | resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" 120 | 121 | form-data@^2.1.1: 122 | version "2.1.2" 123 | resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.2.tgz#89c3534008b97eada4cbb157d58f6f5df025eae4" 124 | dependencies: 125 | asynckit "^0.4.0" 126 | combined-stream "^1.0.5" 127 | mime-types "^2.1.12" 128 | 129 | formidable@^1.1.1: 130 | version "1.1.1" 131 | resolved "https://registry.yarnpkg.com/formidable/-/formidable-1.1.1.tgz#96b8886f7c3c3508b932d6bd70c4d3a88f35f1a9" 132 | 133 | htmlparser2@^3.9.1: 134 | version "3.9.2" 135 | resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" 136 | dependencies: 137 | domelementtype "^1.3.0" 138 | domhandler "^2.3.0" 139 | domutils "^1.5.1" 140 | entities "^1.1.1" 141 | inherits "^2.0.1" 142 | readable-stream "^2.0.2" 143 | 144 | iconv-lite@^0.4.13: 145 | version "0.4.15" 146 | resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.15.tgz#fe265a218ac6a57cfe854927e9d04c19825eddeb" 147 | 148 | inherits@^2.0.1, inherits@~2.0.1: 149 | version "2.0.3" 150 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" 151 | 152 | isarray@~1.0.0: 153 | version "1.0.0" 154 | resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" 155 | 156 | lodash.assignin@^4.0.9: 157 | version "4.2.0" 158 | resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" 159 | 160 | lodash.bind@^4.1.4: 161 | version "4.2.1" 162 | resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" 163 | 164 | lodash.defaults@^4.0.1: 165 | version "4.2.0" 166 | resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" 167 | 168 | lodash.filter@^4.4.0: 169 | version "4.6.0" 170 | resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" 171 | 172 | lodash.flatten@^4.2.0: 173 | version "4.4.0" 174 | resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" 175 | 176 | lodash.foreach@^4.3.0: 177 | version "4.5.0" 178 | resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" 179 | 180 | lodash.map@^4.4.0: 181 | version "4.6.0" 182 | resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" 183 | 184 | lodash.merge@^4.4.0: 185 | version "4.6.0" 186 | resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5" 187 | 188 | lodash.pick@^4.2.1: 189 | version "4.4.0" 190 | resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" 191 | 192 | lodash.reduce@^4.4.0: 193 | version "4.6.0" 194 | resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" 195 | 196 | lodash.reject@^4.4.0: 197 | version "4.6.0" 198 | resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" 199 | 200 | lodash.some@^4.4.0: 201 | version "4.6.0" 202 | resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" 203 | 204 | lodash@^4.14.0: 205 | version "4.17.4" 206 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" 207 | 208 | methods@^1.1.1: 209 | version "1.1.2" 210 | resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" 211 | 212 | mime-db@~1.26.0: 213 | version "1.26.0" 214 | resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.26.0.tgz#eaffcd0e4fc6935cf8134da246e2e6c35305adff" 215 | 216 | mime-types@^2.1.12: 217 | version "2.1.14" 218 | resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.14.tgz#f7ef7d97583fcaf3b7d282b6f8b5679dab1e94ee" 219 | dependencies: 220 | mime-db "~1.26.0" 221 | 222 | mime@^1.3.4: 223 | version "1.3.4" 224 | resolved "https://registry.yarnpkg.com/mime/-/mime-1.3.4.tgz#115f9e3b6b3daf2959983cb38f149a2d40eb5d53" 225 | 226 | ms@0.7.2: 227 | version "0.7.2" 228 | resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" 229 | 230 | nth-check@~1.0.1: 231 | version "1.0.1" 232 | resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" 233 | dependencies: 234 | boolbase "~1.0.0" 235 | 236 | process-nextick-args@~1.0.6: 237 | version "1.0.7" 238 | resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" 239 | 240 | qs@^6.1.0: 241 | version "6.4.0" 242 | resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" 243 | 244 | readable-stream@^2.0.2, readable-stream@^2.0.5: 245 | version "2.2.3" 246 | resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.3.tgz#9cf49463985df016c8ae8813097a9293a9b33729" 247 | dependencies: 248 | buffer-shims "^1.0.0" 249 | core-util-is "~1.0.0" 250 | inherits "~2.0.1" 251 | isarray "~1.0.0" 252 | process-nextick-args "~1.0.6" 253 | string_decoder "~0.10.x" 254 | util-deprecate "~1.0.1" 255 | 256 | string_decoder@~0.10.x: 257 | version "0.10.31" 258 | resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" 259 | 260 | superagent-charset@^1.1.1: 261 | version "1.1.1" 262 | resolved "https://registry.yarnpkg.com/superagent-charset/-/superagent-charset-1.1.1.tgz#b5a2c6798945ac7d78c3cc3b6149d9765069cfad" 263 | dependencies: 264 | iconv-lite "^0.4.13" 265 | 266 | superagent@^3.4.1: 267 | version "3.5.0" 268 | resolved "https://registry.yarnpkg.com/superagent/-/superagent-3.5.0.tgz#56872b8e1ee6de994035ada2e53266899af95a6d" 269 | dependencies: 270 | component-emitter "^1.2.0" 271 | cookiejar "^2.0.6" 272 | debug "^2.2.0" 273 | extend "^3.0.0" 274 | form-data "^2.1.1" 275 | formidable "^1.1.1" 276 | methods "^1.1.1" 277 | mime "^1.3.4" 278 | qs "^6.1.0" 279 | readable-stream "^2.0.5" 280 | 281 | util-deprecate@~1.0.1: 282 | version "1.0.2" 283 | resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" 284 | --------------------------------------------------------------------------------