一、常規(guī)操作
點(diǎn)擊
點(diǎn)擊用page.click方法,默認(rèn)是css-selector。
await page.click('#J_QRCodeLogin > div.login-links > a.forget-pwd.J_Quick2Static')
輸入
輸入用page.type方法,第一個(gè)參數(shù)是選擇器,第二個(gè)參數(shù)是要輸入的字符串柬甥,第三個(gè)參數(shù)是延時(shí)設(shè)置。
await page.type('#TPL_username_1', '123123', {'delay': input_time_random() - 50})
await page.type('#TPL_password_1', '232322332', {'delay': input_time_random()})
import asyncio
import random
from pyppeteer import launch
def input_time_random():
return random.randint(100, 151)
async def main():
browser = await launch({'headless':False})
page = await browser.newPage()
await page.goto('https://login.taobao.com')
await page.waitFor(3 * 1000)
await page.click('#J_QRCodeLogin > div.login-links > a.forget-pwd.J_Quick2Static')
await page.waitFor(3 * 1000)
await page.type('#TPL_username_1', '123123', {'delay': input_time_random() - 50})
await page.type('#TPL_password_1', '232322332', {'delay': input_time_random()})
await page.waitFor(3 * 1000)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
滾動(dòng)
await page.evaluate('window.scrollBy(0, window.innerHeight)') #淘寶滾動(dòng)加載用
獲取元素坐標(biāo)
常用于獲取驗(yàn)證碼相關(guān)坐標(biāo)
el = await page.querySelector('#nc_1_n1z')
box = await el.boundingBox()
await page.hover('#nc_1_n1z') #鼠標(biāo)移動(dòng)方塊上
await page.mouse.down() #鼠標(biāo)拖動(dòng)操作包括按下其垄、移動(dòng)暗甥、放開(kāi)
await page.mouse.move(box['x']+1000,box['y'], {'delay': random.randint(1000, 2000),'steps':3})
await page.mouse.up()
二、常用函數(shù)
page.goto(url)
請(qǐng)求指定url
比較常用的用法是:page.goto(url,{'waitUntil':'load'})
waitUntil的參數(shù)有:load,domcontentloaded,networkidle0,networkidle2
networkidle0表示when there are no more than 0 network connections for at least 500 ms.
newwordidle2表示when there are no more than 2 network connections for at least 500 ms
DOM文檔的加載步驟為:
- 解析HTML結(jié)構(gòu)捉捅。
- 加載外部腳本和樣式表文件。
- 解析并執(zhí)行腳本代碼虽风。
- DOM樹(shù)構(gòu)建完成棒口。 //domcontentloaded
- 加載圖片等外部文件。
- 頁(yè)面加載完畢辜膝。 //load
page.waitfor(time)
設(shè)置頁(yè)面等待時(shí)間,單位是毫秒无牵,常用于設(shè)置操作間隔,讓page能加載完成指定目標(biāo)厂抖,如等待3秒鐘:
page.waitfor(3*1000)
page.waitForSelector(selector)/page.waitForXPath(xpath)
等待目標(biāo)元素加載完成茎毁,默認(rèn)timeout是30秒,可以輔助指定位置元素是否已經(jīng)加載完成。
page.waitForNavigation()
等到某動(dòng)作完成七蜘,常用的是配合其他動(dòng)作一起使用谭溉,如:
await asyncio.wait([
page.click('a.my-link'),
page.waitForNavigation(),
])
這段代碼表示,等待連接點(diǎn)擊并跳轉(zhuǎn)完成橡卤。
page.J(css selector)/page.querySelector(css selector)
通過(guò)css selector定位元素扮念,前面是縮寫(xiě)函數(shù)
page.Jx(xpath)/page.xpath(xpath)
通過(guò)xpath定位元素,前面是縮寫(xiě)函數(shù)
page.content()
獲取頁(yè)面當(dāng)前加載網(wǎng)頁(yè)的document,用法:
doc = await page.content()
page.cookies()
獲取頁(yè)面當(dāng)前的cookies,常用如:
...登錄后...
cookies= await page.cookies()
dosomething(cookies)
page.eveluate(jsstr)
執(zhí)行js碧库,js代碼用字符串書(shū)寫(xiě)柜与,注意引號(hào)的使用
page.evaluateOnNewDocument(jsstr)
用法同上,不過(guò)在頁(yè)面新打開(kāi)一個(gè)document時(shí)才生效嵌灰,上面的函數(shù)是當(dāng)前document生效弄匕。
page.hover(selector)
指針移動(dòng)到selector定位的元素位置
page.screenshot()
頁(yè)面截屏
page.setCacheEnabled()
是否啟用緩存,默認(rèn)是True
page.setJavaScriptEnabled()
是否允許加載js,默認(rèn)是True
page.setRequestInterception()
是否允許請(qǐng)求和返回注入沽瞭,默認(rèn)是False
page.setUserAgent()
設(shè)置UA
page.setViewport()
用法:
await page.setViewport({'width':xx,'height':xx})
2. 元素選擇器方法名 $變?yōu)閝uerySelector
# Pyppeteer使用Python風(fēng)格的函數(shù)名
Page.querySelector()
Page.querySelectorAll()
Page.xpath()
# 簡(jiǎn)寫(xiě)方式為:
Page.J(), Page.JJ(), and Page.Jx()
Page.evaluate() 和 Page.querySelectorEval()的參數(shù)
Pyppeteer的evaluate()方法只使用JavaScript字符串迁匠,該字符串可以是函數(shù)也可以是表達(dá)式,Pyppeteer會(huì)進(jìn)行自動(dòng)判斷秕脓。但有時(shí)會(huì)判斷錯(cuò)誤柒瓣,如果字符串被判斷成了函數(shù),并且報(bào)錯(cuò)吠架,可以添加選項(xiàng)force_expr=True
芙贫,強(qiáng)制Pyppeteer作為表達(dá)式處理。
獲取頁(yè)面內(nèi)容:
content = await page.evaluate('document.body.textContent', force_expr=True)
獲取元素的內(nèi)部文字:
element = await page.querySelector('h1')
title = await page.evaluate('(element) => element.textContent', element)
示例
import asyncio
from pyppeteer import launch
async def main():
# headless參數(shù)設(shè)為False傍药,則變成有頭模式
# Pyppeteer支持字典和關(guān)鍵字傳參磺平,Puppeteer只支持字典傳參
# 指定引擎路徑
# exepath = r'C:\Users\Administrator\AppData\Local\pyppeteer\pyppeteer\local-chromium\575458\chrome-win32/chrome.exe'
# browser = await launch({'executablePath': exepath, 'headless': False, 'slowMo': 30})
browser = await launch(
# headless=False,
{'headless': False}
)
page = await browser.newPage()
await page.setViewport(viewport={'width': 1280, 'height': 800}) # 設(shè)置頁(yè)面視圖大小
await page.setJavaScriptEnabled(enabled=True) # 是否啟用JS,enabled設(shè)為False拐辽,則無(wú)渲染效果
res = await page.goto('https://www.toutiao.com/', options={'timeout': 1000})# 超時(shí)間見(jiàn) 1000 毫秒
resp_headers = res.headers # 響應(yīng)頭
resp_status = res.status # 響應(yīng)狀態(tài)
# 等待
await asyncio.sleep(2)
# 第二種方法拣挪,在while循環(huán)里強(qiáng)行查詢某元素進(jìn)行等待
while not await page.querySelector('.t'):
pass
await page.evaluate('window.scrollBy(0, document.body.scrollHeight)')# 滾動(dòng)到頁(yè)面底部
await asyncio.sleep(2)
await page.screenshot({'path': 'toutiao.png'}) # 截圖 保存圖片
print(await page.cookies()) # 打印頁(yè)面cookies
""" 打印頁(yè)面文本 """
print(await page.content()) # 獲取所有 html 內(nèi)容
# 在網(wǎng)頁(yè)上執(zhí)行js 腳本
dimensions = await page.evaluate(pageFunction='''() => {
return {
width: document.documentElement.clientWidth, // 頁(yè)面寬度
height: document.documentElement.clientHeight, // 頁(yè)面高度
deviceScaleFactor: window.devicePixelRatio, // 像素比 1.0000000149011612
}
}''', force_expr=False) # force_expr=False 執(zhí)行的是函數(shù)
print(dimensions)
# 只獲取文本 執(zhí)行 js 腳本 force_expr為T(mén)rue則執(zhí)行的是表達(dá)式
content = await page.evaluate(pageFunction='document.body.textContent', force_expr=True)
print(content)
print(await page.title()) # 打印當(dāng)前頁(yè)標(biāo)題
# 抓取新聞內(nèi)容 可以使用 xpath 表達(dá)式
"""
# Pyppeteer 三種解析方式
Page.querySelector() # 選擇器
Page.querySelectorAll()
Page.xpath() # xpath 表達(dá)式
# 簡(jiǎn)寫(xiě)方式為:
Page.J(), Page.JJ(), and Page.Jx()
"""
element = await page.querySelector(".feed-infinite-wrapper > ul>li") # 紙抓取一個(gè)
print(element)
# 獲取所有文本內(nèi)容 執(zhí)行 js
content = await page.evaluate('(element) => element.textContent', element)
print(content)
# elements = await page.xpath('//div[@class="title-box"]/a')
elements = await page.querySelectorAll(".title-box a")
for item in elements:
print(await item.getProperty('textContent'))
# <pyppeteer.execution_context.JSHandle object at 0x000002220E7FE518>
# 獲取文本
title_str = await (await item.getProperty('textContent')).jsonValue()
# 獲取鏈接
title_link = await (await item.getProperty('href')).jsonValue()
print(title_str)
print(title_link)
# 關(guān)閉瀏覽器
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
import asyncio
import pyppeteer
from collections import namedtuple
headers = {
'date': 'Sun, 28 Apr 2019 06:50:20 GMT',
'server': 'Cmcc',
'x-frame-options': 'SAMEORIGIN\nSAMEORIGIN',
'last-modified': 'Fri, 26 Apr 2019 09:58:09 GMT',
'accept-ranges': 'bytes',
'cache-control': 'max-age=43200',
'expires': 'Sun, 28 Apr 2019 18:50:20 GMT',
'vary': 'Accept-Encoding,User-Agent',
'content-encoding': 'gzip',
'content-length': '19823',
'content-type': 'text/html',
'connection': 'Keep-alive',
'via': '1.1 ID-0314217270751344 uproxy-17'
}
Response = namedtuple("rs", "title url html cookies headers history status")
async def get_html(url):
browser = await pyppeteer.launch(headless=True, args=['--no-sandbox'])
page = await browser.newPage()
res = await page.goto(url, options={'timeout': 10000})
data = await page.content()
title = await page.title()
resp_cookies = await page.cookies() # cookie
resp_headers = res.headers # 響應(yīng)頭
resp_status = res.status # 響應(yīng)狀態(tài)
print(data)
print(title)
print(resp_headers)
print(resp_status)
return title
if __name__ == '__main__':
url_list = [
"https://www.toutiao.com",
"http://jandan.net/ooxx/page-8#comments",
"https://www.12306.cn/index"
]
task = [get_html(url) for url in url_list]
loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*task))
for res in results:
print(res)
三、快速上手
例一:爬取http://quotes.toscrape.com/js/ 全部頁(yè)面數(shù)據(jù)
import asyncio
from pyppeteer import launch
from pyquery import PyQuery as pq
async def main():
browser = await launch()
page = await browser.newPage()
await page.goto('http://quotes.toscrape.com/js/')
doc = pq(await page.content())
print('Quotes:', doc('.quote').length)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
運(yùn)行結(jié)果:10
1. launch 方法會(huì)新建一個(gè) Browser 對(duì)象;
2. 調(diào)用 newPage方法相當(dāng)于瀏覽器中新建了一個(gè)選項(xiàng)卡俱诸,同時(shí)新建了一個(gè) Page 對(duì)象;
3. 然后Page對(duì)象調(diào)用了goto方法就相當(dāng)于在瀏覽器中輸入了這個(gè)URL菠劝,瀏覽器跳轉(zhuǎn)到了對(duì)應(yīng)的頁(yè)面進(jìn)行加載;
4. 加載完成之后再調(diào)用 content 方法,返回當(dāng)前瀏覽器頁(yè)面的源代碼;
5. 然后進(jìn)一步地睁搭,我們用 pyquery 進(jìn)行同樣地解析赶诊,就可以得到 JavaScript 渲染的結(jié)果了;
在這個(gè)過(guò)程中,我們沒(méi)有配置 Chrome 瀏覽器园骆,沒(méi)有配置瀏覽器驅(qū)動(dòng)舔痪,免去了一些繁瑣的步驟,同樣達(dá)到了 Selenium 的效果锌唾,還實(shí)現(xiàn)了異步抓取锄码,爽歪歪!
例二:模擬網(wǎng)頁(yè)截圖,保存 PDF滋捶,另外還可以執(zhí)行自定義的 JavaScript 獲得特定的內(nèi)容
import asyncio
from pyppeteer import launch
async def main():
browser = await launch()
page = await browser.newPage()
await page.goto('http://quotes.toscrape.com/js/')
await page.screenshot(path='example.png')
await page.pdf(path='example.pdf')
dimensions = await page.evaluate('''() => {
return {
width: document.documentElement.clientWidth,
height: document.documentElement.clientHeight,
deviceScaleFactor: window.devicePixelRatio,
}
}''')
print(dimensions)
# >>> {'width': 800, 'height': 600, 'deviceScaleFactor': 1}
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
這里我們又用到了幾個(gè)新的 API痛悯,完成**了網(wǎng)頁(yè)截圖保存、網(wǎng)頁(yè)導(dǎo)出 PDF 保存炬太、執(zhí)行 JavaScript **并返回對(duì)應(yīng)數(shù)據(jù)灸蟆。
- 首先 screenshot 方法可以傳入保存的圖片路徑,另外還可以指定保存格式 type亲族、清晰度 quality炒考、是否全屏 fullPage、裁切 clip 等各個(gè)參數(shù)實(shí)現(xiàn)截圖霎迫。
- 然后斋枢,可見(jiàn)其內(nèi)容也是 JavaScript 渲染后的內(nèi)容,另外這個(gè)方法還可以指定放縮大小 scale知给、頁(yè)碼范圍 pageRanges瓤帚、寬高 width 和 height、方向 landscape 等等參數(shù)涩赢,導(dǎo)出定制化的 pdf 用這個(gè)方法就十分方便戈次。
- 最后我們又調(diào)用了 evaluate 方法執(zhí)行了一些 JavaScript,JavaScript 傳入的是一個(gè)函數(shù)筒扒,使用 return 方法返回了網(wǎng)頁(yè)的寬高怯邪、像素大小比率三個(gè)值,最后得到的是個(gè) JSON 格式的對(duì)象花墩,內(nèi)容如下:
{'width': 800, 'height': 600, 'deviceScaleFactor': 1}
總之悬秉,利用 Pyppeteer 我們可以控制瀏覽器執(zhí)行幾乎所有動(dòng)作,想要的操作和功能基本都可以實(shí)現(xiàn)冰蘑,用它來(lái)自由地控制爬蟲(chóng)當(dāng)然就不在話下了和泌。
例三:今日頭條
import asyncio
from pyppeteer import launch
async def main():
# headless參數(shù)設(shè)為False,則變成有頭模式
browser = await launch(
# headless=False
)
page = await browser.newPage()
# 設(shè)置頁(yè)面視圖大小
await page.setViewport(viewport={'width':1280, 'height':800})
# 是否啟用JS祠肥,enabled設(shè)為False武氓,則無(wú)渲染效果
await page.setJavaScriptEnabled(enabled=True)
await page.goto('https://www.toutiao.com/')
# 打印頁(yè)面cookies
print(await page.cookies())
# 打印頁(yè)面文本
print(await page.content())
# 打印當(dāng)前頁(yè)標(biāo)題
print(await page.title())
# 抓取新聞標(biāo)題
title_elements = await page.xpath('//div[@class="title-box"]/a')
for item in title_elements:
# 獲取文本
title_str = await (await item.getProperty('textContent')).jsonValue()
print(await item.getProperty('textContent'))
# 獲取鏈接
title_link = await (await item.getProperty('href')).jsonValue()
print(title_str)
print(title_link)
# 關(guān)閉瀏覽器
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
例四:與百度首頁(yè)交互
import time
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(headless=False)
page = await browser.newPage()
await page.setViewport({'width': 1200, 'height': 800})
await page.goto('https://www.baidu.com')
await page.type('input#kw.s_ipt', 'python') # 在搜索框中輸入python
await page.click('input#su') # 點(diǎn)擊搜索按鈕
# 第一種方法等待元素加載,強(qiáng)行等待5秒
# await asyncio.sleep(5)
# 第二種方法等待元素加載仇箱,在while循環(huán)里強(qiáng)行查詢某元素進(jìn)行等待
while not await page.querySelector('.t'):
pass
# 滾動(dòng)到頁(yè)面底部
await page.evaluate('window.scrollBy(0, window.innerHeight)')
# 這些等待方法都不好用
# await page.waitForXPath('h3', timeout=300)
# await page.waitForNavigation(waitUntil="networkidle0")
# await page.waitForFunction('document.getElementByTag("h3")')
# await page.waitForSelector('.t')
# await page.waitFor('document.querySelector("#t")')
# await page.waitForNavigation(waitUntil='networkidle0')
# await page.waitForFunction('document.querySelector("").inner??Text.length == 7')
title_elements = await page.xpath('//h3[contains(@class,"t")]/a')
for item in title_elements:
title_str = await (await item.getProperty('textContent')).jsonValue()
print(title_str)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
問(wèn)題1:css選擇器定位
問(wèn)題2:找各個(gè)標(biāo)題
.t就是這些標(biāo)題所在
例五:
class GetJsEncryptPage():
?
def __init__(self):
self.loop = asyncio.get_event_loop()
self.log = ICrawlerLog('spider').save
?
async def main(self, url, ): # 定義main協(xié)程函數(shù)县恕,
# 以下使用await 可以針對(duì)耗時(shí)的操作進(jìn)行掛起
browser = await launch({'headless': True, 'args': ['--no-sandbox', '--disable-infobars',
# '--proxy-server={}'.format(get_ip()),
],}) # 啟動(dòng)pyppeteer 屬于內(nèi)存中實(shí)現(xiàn)交互的模擬器
page = await browser.newPage() # 啟動(dòng)個(gè)新的瀏覽器頁(yè)面標(biāo)簽
await page.setUserAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36")
cookies = {}
try:
await page.goto(url) # 訪問(wèn)頁(yè)面
# 始終讓window.navigator.webdriver=false
# navigator是windiw對(duì)象的一個(gè)屬性,同時(shí)修改plugins工碾,languages,navigator 且讓
# await page.setJavaScriptEnabled(enabled=True) # 使用 JS 渲染
await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''') # 以下為插入中間js百姓,將淘寶會(huì)為了檢測(cè)瀏覽器而調(diào)用的js修改其結(jié)果渊额。
await page.evaluate('''() =>{ window.navigator.chrome = { runtime: {}, }; }''')
await page.evaluate('''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''')
await page.evaluate('''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''')
await page.goto(url) # 訪問(wèn)頁(yè)面
# content = await page.content() # 獲取頁(yè)面內(nèi)容
await asyncio.sleep(2)
except:
await page.evaluate('''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
await page.evaluate('''() =>{ window.navigator.chrome = { runtime: {}, }; }''')
await page.evaluate('''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''')
await page.evaluate('''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''')
# await page.evaluate('window.open("{}");'.format(url))
await page.evaluate('window.location="{}";'.format(url))
# await page.goto(url) # 訪問(wèn)登錄頁(yè)面
try:
cookies = await self.get_cookie(page)
except Exception as e:
await browser.close()
finally:
await browser.close()
?
return cookies
?
async def get_cookie(self, page):
# res = await page.content()
cookies_list = await page.cookies()
cookies = {}
for cookie in cookies_list:
cookies[cookie.get('name')] = cookie.get('value')
return cookies
?
def retry_if_result_none(self, result):
return result is None
?
def input_time_random(self, ):
return random.randint(100, 151)
?
def work(self, url):
pass
?
def run(self, url, func):
result = {}
try:
# task = asyncio.wait([])
result = self.loop.run_until_complete(func(url)) # 將協(xié)程注冊(cè)到事件循環(huán),并啟動(dòng)事件循環(huán)
except Exception as e:
self.log.info('協(xié)程被動(dòng)結(jié)束, chrome關(guān)閉')
for task in asyncio.Task.all_tasks():
task.cancel()
self.loop.stop()
self.loop.run_forever()
# self.loop.close()
return result
四、詳細(xì)用法
https://miyakogi.github.io/pyppeteer/reference.html
1. 開(kāi)啟瀏覽器
啟動(dòng) Chrome 進(jìn)程并返回瀏覽器實(shí)例
使用 Pyppeteer 的第一步便是啟動(dòng)瀏覽器旬迹,首先我們看下怎樣啟動(dòng)一個(gè)瀏覽器火惊,其實(shí)就相當(dāng)于我們點(diǎn)擊桌面上的瀏覽器圖標(biāo)一樣,把它開(kāi)起來(lái)奔垦。用 Pyppeteer 完成同樣的操作屹耐,只需要調(diào)用 launch 方法即可。
我們先看下 launch 方法的 API椿猎,鏈接為:https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.launcher.launch惶岭,其方法定義如下:
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word; font-family: "Courier New" !important; font-size: 12px !important;">pyppeteer.launcher.launch(options: dict = None, **kwargs) → pyppeteer.browser.Browser</pre>
可以看到它處于 launcher 模塊中,參數(shù)沒(méi)有在聲明中特別指定犯眠,返回類(lèi)型是 browser 模塊中的 Browser 對(duì)象按灶,也就是說(shuō)瀏覽器對(duì)象實(shí)例。另外觀察源碼發(fā)現(xiàn)這是一個(gè) async 修飾的方法筐咧,所以調(diào)用它的時(shí)候需要使用 await鸯旁。
調(diào)用 launch 方法即可,相關(guān)參數(shù)介紹:
- ignoreHTTPSErrors (bool): 是否要忽略 HTTPS 的錯(cuò)誤量蕊,默認(rèn)是 False铺罢。
- headless (bool): 是否啟用 Headless 模式曼库,即無(wú)界面模式伐庭,如果 devtools 這個(gè)參數(shù)是 True 的話义钉,那么該參數(shù)就會(huì)被設(shè)置為 False念颈,否則為 True娱节,即默認(rèn)是開(kāi)啟無(wú)界面模式的衰琐。
- executablePath (str): 可執(zhí)行文件的路徑吗垮,如果指定之后就不需要使用默認(rèn)的 Chromium 了葫盼,可以指定為已有的 Chrome 或 Chromium蛋勺。
- slowMo (int|float): 通過(guò)傳入指定的時(shí)間瓦灶,可以減緩 Pyppeteer 的一些模擬操作。
- args (List[str]): 在執(zhí)行過(guò)程中可以傳入的額外參數(shù)抱完。
- ignoreDefaultArgs (bool): 不使用 Pyppeteer 的默認(rèn)參數(shù)贼陶,如果使用了這個(gè)參數(shù),那么最好通過(guò) args 參數(shù)來(lái)設(shè)定一些參數(shù)巧娱,否則可能會(huì)出現(xiàn)一些意想不到的問(wèn)題碉怔。這個(gè)參數(shù)相對(duì)比較危險(xiǎn),慎用禁添。
- handleSIGINT (bool): 是否響應(yīng) SIGINT 信號(hào)撮胧,也就是可以使用 Ctrl + C 來(lái)終止瀏覽器程序,默認(rèn)是 True老翘。
- handleSIGTERM (bool): 是否響應(yīng) SIGTERM 信號(hào)芹啥,一般是 kill 命令锻离,默認(rèn)是 True。
- handleSIGHUP (bool): 是否響應(yīng) SIGHUP 信號(hào)墓怀,即掛起信號(hào)汽纠,比如終端退出操作,默認(rèn)是 True傀履。
- dumpio (bool): 是否將 Pyppeteer 的輸出內(nèi)容傳給 process.stdout 和 process.stderr 對(duì)象虱朵,默認(rèn)是 False。
- userDataDir (str): 即用戶數(shù)據(jù)文件夾钓账,即可以保留一些個(gè)性化配置和操作記錄碴犬。
- env (dict): 環(huán)境變量,可以通過(guò)字典形式傳入官扣。
- devtools (bool): 是否為每一個(gè)頁(yè)面自動(dòng)開(kāi)啟調(diào)試工具翅敌,默認(rèn)是 False。如果這個(gè)參數(shù)設(shè)置為 True惕蹄,那么 headless 參數(shù)就會(huì)無(wú)效蚯涮,會(huì)被強(qiáng)制設(shè)置為 False。
- logLevel (int|str): 日志級(jí)別卖陵,默認(rèn)和 root logger 對(duì)象的級(jí)別相同遭顶。
- autoClose (bool): 當(dāng)一些命令執(zhí)行完之后,是否自動(dòng)關(guān)閉瀏覽器泪蔫,默認(rèn)是 True棒旗。
- loop (asyncio.AbstractEventLoop): 時(shí)間循環(huán)對(duì)象。
- devtools (bool): 是否為每一個(gè)頁(yè)面自動(dòng)開(kāi)啟調(diào)試工具撩荣,默認(rèn)是 False铣揉。如果這個(gè)參數(shù)設(shè)置為 True,那么 headless 參數(shù)就會(huì)無(wú)效餐曹,會(huì)被強(qiáng)制設(shè)置為 False逛拱。
好了,知道這些參數(shù)之后台猴,我們可以先試試看朽合。
示例一:首先可以試用下最常用的參數(shù) headless,如果我們將它設(shè)置為 True 或者默認(rèn)不設(shè)置它饱狂,在啟動(dòng)的時(shí)候我們是看不到任何界面的曹步,如果把它設(shè)置為 False,那么在啟動(dòng)的時(shí)候就可以看到界面了休讳,一般我們在調(diào)試的時(shí)候會(huì)把它設(shè)置為 False讲婚,在生產(chǎn)環(huán)境上就可以設(shè)置為 True,我們先嘗試一下關(guān)閉 headless 模式:
import asyncio
from pyppeteer import launch
async def main():
await launch(headless=False)
await asyncio.sleep(100)
asyncio.get_event_loop().run_until_complete(main())
運(yùn)行之后看不到任何控制臺(tái)輸出俊柔,但是這時(shí)候就會(huì)出現(xiàn)一個(gè)空白的 Chromium 界面了筹麸。但是可以看到這就是一個(gè)光禿禿的瀏覽器而已纳猫,看一下相關(guān)信息
看到了,這就是 Chromium竹捉,上面還寫(xiě)了開(kāi)發(fā)者內(nèi)部版本,可以認(rèn)為是開(kāi)發(fā)版的 Chrome 瀏覽器就好尚骄。
示例二:開(kāi)啟調(diào)試模式块差。比如在寫(xiě)爬蟲(chóng)的時(shí)候會(huì)經(jīng)常需要分析網(wǎng)頁(yè)結(jié)構(gòu)還有網(wǎng)絡(luò)請(qǐng)求,所以開(kāi)啟調(diào)試工具還是很有必要的倔丈,我們可以將 devtools 參數(shù)設(shè)置為 True憨闰,這樣每開(kāi)啟一個(gè)界面就會(huì)彈出一個(gè)調(diào)試窗口,非常方便需五,示例如下:
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(devtools=True)
page = await browser.newPage()
await page.goto('https://www.baidu.com')
await asyncio.sleep(100)
asyncio.get_event_loop().run_until_complete(main())
剛才說(shuō)過(guò) devtools 這個(gè)參數(shù)如果設(shè)置為了 True鹉动,那么 headless 就會(huì)被關(guān)閉了,界面始終會(huì)顯現(xiàn)出來(lái)宏邮。在這里我們新建了一個(gè)頁(yè)面泽示,打開(kāi)了百度,界面運(yùn)行效果如下:
示例三:可以看到上面的一條提示:"Chrome 正受到自動(dòng)測(cè)試軟件的控制"蜜氨,這個(gè)提示條有點(diǎn)煩械筛,那咋關(guān)閉呢?這時(shí)候就需要用到 args 參數(shù)了飒炎,禁用操作如下
browser = await launch(headless=False, args=['--disable-infobars'])
另外有人就說(shuō)了埋哟,這里你只是把提示關(guān)閉了,有些網(wǎng)站還是會(huì)檢測(cè)到是 webdriver 吧郎汪,比如淘寶檢測(cè)到是 webdriver 就會(huì)禁止登錄了赤赊,我們可以試試:
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(headless=False)
page = await browser.newPage()
await page.goto('https://www.taobao.com')
await asyncio.sleep(100)
asyncio.get_event_loop().run_until_complete(main())
運(yùn)行時(shí)候進(jìn)行一下登錄,然后就會(huì)彈出滑塊煞赢,自己手動(dòng)拖動(dòng)一下抛计,然后就報(bào)錯(cuò)了,界面如下:
2. 最大化窗口
如果你運(yùn)行了上面的代碼耕驰,你會(huì)發(fā)現(xiàn)爷辱,打開(kāi)的頁(yè)面只在窗口左上角一小塊顯示,看著很別扭朦肘,這是因?yàn)閜yppeteer默認(rèn)窗口大小是800*600饭弓,所以,調(diào)整一下吧媒抠。需要設(shè)置下 window-size 還有 viewport弟断,代碼如下:
import asyncio
from pyppeteer import launch
width, height = 1366, 768
async def main():
browser = await launch(headless=False,args=[f'--window-size={width},{height}'])
page = await browser.newPage()
await page.setViewport({'width': width, 'height': height})
await page.goto('https://www.taobao.com')
await asyncio.sleep(100)
asyncio.get_event_loop().run_until_complete(main())
這樣整個(gè)界面就正常了:
3. 執(zhí)行js腳本
(1)規(guī)避webdriver檢測(cè):
有時(shí)候,為了達(dá)成某些目的(例如屏蔽網(wǎng)站原有js)趴生,我們不可避免得需要執(zhí)行一些js腳本阀趴。執(zhí)行js腳本通過(guò)evaluate方法昏翰。如下所示,我們通過(guò)js來(lái)修改window.navigator.webdriver屬性的值刘急,由此繞過(guò)網(wǎng)站對(duì)webdriver的檢測(cè):
import asyncio
from pyppeteer import launch
async def main():
js1 = '''() =>{
Object.defineProperties(navigator,{
webdriver:{
get: () => false
}
})
}'''
js2 = '''() => {
alert (
window.navigator.webdriver
)
}'''
browser = await launch({'headless':False, 'args':['--no-sandbox'],})
page = await browser.newPage()
await page.goto('https://h5.ele.me/login/')
await page.evaluate(js1)
await page.evaluate(js2)
asyncio.get_event_loop().run_until_complete(main())
在上面代碼中棚菊,通過(guò)page.evalute方法執(zhí)行了兩段js腳本,第一段腳本將webdriver的屬性值設(shè)為false叔汁,第二段代碼在此讀取 webdriver屬性值统求,輸出為false。
OK据块,那剛才所說(shuō)的 webdriver 檢測(cè)問(wèn)題怎樣來(lái)解決呢码邻?其實(shí)淘寶主要通過(guò) window.navigator.webdriver 來(lái)對(duì) webdriver 進(jìn)行檢測(cè),所以我們只需要使用 JavaScript 將它設(shè)置為 false 即可另假,代碼如下:
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(headless=False, args=['--disable-infobars'])
page = await browser.newPage()
await page.goto('https://login.taobao.com/member/login.jhtml?redirectURL=https://www.taobao.com/')
await page.evaluate(
'''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
await asyncio.sleep(100)
asyncio.get_event_loop().run_until_complete(main())
Object.defineProperty()
會(huì)直接在一個(gè)對(duì)象上定義一個(gè)新屬性像屋,或者修改一個(gè)對(duì)象的現(xiàn)有屬性,并返回這個(gè)對(duì)象边篮。如果不指定configurable, writable, enumerable 己莺,則這些屬性默認(rèn)值為false,如果不指定value, get, set戈轿,則這些屬性默認(rèn)值為undefined
這里沒(méi)加輸入用戶名密碼的代碼篇恒,當(dāng)然后面可以自行添加,下面打開(kāi)之后凶杖,我們點(diǎn)擊輸入用戶名密碼胁艰,然后這時(shí)候會(huì)出現(xiàn)一個(gè)滑動(dòng)條,這里滑動(dòng)的話智蝠,就可以通過(guò)了腾么,如圖所示:
import asyncio
import random
from pyppeteer import launch
def input_time_random():
return random.randint(100, 151)
async def main():
browser = await launch({'headless':False})
page = await browser.newPage()
await page.evaluateOnNewDocument(
'''() =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) }''')
await page.evaluateOnNewDocument('''() =>{ window.navigator.chrome = { runtime: {}, }; }''')
await page.evaluateOnNewDocument('''() =>{ Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] }); }''')
await page.evaluateOnNewDocument('''() =>{ Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5,6], }); }''')
await page.goto('https://login.taobao.com')
await page.waitFor(4 * 1000)
# await page.click('#J_QRCodeLogin > div.login-links > a.forget-pwd.J_Quick2Static')
await page.waitFor(3 * 1000)
await page.type('#TPL_username_1', '123123', {'delay': input_time_random() - 50})
await page.type('#TPL_password_1', '232322332', {'delay': input_time_random()})
await page.waitFor(2 * 1000)
el = await page.querySelector('#nc_1_n1z')
box = await el.boundingBox()
await page.hover('#nc_1_n1z')
await page.mouse.down()
await page.mouse.move(box['x']+1000,box['y'], {'delay': random.randint(1000, 2000),'steps':3})
await page.mouse.up()
await page.waitFor(5 * 1000)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
OK,這樣的話我們就成功規(guī)避了 webdriver 的檢測(cè)杈湾,使用鼠標(biāo)拖動(dòng)模擬就可以完成淘寶的登錄了解虱。
還有另一種方法可以進(jìn)一步免去淘寶登錄的煩惱,那就是設(shè)置用戶目錄漆撞。平時(shí)我們已經(jīng)注意到殴泰,當(dāng)我們登錄淘寶之后,如果下次再次打開(kāi)瀏覽器發(fā)現(xiàn)還是登錄的狀態(tài)浮驳。這是因?yàn)樘詫毜囊恍╆P(guān)鍵 Cookies 已經(jīng)保存到本地了悍汛,下次登錄的時(shí)候可以直接讀取并保持登錄狀態(tài)。那么這些信息保存在哪里了呢至会?其實(shí)就是保存在用戶目錄下了离咐,里面不僅包含了瀏覽器的基本配置信息,還有一些 Cache奉件、Cookies 等各種信息都在里面宵蛀,如果我們能在瀏覽器啟動(dòng)的時(shí)候讀取這些信息昆著,那么啟動(dòng)的時(shí)候就可以恢復(fù)一些歷史記錄甚至一些登錄狀態(tài)信息了。這也就解決了一個(gè)問(wèn)題:很多朋友在每次動(dòng) Selenium 或 Pyppeteer 的時(shí)候總是是一個(gè)全新的瀏覽器术陶,那就是沒(méi)有設(shè)置用戶目錄凑懂,如果設(shè)置了它,每次打開(kāi)就不再是一個(gè)全新的瀏覽器了梧宫,它可以恢復(fù)之前的歷史記錄征候,也可以恢復(fù)很多網(wǎng)站的登錄信息。
那么這個(gè)怎么來(lái)做呢祟敛?很簡(jiǎn)單,在啟動(dòng)的時(shí)候設(shè)置 userDataDir 就好了兆解,示例如下:
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(headless=False, userDataDir='./userdata', args=['--disable-infobars'])
page = await browser.newPage()
await page.goto('https://www.taobao.com')
await asyncio.sleep(100)
asyncio.get_event_loop().run_until_complete(main())
好馆铁,這里就是加了一個(gè) userDataDir 的屬性,值為 userdata锅睛,即當(dāng)前目錄的 userdata 文件夾埠巨。我們可以首先運(yùn)行一下,然后登錄一次淘寶现拒,這時(shí)候我們同時(shí)可以觀察到在當(dāng)前運(yùn)行目錄下又多了一個(gè) userdata 的文件夾辣垒,里面的結(jié)構(gòu)是這樣子的:
[圖片上傳失敗...(image-4a5a94-1587565252771)]
具體的介紹可以看官方的一些說(shuō)明,如:https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md印蔬,這里面介紹了 userdatadir 的相關(guān)內(nèi)容勋桶。
再次運(yùn)行上面的代碼,這時(shí)候可以發(fā)現(xiàn)現(xiàn)在就已經(jīng)是登錄狀態(tài)了侥猬,不需要再次登錄了例驹,這樣就成功跳過(guò)了登錄的流程。當(dāng)然可能時(shí)間太久了退唠,Cookies 都過(guò)期了鹃锈,那還是需要登錄的。
(2)執(zhí)行js程序:拖動(dòng)滾輪瞧预。調(diào)用evaluate方法屎债。
import asyncio
from pyppeteer import launch
width, height = 1366, 768
async def main():
browser = await launch(headless=False)
page = await browser.newPage()
await page.setViewport({'width': width, 'height': height})
await page.goto('https://movie.douban.com/typerank?type_name=%E5%8A%A8%E4%BD%9C&type=5&interval_id=100:90&action=')
await asyncio.sleep(3)
#evaluate可以返回js程序的返回值
dimensions = await page.evaluate('window.scrollTo(0,document.body.scrollHeight)')
await asyncio.sleep(3)
print(dimensions)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
5. 設(shè)置userAgent、設(shè)置代理IP
browser = await launch({'headless': True, 'timeout': 500, 'args': ['--disable-extensions',
'--hide-scrollbars',
'--disable-bundled-ppapi-flash',
'--mute-audio',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-gpu',
'--proxy-server={}'.format(get_ip()),
], })
page = await browser.newPage()
await page.setUserAgent("Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36")
await page.setViewport({'width': 1000,'height': 3480,})
6.模擬操作
pyppeteer提供了Keyboard和Mouse兩個(gè)類(lèi)來(lái)實(shí)現(xiàn)模擬操作垢油,前者是用來(lái)實(shí)現(xiàn)鍵盤(pán)模擬盆驹,后者實(shí)現(xiàn)鼠標(biāo)模擬(還有其他觸屏之類(lèi)的就不說(shuō)了)。
主要來(lái)說(shuō)說(shuō)輸入和點(diǎn)擊:
import os
os.environ['PYPPETEER_HOME'] = 'D:\Program Files'
import asyncio
from pyppeteer import launch
async def main():
browser = await launch(headless=False, args=['--disable-infobars'])
page = await browser.newPage()
await page.goto('https://h5.ele.me/login/')
await page.type('form section input', '12345678999') # 模擬鍵盤(pán)輸入手機(jī)號(hào)
await page.click('form section button') # 模擬鼠標(biāo)點(diǎn)擊獲取驗(yàn)證碼
await asyncio.sleep(200)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
上面的模擬操作中滩愁,無(wú)論是模擬鍵盤(pán)輸入還是鼠標(biāo)點(diǎn)擊定位都是通過(guò)css選擇器召娜,似乎pyppeteer的type和click直接模擬操作定位都只能通過(guò)css選擇器(或者是我在官方文檔中沒(méi)找到方法),當(dāng)然惊楼,要間接通過(guò)xpath先定位玖瘸,然后再模擬操作也是可以的秸讹。下一小節(jié)中模擬登陸外賣(mài)平臺(tái)就是用這種方法,不過(guò)雅倒,這種方法要麻煩一些璃诀,不推薦。
7. 節(jié)點(diǎn)交互
import asyncio
from pyppeteer import launch
async def main():
# headless參數(shù)設(shè)為False蔑匣,則變成有頭模式
browser = await launch(
headless=False
)
page = await browser.newPage()
await page.setViewport(viewport={'width': 1280, 'height': 800}) # 設(shè)置頁(yè)面視圖大小
await page.goto('https://www.baidu.com/')
# 節(jié)點(diǎn)交互
await page.type('#kw', '周杰倫', {'delay': 1000}) # id選擇器
await asyncio.sleep(3)
await page.click('#su')
await asyncio.sleep(3)
# 使用選擇器選中標(biāo)簽進(jìn)行點(diǎn)擊
alist = await page.querySelectorAll('.s_tab_inner > a')
a = alist[3]
await a.click()
await asyncio.sleep(3)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
8. 執(zhí)行自定義js---
注入攔截和篩選請(qǐng)求和返回劣欢。page.on監(jiān)聽(tīng)請(qǐng)求與響應(yīng), 并對(duì)請(qǐng)求和響應(yīng)進(jìn)行修改和過(guò)濾
page.on(event, function) ,指定監(jiān)聽(tīng)事件, 與處理函數(shù)
例如: page.on('request', intercept_response)
?
# 請(qǐng)求處理函數(shù)
async def request_check(req):
'''請(qǐng)求過(guò)濾'''
if req.resourceType in ['image', 'media', 'eventsource', 'websocket']:
await req.abort()
else:
await req.continue_()
?
# 響應(yīng)處理函數(shù)
async def intercept_response(res):
resourceType = res.request.resourceType
if resourceType in ['image', 'media']:
resp = await res.text()
print(resp)
下面這個(gè)例子經(jīng)常用來(lái):
- 加快網(wǎng)頁(yè)加載速度
- 快速篩選數(shù)據(jù)api接口
做新聞爬蟲(chóng)的時(shí)候,遇到網(wǎng)頁(yè)有視頻其實(shí)挺尷尬的裁良,首先如果加載視頻會(huì)導(dǎo)致打開(kāi)網(wǎng)頁(yè)比較慢凿将,有時(shí)甚至?xí)?dǎo)致瀏覽器超時(shí)崩潰,其次是視頻的加載可能不同時(shí)帶入一些廣告的超鏈接价脾,對(duì)于提取新聞內(nèi)容會(huì)造成干擾牧抵。
通過(guò)page.setRequestInterception參數(shù)開(kāi)啟注入。
import asyncio
from pyppeteer import launch
async def inject_request(req):
"""
resourceType:
document, stylesheet, image, media, font, script, texttrack, xhr, fetch, eventsource, websocket, manifest, other
"""
if req.resourceType in ['media','image']:
await req.abort()
else:
await req.continue_()
async def inject_response(res):
if res.request.resourceType in ['xhr']:
print(res.request.url)
async def main():
browser = await launch({'headless':False})
page = await browser.newPage()
await page.setRequestInterception(True)
page.on('request', inject_request)
page.on('response',inject_response)
await page.goto('https://movie.douban.com/explore#!type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=0')
await page.waitFor(5 * 1000)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
輸出:
https://m.douban.com/j/puppy/frodo_landing?include=anony_home
https://movie.douban.com/j/search_tags?type=movie&source=
https://movie.douban.com/j/search_tags?type=movie&tag=%E7%83%AD%E9%97%A8&source=
https://movie.douban.com/j/search_subjects?type=movie&tag=%E7%83%AD%E9%97%A8&sort=recommend&page_limit=20&page_start=0
https://movie.douban.com/j/subject_abstract?subject_id=24389792
https://movie.douban.com/j/subject_abstract?subject_id=27119724
先分析inject_request部分:不請(qǐng)求圖片和媒體資源侨把。
async def inject_request(req):
if req.resourceType in ['media','image']:
await req.abort()
else:
await req.continue_()
一般用得比較多的是一個(gè)屬性兩個(gè)方法:
一個(gè)屬性:
resourceType,表示請(qǐng)求的資源類(lèi)型犀变,有document, stylesheet, image, media, font, script, texttrack, xhr, fetch, eventsource, websocket, manifest, other(加粗的是比較常用的資源類(lèi)型)兩個(gè)方法:
abort(),跳過(guò)當(dāng)前請(qǐng)求
continue_(),繼續(xù)當(dāng)前請(qǐng)求
inject_response部分
async def inject_response(res):
if res.request.resourceType in ['xhr']:
print(res.request.url)
一般js動(dòng)態(tài)加載的數(shù)據(jù)連接在xhr資源,所以我這里把網(wǎng)頁(yè)請(qǐng)求的xhr資源都打印出來(lái)秋柄,如果這里沒(méi)有數(shù)據(jù)連接获枝,那就是在document里面了,比F12清晰一點(diǎn)骇笔。
9. 獲取瀏覽器依據(jù)加載的圖片內(nèi)容, Selenium與Pyppeteer相同
執(zhí)行JS, 返回圖片的二進(jìn)制的Base64編碼, 參照: https://www.w3ctech.com/topic/767
'''
() => {
var img = document.getElementById("%s");
var canvas = document.createElement("canvas");
canvas.width = %s;
canvas.height = %s;
var ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0);
var dataURL = canvas.toDataURL("image/png");
return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");}''' % (id, width, height)
10. 切換瀏覽器的tag
# 在Pyppeteer中每一個(gè)標(biāo)簽頁(yè)就是一個(gè)page對(duì)象, 切換page對(duì)象就是切換標(biāo)簽頁(yè)
for _page in await browser.pages() :
if _page != page:
await _page.close()
三省店、案例
綜合應(yīng)用:爬取頭條和網(wǎng)易的新聞標(biāo)題
import asyncio
from pyppeteer import launch
from lxml import etree
async def main():
browser = await launch(headless=False) # headless參數(shù)設(shè)為False,則變成有頭模式
page1 = await browser.newPage()
await page1.setViewport(viewport={'width': 1280, 'height': 800}) # 設(shè)置頁(yè)面視圖大小
await page1.goto('https://www.toutiao.com/')
await asyncio.sleep(2)
page_text = await page1.content() # 打印頁(yè)面文本
page2 = await browser.newPage()
await page2.setViewport(viewport={'width': 1280, 'height': 800})
await page2.goto('https://news.163.com/domestic/')
await page2.evaluate('window.scrollTo(0,document.body.scrollHeight)')
page_text1 = await page2.content()
await browser.close()
return {'wangyi':page_text1,'toutiao':page_text}
def parse(task):
content_dic = task.result()
wangyi = content_dic['wangyi']
toutiao = content_dic['toutiao']
tree = etree.HTML(toutiao)
a_list = tree.xpath('//div[@class="title-box"]/a')
for a in a_list:
title = a.xpath('./text()')[0]
print('toutiao:',title)
tree = etree.HTML(wangyi)
div_list = tree.xpath('//div[@class="data_row news_article clearfix "]')
print(len(div_list))
for div in div_list:
title = div.xpath('.//div[@class="news_title"]/h3/a/text()')[0]
print('wangyi:',title)
tasks = []
task1 = asyncio.ensure_future(main())
task1.add_done_callback(parse)
tasks.append(task1)
asyncio.get_event_loop().run_until_complete(asyncio.wait(tasks))
爬取結(jié)果:
toutiao: 「央視快評(píng)」堅(jiān)守初心 為國(guó)奉獻(xiàn)
toutiao: 南航一A380客機(jī)北京降落時(shí)遭冰雹風(fēng)擋現(xiàn)裂痕 已平安降落無(wú)人受傷
toutiao: 美國(guó)正開(kāi)啟第二戰(zhàn)場(chǎng):圍獵中國(guó)高科技企業(yè) |“雙線作戰(zhàn)”戰(zhàn)略意圖
toutiao: 云南省陸良縣:農(nóng)民給供銷(xiāo)社打“白條”
toutiao: 媒體:90后副縣長(zhǎng)若非靠拼爹上位 需拿出業(yè)績(jī)服眾
toutiao: 南航A380飛北京客機(jī)遭遇冰雹襲擊笨触,擋風(fēng)玻璃全碎
toutiao: 秘魯北部發(fā)生7.8級(jí)地震
toutiao: 1958年萨西,由捷克斯洛伐克援建的北京電影洗印廠曾為全國(guó)行業(yè)的老大
toutiao: 一箭60星,發(fā)射成功旭旭!馬斯克衛(wèi)星互聯(lián)網(wǎng)計(jì)劃啟動(dòng)
69
wangyi: 中美經(jīng)貿(mào)摩擦背后:有人在干谎脯,有人在騙
wangyi: 華為回應(yīng)個(gè)別標(biāo)準(zhǔn)組織撤銷(xiāo)資格:產(chǎn)品服務(wù)不受影響
wangyi: 隔空約架?中方主播劉欣23年前就贏得國(guó)際演講比賽
wangyi: 從錢(qián)學(xué)森到任正非 中國(guó)教育有多少底氣應(yīng)對(duì)全球化
wangyi: 2個(gè)月內(nèi)二度履新 35歲清華博士任安徽省直單位領(lǐng)導(dǎo)
wangyi: 南陽(yáng)“水氫發(fā)動(dòng)機(jī)汽車(chē)”引熱議 官方回應(yīng)四大疑問(wèn)
wangyi: 31歲北大博士躋身縣委常委 主筆6萬(wàn)字全縣發(fā)展規(guī)劃
wangyi: 干部退休15年后投案自首 省委巡視辦:頭一次碰到
wangyi: 臺(tái)灣被標(biāo)注"中國(guó)臺(tái)灣省" 臺(tái)外事部門(mén)要求更正被拒
wangyi: 190天3次現(xiàn)場(chǎng)辦公!南陽(yáng)領(lǐng)導(dǎo)為何鐘愛(ài)青年汽車(chē)項(xiàng)目
2. 淘寶登錄
import time
import random
import asyncio
import pyppeteer
class LoginTaoBao:
"""
類(lèi)異步
"""
pyppeteer.DEBUG = True
page = None
async def _injection_js(self):
"""注入js
"""
await self.page.evaluate('''() =>{
Object.defineProperties(navigator,{
webdriver:{
get: () => false
}
})
}''')
async def _init(self):
"""初始化瀏覽器
"""
browser = await pyppeteer.launch({'headless': False,
'args': [
'--window-size={1300},{600}'
'--disable-extensions',
'--hide-scrollbars',
'--disable-bundled-ppapi-flash',
'--mute-audio',
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-gpu',
],
'dumpio': True,
})
self.page = await browser.newPage()
# 設(shè)置瀏覽器頭部
await self.page.setUserAgent('Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299')
# 設(shè)置瀏覽器大小
await self.page.setViewport({'width': 1200, 'height': 600})
async def get_cookie(self):
cookies_list = await self.page.cookies()
cookies = ''
for cookie in cookies_list:
str_cookie = '{0}={1};'
str_cookie = str_cookie.format(cookie.get('name'), cookie.get('value'))
cookies += str_cookie
print(cookies)
return cookies
async def mouse_slider(self):
"""滑動(dòng)滑塊
"""
await asyncio.sleep(3)
try:
await self.page.hover('#nc_1_n1z')
await self.page.mouse.down() # 鼠標(biāo)按下按鈕
await self.page.mouse.move(2000, 0, {'steps': 30}) # 移動(dòng)鼠標(biāo)
await self.page.mouse.up() # 松開(kāi)鼠標(biāo)
await asyncio.sleep(2)
except Exception as e:
print(e, ' :錯(cuò)誤')
return None
else:
await asyncio.sleep(3)
# 獲取元素內(nèi)容
slider_again = await self.page.querySelectorEval('#nc_1__scale_text', 'node => node.textContent')
if slider_again != '驗(yàn)證通過(guò)':
return None
else:
print('驗(yàn)證通過(guò)')
return True
async def main(self, username_, pwd_):
"""登陸
"""
await self._init() # 初始化瀏覽器
await self.page.goto('https://login.taobao.com') # 打開(kāi)淘寶登陸頁(yè)面
await self._injection_js() # 注入js
await self.page.click('div.login-switch') # 點(diǎn)擊密碼登陸按鈕
time.sleep(random.random() * 2)
await self.page.type('#TPL_username_1', username_, {'delay': random.randint(100, 151) - 50}) # 輸入用戶名
await self.page.type('#TPL_password_1', pwd_, {'delay': random.randint(100, 151)}) # 輸入密碼
time.sleep(random.random() * 2)
slider = await self.page.querySelector('#nc_1__scale_text') # 獲取滑塊元素
if slider:
print('有滑塊')
# 移動(dòng)滑塊
flag = await self.mouse_slider()
if not flag:
print('滑動(dòng)滑塊失敗')
return None
time.sleep(random.random() + 1.5)
# 點(diǎn)擊登陸
print('點(diǎn)擊登陸')
await self.page.click('#J_SubmitStatic')
await asyncio.sleep(100)
else:
print('沒(méi)滑塊')
# 按下回車(chē)
await self.page.keyboard.press('Enter')
if __name__ == '__main__':
username = input('淘寶用戶名')
pwd = input('密碼')
login = LoginTaoBao()
loop = asyncio.get_event_loop()
task = asyncio.ensure_future(login.main(username, pwd))
loop.run_until_complete(task)
3. 某電商平臺(tái)模擬登陸
我曾經(jīng)用selenium + chrome 實(shí)現(xiàn)了模擬登陸這個(gè)電商平臺(tái)持寄,但是實(shí)在是有些麻煩源梭,繞過(guò)對(duì)webdriver的檢測(cè)不難,但是稍味,通過(guò)webdriver對(duì)瀏覽器的每一步操作都會(huì)留下特殊的痕跡废麻,會(huì)被平臺(tái)識(shí)別,這個(gè)必須通過(guò)重新編譯chrome的webdriver才能實(shí)現(xiàn)模庐,麻煩得讓人想哭烛愧。不說(shuō)了,都是淚,下面直接上用pyppeteer實(shí)現(xiàn)的代碼:
import os
os.environ['PYPPETEER_HOME'] = 'D:\Program Files'
import asyncio
from pyppeteer import launch
def screen_size():
"""使用tkinter獲取屏幕大小"""
import tkinter
tk = tkinter.Tk()
width = tk.winfo_screenwidth()
height = tk.winfo_screenheight()
tk.quit()
return width, height
async def main():
js1 = '''() =>{
Object.defineProperties(navigator,{
webdriver:{
get: () => false
}
})
}'''
js2 = '''() => {
alert (
window.navigator.webdriver
)
}'''
browser = await launch({'headless':False, 'args':['--no-sandbox'],})
page = await browser.newPage()
width, height = screen_size()
await page.setViewport({ # 最大化窗口
"width": width,
"height": height
})
await page.goto('https://h5.ele.me/login/')
await page.evaluate(js1)
await page.evaluate(js2)
input_sjh = await page.xpath('//form/section[1]/input[1]')
click_yzm = await page.xpath('//form/section[1]/button[1]')
input_yzm = await page.xpath('//form/section[2]/input[1]')
but = await page.xpath('//form/section[2]/input[1]')
print(input_sjh)
await input_sjh[0].type('*****手機(jī)號(hào)********')
await click_yzm[0].click()
ya = input('請(qǐng)輸入驗(yàn)證碼:')
await input_yzm[0].type(str(ya))
await but[0].click()
await asyncio.sleep(3)
await page.goto('https://www.ele.me/home/')
await asyncio.sleep(100)
await browser.close()
asyncio.get_event_loop().run_until_complete(main())
登錄時(shí)怜姿,由于等待時(shí)間過(guò)長(zhǎng)(我猜的)導(dǎo)致出現(xiàn)以下錯(cuò)誤:
pyppeteer.errors.NetworkError: Protocol Error (Runtime.callFunctionOn): Session closed. Most likely the page has been closed.
在github上找到了解決方法慎冤,似乎只能改源碼,找到pyppeteer包下的connection.py模塊沧卢,在其43行和44行改為下面這樣:
self._ws = websockets.client.connect(
# self._url, max_size=None, loop=self._loop)
self._url, max_size=None, loop=self._loop, ping_interval=None, ping_timeout=None)
再次運(yùn)行就沒(méi)問(wèn)題了蚁堤。可以成功繞過(guò)官方對(duì)webdriver的檢測(cè)但狭,登錄成功披诗,諸位可以自己嘗試一下。
四立磁、爬取京東商城
from pyppeteer import launch
import asyncio
def screen_size():
"""使用tkinter獲取屏幕大小"""
import tkinter
tk = tkinter.Tk()
width = tk.winfo_screenwidth()
height = tk.winfo_screenheight()
tk.quit()
return width, height
async def main(url):
browser = await launch({'headless': False, 'args': ['--no-sandbox'], })
# browser = await launch({'args': ['--no-sandbox'], })
page = await browser.newPage()
width, height = screen_size()
await page.setViewport(viewport={"width": width, "height": height})
await page.setJavaScriptEnabled(enabled=True)
await page.setUserAgent(
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 '
'(KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36 Edge/16.16299'
)
await page.goto(url)
await asyncio.sleep(2*1000)
await page.evaluate('window.scrollBy(0, document.body.scrollHeight)')
# await asyncio.sleep(1)
# content = await page.content()
li_list = await page.xpath('//*[@id="J_goodsList"]/ul/li')
print(li_list)
'''
[
<pyppeteer.element_handle.ElementHandle object at 0x000000000B994128>,
<pyppeteer.element_handle.ElementHandle object at 0x000000000B8CDD68>,
...一個(gè)個(gè)pyppeteer對(duì)象]
'''
item_list = []
for li in li_list:
a = await li.xpath('.//div[@class="p-img"]/a')
detail_url = await (await a[0].getProperty("href")).jsonValue()
promo_words = await (await a[0].getProperty("title")).jsonValue()
a_ = await li.xpath('.//div[@class="p-commit"]/strong/a')
p_commit = await (await a_[0].getProperty("textContent")).jsonValue()
i = await li.xpath('./div/div[3]/strong/i')
price = await (await i[0].getProperty("textContent")).jsonValue()
em = await li.xpath('./div/div[4]/a/em')
title = await (await em[0].getProperty("textContent")).jsonValue()
item = {
"title": title,
"detail_url": detail_url,
"promo_words": promo_words,
'p_commit': p_commit,
'price': price
}
item_list.append(item)
# print(item)
# break
# print(content)
await page_close(browser)
return item_list
async def page_close(browser):
for _page in await browser.pages():
await _page.close()
await browser.close()
msg = "手機(jī)"
url = "https://search.jd.com/Search?keyword={}&enc=utf-8&qrst=1&rt=1&stop=1&vt=2&wq={}&cid2=653&cid3=655&page={}"
task_list = []
for i in range(1, 2):
page = i * 2 - 1
url = url.format(msg, msg, page)
task_list.append(main(url))
loop = asyncio.get_event_loop()
results = loop.run_until_complete(asyncio.gather(*task_list))
# print(results, len(results))
for i in results:
print(i, len(i))
print('*' * 100)
# soup = BeautifulSoup(content, 'lxml')
# div = soup.find('div', id='J_goodsList')
# for i, li in enumerate(div.find_all('li', class_='gl-item')):
# if li.select('.p-img a'):
# print(li.select('.p-img a')[0]['href'], i)
# print(li.select('.p-price i')[0].get_text(), i)
# print(li.select('.p-name em')[0].text, i)
# else:
# print("#" * 200)
# print(li)
detail_url = await (await a[0].getProperty("href")).jsonValue() # 取屬性值
i = await li.xpath('./div/div[3]/strong/i')
price = await (await i[0].getProperty("textContent")).jsonValue() # 取文本
em = await li.xpath('./div/div[4]/a/em')
title = await (await em[0].getProperty("textContent")).jsonValue()
REF
http://www.reibang.com/p/84f39941f3ea
https://blog.csdn.net/freeking101/article/details/93331204
https://www.cnblogs.com/baihuitestsoftware/p/10531462.html
<pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word;">pyppeteer github 地址:https://github.com/miyakogi/pyppeteer
pyppeteer 英文文檔地址:https://miyakogi.github.io/pyppeteer/
pyppeteer 官方文檔 API Reference :https://miyakogi.github.io/pyppeteer/reference.html
puppeteer( Nodejs 版 selenium )快速入門(mén):https://blog.csdn.net/freeking101/article/details/91542887
爬蟲(chóng)界又出神器|一款比selenium更高效的利器:https://blog.csdn.net/chen801090/article/details/93216278
python爬蟲(chóng)利器 pyppeteer(模擬瀏覽器) 實(shí)戰(zhàn):https://blog.csdn.net/xiaoming0018/article/details/89841728</pre>