上篇文章,講解了如何快速使用puppeteer爬取一個簡單的視頻網站,可是,邏輯比較簡單,投機取巧,直接利用詳情頁分頁的邏輯爬取電視劇,可是這樣只能爬取 某一部電視劇,電影。這樣比較煩。想作本身的視頻網站仍是有點雞肋。固然還有一個棘手的問題,若是詳情頁有token驗證怎麼辦。javascript
本文涉及相關僅供學習交流,侵聯改java
本文就這幾個問題進行解答:web
梳理一下爬取視頻網站須要幹那些事:數據庫
重複 3 ~ 5,待當前列表頁全部內容爬取後,跳轉至列表第二頁,以此類推,等所有列表頁爬取完畢後關閉瀏覽器。瀏覽器
知道步驟後,那麼開始吧bash
爲了不廣告的嫌疑,demo中的視頻網址使用假地址,若是想要地址,請評論app
這是它的列表頁,能夠看出來,他是有分頁的。有兩種辦法less
先使用分析分頁規則的方案實現dom
http://www.xxxxxxx.xx
http://www.xxxxxxx.xx/page/2
http://www.xxxxxxx.xx/page/3
...
複製代碼
它是什麼規則不用多介紹了吧async
若是對puppeteer API 不熟悉的,請移步萬物皆可爬-puppeteer實戰
const findAllMovie = async () => {
console.log('開始參觀這個網站')
const browser = await (puppeteer.launch({
executablePath: puppeteer.executablePath(),
headless: false
}));
/* @params
* pageSize:你想爬多少頁
*/
for (let i = 1; i <= pageSize; i++) {
// 用來存爬到的詳情頁地址
var arr = []
const targetUrl = `https://www.xxxxxxx.xx/page/${i}`
const page = await browser.newPage();
// 進入頁面
await page.goto(targetUrl, {
timeout: 0,
waitUntil: 'domcontentloaded'
});
// 獲取根節點
const baseNode = 'ul#post_container'
const movieList = await page.evaluate(sel => {
const movieBox = Array.from($(sel).find('li'))
var ctn = movieBox.map(v => {
const url = $(v).find('.article h2 a').attr('href');
return {url: url}
})
return ctn
}, baseNode)
arr.push(...movieList)
// 準備爬取詳情頁
await detailMovie(arr, page)
}
browser.close();
console.log('Visit Over')
return {msg: '同步完成'}
}
複製代碼
const detailMovie = async (arr, page) => {
var detailArr = []
console.log('當頁影片數:' + arr.length)
for (let i = 0; i < arr.length; i++) {
await page.goto(arr[i].url, {
timeout: 0,
waitUntil: 'domcontentloaded'
})
const baseNode = '.article_container.row.box'
// jq大法 不用過多說明了吧
const movieList = await page.evaluate(sel => {
const movieBox = Array.from($(sel).find('#post_content').find('p'))
const urlBox = $(sel).find('#藍光高清 td a').attr('href')
var tmp = [{}]
var ctn = tmp.map((v,i) => {
const imgUrl = $(movieBox[0]).find('a').attr('href');
var info = $(movieBox[1]).text()
return {
imgUrl: imgUrl,
name: info,
urlBox: urlBox
}
})
return ctn
}, baseNode)
console.log(movieList)
detailArr.push(...movieList)
console.log('抓取第 ' + detailArr.length + ' 頁完成')
}
console.log('開始向數據庫添加數據')
await addMovie(detailArr)
page.close()
return detailArr
}
複製代碼
感受很爽有沒有!
本身的視頻網站就建好了。
puppeteer 強大之處,他能夠進行模擬操做,好比咱們可讓它本身進行百度
很簡單,一個方法搞定
page.type(selector, text[, options])
注意: 每一個字符輸入後都會觸發keydown,keypress/input 和 keyup事件。
page.type('#mytextarea', 'Hello'); // 當即輸入
page.type('#mytextarea', 'World', {delay: 100}); // 輸入變慢,像一個用戶
複製代碼
接下來打開百度試試
const getForm = async () => {
// puppteer 驗證
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
await page.goto('https://baidu.com');
await page.type('#kw', 'puppeteer', {delay: 100}); //打開百度後,自動在搜索框裏慢慢輸入puppeteer ,
page.click('#su') //而後點擊搜索
await page.waitFor(1000);
const targetLink = await page.evaluate(() => {
let url = document.querySelector('.result a').href
return url
});
console.log(targetLink);
await page.goto(targetLink);
// await page.waitFor(1000);
browser.close();
}
複製代碼
上述的例子中,咱們還使用的page.click方法。
page.click(selector, [options])
此方法找到一個匹配 selector 選擇器的元素,若是須要會把此元素滾動到可視,而後經過 page.mouse 點擊它。 若是選擇器沒有匹配任何元素,此方法將會報錯。
要注意若是 click() 觸發了一個跳轉,會有一個獨立的 page.waitForNavigation() Promise對象須要等待。 正確的等待點擊後的跳轉是這樣的:
const [response] = await Promise.all([
page.waitForNavigation(waitOptions),
page.click(selector, clickOptions),
]);
複製代碼
難點剖析
const devices = require('puppeteer/DeviceDescriptors');
const iPhone6 = devices['iPhone 6'];
await page.emulate(iPhone6)
複製代碼
const getYzm = async () => {
const devices = require('puppeteer/DeviceDescriptors');
const iPhone6 = devices['iPhone 6'];
const conf = {
headless: false,
defaultViewport: {
width: 1300,
height: 900
},
slowMo: 30
}
puppeteer.launch(conf).then(async browser => {
var page = await browser.newPage()
await page.emulate(iPhone6)
await page.goto('https://www.dingtalk.com/oasite/register_h5_new.htm')
// 滑動驗證碼會檢測nabigator.webdriver這個屬性。所以咱們須要在滑動前將這個屬性設置爲false
// 接口的webdriver只讀屬性navigator指示用戶代理是否由自動化控制。
await page.evaluate(async () => {
Object.defineProperty(navigator, 'webdriver', {get: () => false})
})
// 錯誤輸入,觸發驗證碼
await page.type('#mobileReal', '15724564118')
await page.click('.am-button')
await page.type('#mobileReal', '')
await page.keyboard.press('Backspace')
await page.click('._2q5FIy80')
// 等待滑塊出現
var slide_btn = await page.waitForSelector('#nc_1_n1t', {timeout: 30000})
// 計算滑塊距離
const rect = await page.evaluate((slide_btn) => {
// 返回元素的大小及其相對於視口的位置
const {top, left, bottom, right} = slide_btn.getBoundingClientRect();
return {top, left, bottom, right}
}, slide_btn)
console.log(rect)
rect.left = rect.left + 10
rect.top = rect.top + 10
const mouse = page.mouse
await mouse.move(rect.left, rect.top)
// touchevent,而puppeteer只有mouseevent。所以須要經過某個方法,在滑動前先將事件傳遞出去。
await page.touchscreen.tap(rect.left, rect.top) // h5須要手動分發事件 模擬app的事件分發機制。
await mouse.down()
var start_time = new Date().getTime()
await mouse.move(rect.left + 800, rect.top, {steps: 25})
await page.touchscreen.tap(rect.left + 800, rect.top,)
console.log(new Date().getTime() - start_time)
await mouse.up()
console.log(await page.evaluate('navigator.webdriver'))
console.log('end')
// await page.close()
})
}
複製代碼