项目背景
最近刚刚用vue完成了一个商城的项目。截下来就是填充数据的问题了,我们缺图片信息,只有想办法从淘宝弄了。第一次写个小爬虫还是挺兴奋的。python不会,只好用node实现了,查了下目前有两种方案:
- superagent + cheerio
- phantomjs
superagent+cheerio
superagent是服务器端异步请求模块的一种实现,它支持restful形式的api。内部依赖nodejs原生的请求api,适用于nodejs环境下。
1 2 3 4 5 6 7 8 9 10 11 12
| request .post('/api/pet') .send({ name: 'Manny', species: 'cat' }) .set('X-API-Key', 'foobar') .set('Accept', 'application/json') .end(function(res){ if (res.ok) { alert('yay got ' + JSON.stringify(res.body)); } else { alert('Oh no! error ' + res.text); } });
|
cheerio则是服务器端html解析器的一种实现,服务器端不像浏览器,带有html解析器。所以在操作html文档数据的时候,需要这种工具。
说到这里,思路也很清晰了:利用superagent异步拉取淘宝的商品详情页html。再利用cheerio解析其中需要的图文详情dom获得图片url。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| var express = require('express'); var bodyParser = require('body-parser'); var cheerio = require('cheerio'); var superagent = require('superagent'); var fs = require('fs'); var app = express(); app.use(bodyParser.json({limit: '1mb'})); app.use(bodyParser.urlencoded({ extended: true })); app.get('/index.html', function(req, res, next){ res.send(fs.readFileSync('index.html').toString()); }); app.post('/result', function(req, res, next){ var urls = req.body.urls.split('\r\n'); urls.forEach(function(url) { superagent.get(url).end(function (err, sres) { if (err) { return next(err); } var $ = cheerio.load(sres.text); var items = []; $('.mui-custommodule .mui-custommodule-item img').each(function(idx, element){ var ele = $(element); item.push({ imgsrc: ele.attr('src') }) }); var se = []; se.push('页面地址:', url, '\n'); se.push('页面html:', sres.text, '\n'); se.push('页面结果:', JSON.stringify(items), '\n'); res.send(se.join('')); }); }); }); app.listen(3000, function () { console.log('app is listening at port 3000'); });
|
这里在index.html页面新建了一个form和textarea,textarea以换行符输入多个详情页面的url。要注意在使用express时,如果需要提取post提交的参数时,需要使用body-parser。
结果
最终结果并不如我所料。抓取到的页面url总是404,经过一番google。发现有如下原因:
- 淘宝页面dom采用js动态生成,抓取不到;
- 淘宝反爬虫策略可以检测出这种基础的盗取数据方式
phantomjs
PhantomJS是一个无界面的,可脚本编程的WebKit浏览器引擎。它原生支持多种web 标准:DOM 操作,CSS选择器,JSON,Canvas 以及SVG。可以看做是一个无界面的浏览器。既然浏览器可以正常打开淘宝页面,phantomjs应该也可以;而且它还提供了操作dom的能力,只要等待页面加载完毕,dom生成之后,也就可以拿到数据了。代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
| var webPage = require('webpage'); var page = webPage.create(); var pageTb = webPage.create(); var pageImg = webPage.create(); var tbUrl = system.args[2]; page.settings.userAgent = "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36"; pageTb.open(tbUrl, function(status) { setTimeout(function() { var result = pageTb.evaluate(function() { var temp = []; Array.prototype.forEach.call(document.querySelectorAll('.mui-custommodule-item img'), function(item){ temp.push(item.dataset.ksLazyload); }) return temp; }); var resultLen = result.length; pageImg.viewportSize = { width: 375 }; function getImgs(){ pageImg.open(result[0], function(status) { if (status !== 'success') { console.log('Unable to load the address!'); getImgs(); } else { window.setTimeout(function () { var size = pageImg.evaluate(function() { var img = document.querySelector('img'); var h = img.getAttribute('height'), w = img.getAttribute('width'); return { height: h/w*750, width: 750}; }); pageImg.viewportSize = size; pageImg.render( resultLen-result.length+'.jpg', {format: 'jpg', quality: '100'}); getImgs(); }, 200); } result.shift(); }) if(result.length<=0) phantom.exit(); } getImgs() }, 2000); });
|
注意phantom可以用npm安装,但是不是node直接可以执行的,执行命令如下:
1
| phantom --ssl-protocol=any "https://detail.m.tmall.com/item.htm?id=40586936292"
|
由于是打开https协议头的网页,所以执行js文件时,需要添加”–ssl-protocol=any”参数。详情页面的url作为参数传入。
可以看出上面phantom先加载页面,根据ksLazyload这个图片懒加载属性获取图片真实url;之后递归加载每张图片,利用render方法生成图片。
最终拉取下来效果是这样: