熟悉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[id=fm-login-id]', '你的賬號', { delay: (parseInt(Math.random() * 300)) }); // 自動輸入密碼,間隔頻率隨機數300毫秒內 await page.type('input[id=fm-login-password]', '你的密碼)', { 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[id=q]', 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也本應是動態獲取的,但都比較簡單,有問題的小伙伴可以留言問我,轉載請注明出處,謝謝!