上篇文章,讲解了如何快速使用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()
})
}
复制代码