nodejs + puppeteer 模拟淘宝登录并采集数据
熟悉puppeteer模块时做的小demo,代码十分简单易懂,分享给大家交流学习,请勿恶意抓取或做违反国家政策的行为。
一、前期准备工作
首先需要安装nodejs,并初始化一个项目,安装puppeteer模板以及log4js日志模板并保存。
npm install puppeteer --save-dev
npm install log4js --save-dev
二、先引用模块并定义相关变量以及日志配置
1 var puppeteer = require(\'puppeteer\'); 2 var log4js = require(\'log4js\'); 3 var logger = log4js.getLogger(); 4 // 结果集合 5 var resultList = []; 6 // 采集最大页码 7 var maxPage = 10; 8 // 当前采集页码 9 var nowPage = 1; 10 11 12 log4js.configure({ 13 appenders: { 14 xcLogFile: { 15 type: "dateFile", 16 filename: __dirname + \'/logs/log4js.log\',//您要写入日志文件的路径 17 //compress: true,//(默认为false) - 在滚动期间压缩备份文件(备份文件将具有.gz扩展名) 18 pattern: "-yyyy-MM-dd-hh",//(可选,默认为.yyyy-MM-dd) - 用于确定何时滚动日志的模式。格式:.yyyy-MM-dd-hh:mm:ss.log 19 encoding: \'utf-8\',//default "utf-8",文件的编码 20 maxLogSize: 2048000 //文件最大存储空间,当文件内容超过文件存储空间会自动生成一个文件xxx.log.1的序列自增长的文件 21 }, 22 xcLogConsole: { 23 type: \'console\' 24 } 25 }, 26 categories: { 27 default: { 28 appenders: [\'xcLogFile\'], 29 level: \'all\' 30 }, 31 xcLogFile: { 32 appenders: [\'xcLogFile\'], 33 level: \'all\' 34 }, 35 xcLogConsole: { 36 appenders: [\'xcLogConsole\'], 37 level: log4js.levels.ALL 38 } 39 } 40 }) 41 logger.level = \'debug\';
三、实现淘宝模拟登录
需要特别说明的是其中有一步跳过 selenium检测机制的代码,是十分重要的,如果没加将会无限循环验证码。
await page.evaluate(async () => { Object.defineProperty(navigator, \'webdriver\', { get: () => false }) })
const action = (async () => { // 定义浏览器无头模式、分辨率以及关闭沙盒模式等等。 const browser = await puppeteer.launch({ headless: false, defaultViewport: { width: 1300, height: 900 }, args: [\'--no-sandbox\', \'--disable-setuid-sandbox\'] }); // 新建页面 var page = await browser.newPage(); // 设定浏览器UserAgent await page.setUserAgent(\'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36\'); // 跳转淘宝登录页 await page.goto(\'https://login.taobao.com/member/login.jhtml?redirectURL=https%3A%2F%2Fwww.taobao.com%2F\'); // 这一步十分重要,因为大部分大型网站都会对selenium机制进行检测,例如navigator.webdriver,navigator.languages等等。 // 这一步就是把navigator的一些属性方法等等注入到浏览器中,绕过这些检测机制。 await page.evaluate(async () => { Object.defineProperty(navigator, \'webdriver\', { get: () => false }) }) // 等待登录按钮加载完毕 await page.$(\'.password-login\'); // 自动输入账号,间隔频率随机数300毫秒内 await page.type(\'input[, \'你的账号\', { delay: (parseInt(Math.random() * 300)) }); // 自动输入密码,间隔频率随机数300毫秒内 await page.type(\'input[, \'你的密码)\', { delay: (parseInt(Math.random() * 300)) }) // 点击登录按钮 await page.click(\'.password-login\'); console.log(\'登录成功!\') // 获取cookies // cookies = await page.evaluate(() => document.cookie); // console.log(cookies); // 等待页面加载完毕 await page.waitForNavigation() // 搜索关键字 var keyName = \'连衣裙\'; // 等待输入框加载完毕 await page.$(\'#q\'); // 输入关键字 await page.type(\'input[, keyName, { delay: 300 }); // 点击搜索按钮 await page.click(\'.btn-search\'); // 等待5秒后进入采集方法 await page.waitFor(5000); console.log(\'采集开始...\'); gather(page); })();
四、实现淘宝数据采集
const gather = async (page) => { await page.waitFor(5000); // 等待下一页的按钮 var nextBtn = await page.waitForSelector(\'#mainsrp-pager .next:not(.next-disabled)\'); // 数据对象 console.log(`进行第${nowPage}页数据采集...`) // 执行JS方法,将获取到的结果返回 var content = await page.evaluate(() => { // 这里的:not(.item-ad)是指不需要推广的商品,如果需要则直接去掉即可。 var itemList = document.querySelectorAll(\'#mainsrp-itemlist .J_MouserOnverReq:not(.item-ad)\'); var dataList = []; for (var i = 0; i < itemList.length; i++) { var data = {}; var item = itemList[i]; // 商品名称 data.name = item.querySelector(\'.title .J_ClickStat\').innerText; // 价格 data.price = item.querySelector(\'.price strong\').innerText; // 地区 data.area = item.querySelector(\'.location\').innerText; // 店铺名称 data.shopName = item.querySelector(\'.shopname\').innerText; // 月销量 data.sellerMonth = item.querySelector(\'.deal-cnt\').innerText || 0; if (data.sellerMonth) { data.sellerMonth = data.sellerMonth.replace(\'人付款\', \'\'); } dataList.push(data); } return dataList; }); console.log(`采集到${content.length}条数据!`); content.map(item => { // 这里本应是数据库操作,但是篇幅有限就直接写在日志里。 logger.debug(JSON.stringify(item)); resultList.push(item); }) // 判断页码是否小于定义的采集最大页码,并且存在下一页按钮。 if (nowPage < maxPage && nextBtn) { nowPage++; page.click(\'#mainsrp-pager .next\'); gather(page); } else { console.log(`采集完毕,一共采集${maxPage}页,${resultList.length}条数据!`); } }
代码都是实测没问题的,这里的maxPage也本应是动态获取的,但都比较简单,有问题的小伙伴可以留言问我,转载请注明出处,谢谢!