安裝
python3 -m pip install pyppeteer
最好是py3.5+javascript
手動安裝java
你懂的,天朝網絡環境很複雜,若是要用pyppeteer
本身綁定的chromium
,半天都下載不下來,因此咱們要手動安裝,而後在程序裏面指定executablePath
。node
下載地址python
模塊介紹
啓動pyppeteer.launch
launch 瀏覽器,能夠傳入一個字典來配置幾個options,好比:web
browser = await pyppeteer.launch({ 'headless': False, # 關閉無頭模式 'devtools': True, # 打開 chromium 的 devtools // 'executablePath': '你下載的Chromium.app/Contents/MacOS/Chromiu', //# 瀏覽器的存放地址 'args': [ '--disable-extensions', '--hide-scrollbars', '--disable-bundled-ppapi-flash', '--mute-audio', '--no-sandbox', '--disable-setuid-sandbox', '--disable-gpu', ], 'dumpio': True, })
browser = await launch({'headless': False,'dumpio':True, 'autoClose':False,'args': ['--no-sandbox', '--window-size=1366,850']}) await page.setViewport({'width':1366,'height':768})
注入腳本page.evaluate
await page.evaluate(""" () =>{ Object.defineProperties(navigator,{ webdriver:{ get: () => false } }) } """)
咱們會看到這一步很是關鍵,由於
puppeteer
出於政策考慮(這個詞用的不是很好,就是那個意思)會設置window.navigator.webdriver
爲true
,告訴網站我是一個 webdriver 驅動的瀏覽器。有些網站比較聰明(反爬措施作得比較好),就會經過這個來判斷對方是否是爬蟲程序。ajax
截獲response和request
await page.setRequestInterception(True) page.on('request', intercept_request) page.on('response', intercept_response)
intercept_request
和intercept_response
至關因而註冊的兩個回調函數,在瀏覽器發出請求和獲取到請求以前指向這兩個函數。redis
好比能夠這樣禁止獲取圖片、多媒體資源和發起 websocket 請求:chrome
async def intercept_request(req): """請求過濾""" if req.resourceType in ['image', 'media', 'eventsource', 'websocket']: await req.abort() else: await req.continue_()
而後每次獲取到請求以後將內容打印出來(這裏只打印了fetch
和xhr
類型response 的內容):json
async def intercept_response(res): resourceType = res.request.resourceType if resourceType in ['xhr', 'fetch']: resp = await res.text() print(resp)
一共有哪些resourceType,pyppeteer文檔裏面有:api
頁面自動下拉的代碼
健壯的代碼
請求鉤子
async def request_check(req): '''請求過濾''' if req.resourceType in ['image', 'media', 'eventsource', 'websocket']: await req.abort() else: await req.continue_() await page.setRequestInterception(True) page.on('request', request_check)
判斷加載是否完成
async def goto(page, url): while True: try: await page.goto(url, { 'timeout': 0, 'waitUntil': 'networkidle0' }) break except (pyppeteer.errors.NetworkError, pyppeteer.errors.PageError) as ex: # 無網絡 'net::ERR_INTERNET_DISCONNECTED','net::ERR_TUNNEL_CONNECTION_FAILED' if 'net::' in str(ex): await asyncio.sleep(10) else: rais
注入JS文件
from pathlib import Path CURDIR = Path(__file__).parent JS_AJAX_HOOK_LIB = str(CURDIR / 'static' / 'ajaxhook.min.js') await page.addScriptTag(path=JS_AJAX_HOOK_LIB)
這樣注入的js文件不能有中文,由於 pyppeteer
裏面打開文件用的是默認編碼,能夠 hook 住 open
函數來解決。
# 由於 pyppeteer 庫裏面 addScriptTag 用的是系統默認編碼,致使js文件裏面不能有中文 pyppeteer.frame_manager.open = lambda file: open(file, encoding='utf8')
完整的跑一個小項目
import asyncio from pyppeteer import launch import time async def main():exepath = 'C:/Users/tester02/AppData/Local/Google/Chrome/Application/chrome.exe' browser = await launch({'executablePath': exepath, 'headless': False, 'slowMo': 30}) page = await browser.newPage() await page.setViewport({'width': 1366, 'height': 768}) await page.goto('http://192.168.2.66') // # 請求網址 await page.type("#Login_Name_Input", "test02") # 第一個參數是選擇器,選中某個元素,第二個參數是要輸入的內容 await page.type("#Login_Password_Input", "12345678", ) await page.waitFor(1000) # 停頓 await page.click("#Login_Login_Btn") await page.waitFor(3000) await browser.close()# 關閉瀏覽器 asyncio.get_event_loop().run_until_complete(main()) # 執行main函數
登錄谷歌
import asyncio import time from pyppeteer import launch async def gmailLogin(username, password, url): #'headless': False若是想要瀏覽器隱藏更改False爲True # 127.0.0.1:1080爲代理ip和端口,這個根據本身的本地代理進行更改,若是是vps裏或者全局模式能夠刪除掉'--proxy-server=127.0.0.1:1080' browser = await launch({'headless': False, 'args': ['--no-sandbox', '--proxy-server=127.0.0.1:1080']}) page = await browser.newPage() await page.setUserAgent( 'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36') await page.goto(url) # 輸入Gmail await page.type('#identifierId', username) # 點擊下一步 await page.click('#identifierNext > content') page.mouse # 模擬真實點擊 time.sleep(10) # 輸入password await page.type('#password input', password) # 點擊下一步 await page.click('#passwordNext > content > span') page.mouse # 模擬真實點擊 time.sleep(10) # 點擊安全檢測頁面的DONE # await page.click('div > content > span')#若是本機以前登陸過,而且page.setUserAgent設置爲以前登陸成功的瀏覽器user-agent了, # 就不會出現安全檢測頁面,這裏若是有須要的本身根據需求進行更改,可是仍是推薦先用經常使用瀏覽器登陸成功後再用python程序進行登陸。 # 登陸成功截圖 await page.screenshot({'path': './gmail-login.png', 'quality': 100, 'fullPage': True}) #打開谷歌全家桶跳轉,以Youtube爲例 await page.goto('https://www.youtube.com') time.sleep(10) if __name__ == '__main__': username = '你的gmail包含@gmail.com' password = r'你的gmail密碼' url = 'https://gmail.com' loop = asyncio.get_event_loop() loop.run_until_complete(gmailLogin(username, password, url)) # 代碼由三分醉編寫,網址www.sanfenzui.com,參考以下文章: # https://blog.csdn.net/Chen_chong__/article/details/82950968
登錄淘寶
async def taobao_login(username, password, url): """ 淘寶登陸主程序 :param username: 用戶名 :param password: 密碼 :param url: 登陸網址 :return: 登陸cookies """ ### await page.click('#J_QRCodeLogin > div.login-links > a.forget-pwd.J_Quick2Static') page.mouse time.sleep(1) # 輸入用戶名,密碼 await page.type('#TPL_username_1', username, {'delay': input_time_random() - 50}) # delay是限制輸入的時間 await page.type('#TPL_password_1', password, {'delay': input_time_random()}) time.sleep(2) # 檢測頁面是否有滑塊。原理是檢測頁面元素。 slider = await page.Jeval('#nocaptcha', 'node => node.style') # 是否有滑塊 if slider: print('當前頁面出現滑塊') # await page.screenshot({'path': './headless-login-slide.png'}) # 截圖測試 flag, page = await mouse_slide(page=page) # js拉動滑塊過去。 if flag: await page.keyboard.press('Enter') # 確保內容輸入完畢,少數頁面會自動完成按鈕點擊 print("print enter", flag) await page.evaluate('''document.getElementById("J_SubmitStatic").click()''') # 若是沒法經過回車鍵完成點擊,就調用js模擬點擊登陸按鈕。 time.sleep(2) cookies_list = await page.cookies() print(cookies_list) return await get_cookie(page) # 導出cookie 完成登錄後就能夠拿着cookie玩各類各樣的事情了。 else: print("") await page.keyboard.press('Enter') print("print enter") await page.evaluate('''document.getElementById("J_SubmitStatic").click()''') await page.waitFor(20) await page.waitForNavigation() try: global error # 檢測是不是帳號密碼錯誤 print("error_1:", error) error = await page.Jeval('.error', 'node => node.textContent') print("error_2:", error) except Exception as e: error = None finally: if error: print('確保帳戶安全從新入輸入') # 程序退出。 loop.close() else: print(page.url) return await get_cookie(page)
今日頭條文章
import asyncio from pyppeteer import launch async def main(): # headless參數設爲False,則變成有頭模式 browser = await launch( # headless=False ) page = await browser.newPage() # 設置頁面視圖大小 await page.setViewport(viewport={'width':1280, 'height':800}) # 是否啓用JS,enabled設爲False,則無渲染效果 await page.setJavaScriptEnabled(enabled=True) await page.goto('https://www.toutiao.com/') # 打印頁面cookies print(await page.cookies()) # 打印頁面文本 print(await page.content()) # 打印當前頁標題 print(await page.title()) # 抓取新聞標題 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) # 關閉瀏覽器 await browser.close() asyncio.get_event_loop().run_until_complete(main())
Pyppeteer和Puppeteer的不一樣點
- Pyppeteer支持字典和關鍵字傳參,Puppeteer只支持字典傳參
# Puppeteer只支持字典傳參 browser = await launch({'headless': True}) # Pyppeteer支持字典和關鍵字傳參 browser = await launch({'headless': True}) browser = await launch(headless=True)
- 元素選擇器方法名 $變爲querySelector
# Puppeteer使用$符 Page.$()/Page.$$()/Page.$x() # Pyppeteer使用Python風格的函數名 Page.querySelector()/Page.querySelectorAll()/Page.xpath() # 簡寫方式爲: Page.J(), Page.JJ(), and Page.Jx()
- Page.evaluate() 和 Page.querySelectorEval()的參數
Puppeteer的evaluate()方法使用JavaScript原生函數或JavaScript表達式字符串。Pyppeteer的evaluate()方法只使用JavaScript字符串,該字符串能夠是函數也能夠是表達式,Pyppeteer會進行自動判斷。但有時會判斷錯誤,若是字符串被判斷成了函數,而且報錯,能夠添加選項force_expr=True
,強制Pyppeteer做爲表達式處理。
獲取頁面內容:
content = await page.evaluate('document.body.textContent', force_expr=True)
獲取元素的內部文字:
element = await page.querySelector('h1') title = await page.evaluate('(element) => element.textContent', element)
爬蟲例子
# -*- coding: utf-8 -*- import asyncio from pyppeteer import launch from pyquery import PyQuery as pq # 最好指定一下本身瀏覽器的位置,若是不指定會自動下載,太慢了... executable_path = "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" # 示例一: 渲染頁面 async def crawl_page(): # 打開瀏覽器 browser = await launch(executablePath=executable_path) # 打開tab 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() # 示例二:截圖,保存pdf,執行js async def save_pdf(): browser = await launch(executablePath=executable_path) page = await browser.newPage() await page.goto('http://quotes.toscrape.com/js/') # 網頁截圖保存 await page.screenshot(path='example.png') # 網頁導出 PDF 保存 await page.pdf(path='example.pdf') # 執行 JavaScript dimensions = await page.evaluate('''() => { return { width: document.documentElement.clientWidth, height: document.documentElement.clientHeight, deviceScaleFactor: window.devicePixelRatio, } }''') print(dimensions) await browser.close() if __name__ == '__main__': asyncio.get_event_loop().run_until_complete(crawl_page()) # asyncio.get_event_loop().run_until_complete(save_pdf(
如何創建一個cookie池
必須多個帳號,那麼如何註冊多個淘寶帳號呢。。
- 能夠經過第三方提供手機號驗證碼服務商,經過pyppeteer註冊帳號,保存帳號信息
- 登陸帳號並保存在redis
- 開線程檢查帳號是否已過時,若過時從新登陸便可