如果网页的内容是ajax异步加载的,恰好接口又是加密的,为了快速实现爬取数据,首先考虑到的是selenium,但是selenium还要配置谷歌浏览器,谷歌驱动等等,如果要部署或者迁移到其他设备上再去执行,环境配置就是必不可少的,遇见问题,可能半个小时就过去了,如果用pyppeteer完全不用考虑环境配置的问题,程序会自动加载所需要的环境,配合asyncio使用之后,爬取速度起飞,毕竟异步爬取秒同步爬取分分钟的事儿
对于已知道爬取上限的网站来说很实用,但是对于需要判断是否有下一页的网站来说就略有不足,这里仅仅是学习体验一波,毕竟scrapy异步框架,还是值得信赖的,在scrapy里面的中间件中,可以使用pyppeteer加载ajax,不管是爬取还是保存,在不同的文件中配置,这一点是我最喜欢的。
import asyncio
import pyppeteer
from pyppeteer import launch
from pyquery import PyQuery as pq
# 官方文档
# https://miyakogi.github.io/pyppeteer/reference.html
url = "https://dynamic2.scrape.center/"
# 粒子01
async def main1():
# launch 方法会新建一个 Browser 对象,其执行后最终会得到一个 Browser 对象,
# 然后赋值给 browser。这一步就相当于启动了浏览器
browser = await launch()
# 然后 browser 调用 newPage 方法相当于浏览器中新建了一个选项卡,同时新建了一个 Page 对象,
# 这时候新启动了一个选项卡,但是还未访问任何页面,浏览器依然是空白
page = await browser.newPage()
# 随后 Page 对象调用了 goto 方法就相当于在浏览器中输入了这个 URL,浏览器跳转到了对应的页面进行加载。
await page.goto(url)
# Page 对象调用 waitForSelector 方法,传入选择器,那么页面就会等待选择器所对应的节点信息加载出来,
# 如果加载出来了,立即返回,否则会持续等待直到超时。此时如果顺利的话,页面会成功加载出来。
await page.waitForSelector(".item .name")
# 页面加载完成之后再调用 content 方法,可以获得当前浏览器页面的源代码,这就是 JavaScript 渲染后的结果。
doc = pq(await page.content())
# 然后进一步的,我们用 pyquery 进行解析并提取页面的电影名称,就得到最终结果了。
names = [item.text() for item in doc(".item .name").items()]
print("Names: ", names)
await browser.close()
# asyncio.get_event_loop().run_until_complete(main1())
# 粒子02
width, height = 1366, 768
async def main2():
# browser = await launch(headless=False) # 有界面的,创建一个空白的浏览器
# browser = await launch(devtools=True) # 有界面的,创建一个空白的浏览器,右侧直接打开调试工具
# browser = await launch(headless=False, args=["--disable-infobars"]) # 有界面的,创建一个空白的浏览器,右侧直接打开调试工具
# 使得浏览器的窗口和打开的标签内的网页大小一致
# browser = await launch(headless=False,
# args=["--disable-infobars", f'--window-size={width},{height}']) # 有界面的,创建一个空白的浏览器,右侧直接打开调试工具
# userDataDir读取本地浏览器配置信息,可以缓存cookies,再次运行程序会读取这些配置
browser = await launch(headless=False, userDataDir='./userdata', args=['--disable-infobars'])
# browser = await launch()
# 无痕模式 使用context对象创建page选项卡
# context = await browser.createIncognitoBrowserContext()
# page = await context.newPage()
page = await browser.newPage()
# 防止浏览器检测webdriver
await page.evaluateOnNewDocument('Object.defineProperty(navigator, "webdriver", {get: () => undefined})')
await page.setViewport({"width": width, "height": height}) # 设置窗口大小
await page.goto(url)
await page.waitForSelector(".item .name")
await asyncio.sleep(2)
await page.screenshot(path="example.png")
dimensions = await page.evaluate("""() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio,
}
}""")
print(dimensions) # {'width': 1366, 'height': 768, 'deviceScaleFactor': 1} 网页宽高和像素
await browser.close()
#
#
# asyncio.get_event_loop().run_until_complete(main2())
# 粒子03 Page选择器
async def main3():
browser = await launch()
page = await browser.newPage()
await page.goto("https://dynamic2.scrape.center/")
await page.waitForSelector(".item .name")
# J 方法传入一个选择器 Selector,则能返回对应匹配的第一个节点,等价于 querySelector。
# 如 JJ 方法则是返回符合 Selector 的列表,类似于 querySelectorAll。
j_result1 = await page.J(".item .name")
j_result2 = await page.querySelector(".item .name")
jj_result1 = await page.JJ(".item .name")
jj_result2 = await page.querySelectorAll(".item .name")
print("J Result1:", j_result1)
print("J Result2:", j_result2)
print("JJ Result1:", jj_result1)
print("JJ Result2:", jj_result2)
await browser.close()
# asyncio.get_event_loop().run_until_complete(main3())
# 粒子04 选项卡操作
async def main4():
browser = await launch(headless=False)
page = await browser.newPage()
await page.goto("https://www.baidu.com")
page = await browser.newPage()
await page.goto("https://www.bing.com")
pages = await browser.pages() # 获取所有选项卡
print("Pages:", pages)
page1 = pages[1] # 选择准备切换到的选项卡
await page1.bringToFront() # 切换到对应的选项卡
await asyncio.sleep(10)
# asyncio.get_event_loop().run_until_complete(main4())
# 粒子05 page页面常规操作
async def main5():
browser = await launch(headless=False)
page = await browser.newPage()
await page.goto('https://dynamic1.scrape.center/')
await page.goto('https://dynamic2.scrape.center/')
await page.goBack() # 后退
await page.goForward() # 前进
await page.reload() # 刷新
await page.pdf() # 保存pdf
await page.screenshot() # 截图
await page.setViewport("Hello World
") # 设置页面HTML
await page.setUserAgent("python") # 设置ua
await page.setExtraHTTPHeaders(headers={}) # 设置headers
await page.close() # 关闭
await browser.close()
# asyncio.get_event_loop().run_until_complete(main5())
# 粒子06 模拟点击操作
async def main6():
browser = await launch(headless=False)
page = await browser.newPage()
await page.goto("https://dynamic2.scrape.center/")
await page.waitForSelector(".item .name")
await page.click(".item .name", options={
"button": "right", # 鼠标按钮,分为 left、middle、right
"clickCount": 1, # 1或2 双击、单击
"delay": 3000 # 毫秒 延迟点击
})
# await page.type('#q', 'iPad') # 定位 输入文本 如send_keys,这里是type方法
# print('HTML:', await page.content()) # 获取整个网页源代码,需要配合pq解析
# print('Cookies:', await page.cookies()) # 获取cookies
await browser.close()
asyncio.get_event_loop().run_until_complete(main6())
# 除了 waitForSelector 方法,还有很多其他的等待方法,介绍如下。
# waitForFunction:等待某个 JavaScript 方法执行完毕或返回结果。
# waitForNavigation:等待页面跳转,如果没加载出来就会报错。
# waitForRequest:等待某个特定的请求被发出。
# waitForResponse:等待某个特定的请求收到了回应。
# waitFor:通用的等待方法。
# waitForSelector:等待符合选择器的节点加载出来。
# waitForXPath:等待符合 XPath 的节点加载出来。
# 通过等待条件,我们就可以控制页面加载的情况了。