利用nodejs+phantomjs+casperjs采集淘宝商品的价格
因为一些业务需求需要采集淘宝店铺商品的销售价格,但是淘宝详情页面的价格显示是通过js动态调用显示的.所以就没法通过普通的获取页面html然后通过正则或者xpath的方式获取到想到的信息了.
所幸我们现在有了casperjs.这个是一个基于Phantomjs的库,而Phantomjs则是一个服务器端的js api的webkit浏览器.是不是很神奇?真的是.net的以外的世界很神奇,我们要多走去看看.
好了,现在废话不多说,开始切入正题.
首先就是就是几个相关库的安装.安装过程很简单,相关内容大家百度即可.在文章的底部我也会列出参考链接.
我们先进行下简要的分析:
具体的操作流程就是利用casperjs模拟鼠标点击商品的图片,然后网页显示对应的价格.
默认情况是这样的,如果不点击颜色分类下的图片,则对应的促销价格也只是会显示一个区间.
而只要我们点击了颜色分类对应的图片之后,则会是下面的效果
那么我们具体的操作步骤应该是:
1)打开具体的商品详情页
2)获取到颜色分类下图片个数,然后依次模拟鼠标点击
3)每点击一次图片,然后获取对应的促销价格
4)保存每次操作后的结果到数据库或者本地文件中待下一步处理
下面我们就来具体的一步步实现上面分析后所需要的步骤:
1.初始化casperjs
1 2 3 4 5 6 7 8 9 | var casper = require( 'casper' ).create({ clientScripts: [ "jquery.js" ], verbose: false , logLevel: 'debug' , pageSettings: { loadImages: false , // The WebPage instance used by Casper will loadPlugins: false // use these settings } });<br><br>phantom.outputEncoding = "gbk" ; //解决乱码问题 |
2.打开具体的url
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | /* 获取需要采集的url列表 */ casper.start(url, function () { casper.GetDetailUrl(url); }); /* 打开具体url */ casper.GetDetailUrl = function (detailUrl) { casper.thenOpen(detailUrl, function () { console.log( this .getCurrentUrl()); }); }; |
3.处理当前页面的所有sku价格与信息
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 | /* 处理当前页面的所有sku价格与信息 */ casper.then( function getPic() { // console.log(this.getHTML()); // fs.write('123', this.getHTML(), 'w'); product = casper.evaluate( function getProductFromPage() { return $( 'ul[class*="tb-img"]' ).children().size(); }); console.log(product); var str = '' for ( var i = 1; i <= product; i++) { str += casper.getPrice(i) + "|" ; } var item = new Object(); item.price = str; item.numiid = this .getCurrentUrl(); casper.PostData(item); // fs.write('myfile.html', str, 'w'); //this.capture("4.png"); });<br><br> |
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 | /* 获取商品的价格 */ casper.getPrice = function (index) { var dd = casper.clickByImg(index); if (dd == -1) { return '' ; } productPrice = casper.evaluate( function getPriceFromPage() { return $( '.tm-price' ).first().text().trim(); }); return (dd + "_" + productPrice); }; /* 点击小图及获取此商品的data-value */ casper.clickByImg = function (index) { var x = require( 'casper' ).selectXPath; // 如果此商品缺货则跳出 var path = '//*[@id="J_DetailMeta"]/div[1]/div[1]/div/div[4]/div/div/dl[1]/dd/ul/li[' + index + ']' ; var outOfStock = this .getElementAttribute(x(path), 'class' ); if (outOfStock == 'tb-out-of-stock' ) return '-1' ; this .click(x( '//*[@id="J_DetailMeta"]/div[1]/div[1]/div/div[4]/div/div/dl[1]/dd/ul/li[' + index + ']/a' )); return this .getElementAttribute(x(path), 'data-value' ); // "data-value" }; |
4.将最后处理后得到的结果提交到服务器上
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 | /* 提交商品价格信息到服务器 */ casper.PostData = function (item) { casper.open( 'http://XXX/UpdateItemsPrice' ).then( function () { this .fill( "form" , { 'numiid' : item.numiid, 'value' : item.price }, false ); this .capture( 'post.png' ); this .click( "#btnSave" ); this .echo( 'GOT it1.' + item.numiid); }); this .echo( 'GOT it2.' + item.numiid); this .wait(2000, function () { this .echo( "I've waited for a second." ); }); } |
最后run即可.
1 | casper.run(); |
通过以上4个步骤我们就能获取到单个链接下,所有sku的促销价格了.
现在还有个问题,就是我们的nodejs还没出场呢,不会把它忘记的,呵呵.
为什么这里casperjs都搞定了,还需要nodejs呢?那就是因为casperjs只能处理单个链接,如果有多条链接处理的话,就需要启动多个casperjs的实例来完成.
上面的所有代码都是casperjs的一个操作步骤,最后的一个run就是让这个实例按我们定义好的步骤来进行的一个完整的流程.
那么既然如果,我们就请nodejs出场吧~
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 | var count = 0; console.log( '主进程开启' ); var startTime = new Date().getTime(); var https = require( 'http' ); /* 获取需要采集的url列表 */ https.get( 'http://XXX/GetItemsList' , function (res) { // console.log("statusCode: ", res.statusCode); // console.log("headers: ", res.headers); res.on( 'data' , function (d) { // process.stdout.write(d); var obj = JSON.parse(d) for ( var i = 0; i < obj.items.length; i++) { capture(obj.items[i].detail_url); } ; }); }).on( 'error' , function (e) { console.error(e); }); /* 启动casperjs读取单个url */ function capture(url) { count++; var spawn = require( 'child_process' ).spawn, ls = spawn( 'casperjs' , [ 'casperjs.js' , url]); ls.on( 'close' , function (code) { if (code == 1) { console.log( 'child process异常结束。目标:' + url); } }); } |
当然,这里我们的casperjs需要进行模块化处理的,其实就是让casperjs可以获取调用的参数啦
1 2 | var system = require( 'system' ); var url = system.args[4]; |
以上,就是所有采集需要使用到的代码了!怎么样,是不是非常的彪悍啊,整个处理流程只用了区区100来行的代码,就搞定了所有的采集流程.
参考链接:
http://www.open-open.com/lib/view/open1338375857589.html
http://www.cnmiss.cn/?p=413
http://blog.csdn.net/sagomilk/article/details/20800543
http://www.cnblogs.com/zeusro/p/4188229.html
http://casperjs.readthedocs.org/en/latest/modules/casper.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了