原文:https://mp.weixin.qq.com/s/Iz-DY1UrSfVFRFh5CyHl3Qhtml
一.簡介
Puppeteer 是 Google 基於 Node.js 開發的一個工具,有了它咱們能夠經過 JavaScript 來控制 Chrome 瀏覽器的一些操做,固然也能夠用做網絡爬蟲上,其 API 極其完善,功能很是強大。 而 Pyppeteer 又是什麼呢?它其實是 Puppeteer 的 Python 版本的實現,但他不是 Google 開發的,是一位來自於日本的工程師依據 Puppeteer 的一些功能開發出來的非官方版本。git
在 Pyppetter 中,實際上它背後也是有一個相似 Chrome 瀏覽器的 Chromium 瀏覽器在執行一些動做進行網頁渲染,首先說下 Chrome 瀏覽器和 Chromium 瀏覽器的淵源。github
Chromium 是谷歌爲了研發 Chrome 而啓動的項目,是徹底開源的。兩者基於相同的源代碼構建,Chrome 全部的新功能都會先在 Chromium 上實現,待驗證穩定後纔會移植,
所以 Chromium 的版本更新頻率更高,也會包含不少新的功能,但做爲一款獨立的瀏覽器,Chromium 的用戶羣體要小衆得多。兩款瀏覽器「同根同源」,它們有着一樣的 Logo,
但配色不一樣,Chrome 由藍紅綠黃四種顏色組成,而 Chromium 由不一樣深度的藍色構成。
Pyppeteer 就是依賴於 Chromium 這個瀏覽器來運行的。那麼有了 Pyppeteer 以後,咱們就能夠免去那些繁瑣的環境配置等問題。若是第一次運行的時候,Chromium 瀏覽器沒有安裝,那麼程序會幫咱們自動安裝和配置,就免去了繁瑣的環境配置等工做。另外 Pyppeteer 是基於 Python 的新特性 async 實現的,因此它的一些執行也支持異步操做,效率相對於 Selenium 來講也提升了。web
安裝:瀏覽器
pip3 install pyppeteer
二.抓取js
而Pyppeteer 模擬jsvascript渲染,抓取信息。網絡
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())
運行結果:less
10
那麼這裏面的過程發生了什麼?異步
實際上,Pyppeteer 整個流程就完成了瀏覽器的開啓、新建頁面、頁面加載等操做。另外 Pyppeteer 裏面進行了異步操做,因此須要配合 async/await 關鍵詞來實現。async
首先, launch 方法會新建一個 Browser 對象,而後賦值給 browser,而後調用 newPage 方法至關於瀏覽器中新建了一個選項卡,同時新建了一個 Page 對象。然 後 Page 對象調用了 goto 方法就至關於在瀏覽器中輸入了這個 URL,瀏覽器跳轉到了對應的頁面進行加載,加載完成以後再調用 content 方法,返回當前瀏覽器頁面的源代碼。而後進一步地,咱們用 pyquery 進行一樣地解析,就能夠獲得 JavaScript 渲染的結果了。函數
另外其餘的一些方法如調用 asyncio 的 get_event_loop 等方法的相關操做則屬於 Python 異步 async 相關的內容了,你們若是不熟悉能夠了解下 Python 的 async/await 的相關知識。
好,經過上面的代碼,咱們就能夠完成 JavaScript 渲染頁面的爬取了。
在這個過程當中,咱們沒有配置 Chrome 瀏覽器,沒有配置瀏覽器驅動,免去了一些繁瑣的步驟,一樣達到了 Selenium 的效果,還實現了異步抓取,爽歪歪!
接下來咱們再看看另一個例子,這個例子能夠模擬網頁截圖,保存 PDF,另外還能夠執行自定義的 JavaScript 得到特定的內容,代碼以下
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())
這裏咱們又用到了幾個新的 API,完成了網頁截圖保存、網頁導出 PDF 保存、執行 JavaScript 並返回對應數據。
首先 screenshot 方法能夠傳入保存的圖片路徑,另外還能夠指定保存格式 type、清晰度 quality、是否全屏 fullPage、裁切 clip 等各個參數實現截圖。
可見其內容也是 JavaScript 渲染後的內容,另外這個方法還能夠指定放縮大小 scale、頁碼範圍 pageRanges、寬高 width 和 height、方向 landscape 等等參數,導出定製化的 pdf 用這個方法就十分方便。
最後咱們又調用了 evaluate 方法執行了一些 JavaScript,JavaScript 傳入的是一個函數,使用 return 方法返回了網頁的寬高、像素大小比率三個值,最後獲得的是一個 JSON 格式的對象,內容以下:
{'width': 800, 'height': 600, 'deviceScaleFactor': 1}
OK,實例就先感覺到這裏,還有太多太多的功能還沒說起。
總之利用 Pyppeteer 咱們能夠控制瀏覽器執行幾乎全部動做,想要的操做和功能基本均可以實現,用它來自由地控制爬蟲固然就不在話下了。
二.詳細用法
https://miyakogi.github.io/pyppeteer/reference.html
開啓瀏覽器
使用 Pyppeteer 的第一步即是啓動瀏覽器,首先咱們看下怎樣啓動一個瀏覽器,其實就至關於咱們點擊桌面上的瀏覽器圖標同樣,把它開起來。用 Pyppeteer 完成一樣的操做,只須要調用 launch 方法便可。
咱們先看下 launch 方法的 API,連接爲:https://miyakogi.github.io/pyppeteer/reference.html#pyppeteer.launcher.launch,其方法定義以下:
pyppeteer.launcher.launch(options: dict = None, **kwargs) → pyppeteer.browser.Browser
能夠看到它處於 launcher 模塊中,參數沒有在聲明中特別指定,返回類型是 browser 模塊中的 Browser 對象,另外觀察源碼發現這是一個 async 修飾的方法,因此調用它的時候須要使用 await。
接下來看看它的參數:
-
ignoreHTTPSErrors (bool): 是否要忽略 HTTPS 的錯誤,默認是 False。
-
headless (bool): 是否啓用 Headless 模式,即無界面模式,若是 devtools 這個參數是 True 的話,那麼該參數就會被設置爲 False,不然爲 True,即默認是開啓無界面模式的。
-
executablePath (str): 可執行文件的路徑,若是指定以後就不須要使用默認的 Chromium 了,能夠指定爲已有的 Chrome 或 Chromium。
-
slowMo (int|float): 經過傳入指定的時間,能夠減緩 Pyppeteer 的一些模擬操做。
-
args (List[str]): 在執行過程當中能夠傳入的額外參數。
-
ignoreDefaultArgs (bool): 不使用 Pyppeteer 的默認參數,若是使用了這個參數,那麼最好經過 args 參數來設定一些參數,不然可能會出現一些意想不到的問題。這個參數相對比較危險,慎用。
-
handleSIGINT (bool): 是否響應 SIGINT 信號,也就是可使用 Ctrl + C 來終止瀏覽器程序,默認是 True。
-
handleSIGTERM (bool): 是否響應 SIGTERM 信號,通常是 kill 命令,默認是 True。
-
handleSIGHUP (bool): 是否響應 SIGHUP 信號,即掛起信號,好比終端退出操做,默認是 True。
-
dumpio (bool): 是否將 Pyppeteer 的輸出內容傳給 process.stdout 和 process.stderr 對象,默認是 False。
-
userDataDir (str): 即用戶數據文件夾,便可以保留一些個性化配置和操做記錄。
-
env (dict): 環境變量,能夠經過字典形式傳入。
-
devtools (bool): 是否爲每個頁面自動開啓調試工具,默認是 False。若是這個參數設置爲 True,那麼 headless 參數就會無效,會被強制設置爲 False。
-
logLevel (int|str): 日誌級別,默認和 root logger 對象的級別相同。
-
autoClose (bool): 當一些命令執行完以後,是否自動關閉瀏覽器,默認是 True。
-
loop (asyncio.AbstractEventLoop): 時間循環對象。
好了,知道這些參數以後,咱們能夠先試試看。
首先能夠試用下最經常使用的參數 headless,若是咱們將它設置爲 True 或者默認不設置它,在啓動的時候咱們是看不到任何界面的,若是把它設置爲 False,那麼在啓動的時候就能夠看到界面了,通常咱們在調試的時候會把它設置爲 False,在生產環境上就能夠設置爲 True,咱們先嚐試一下關閉 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())
運行以後看不到任何控制檯輸出,可是這時候就會出現一個空白的 Chromium 界面了:
可是能夠看到這就是一個光禿禿的瀏覽器而已,看一下相關信息
看到了,這就是 Chromium,上面還寫了開發者內部版本,能夠認爲是開發版的 Chrome 瀏覽器就好。
另外咱們還能夠開啓調試模式,好比在寫爬蟲的時候會常常須要分析網頁結構還有網絡請求,因此開啓調試工具仍是頗有必要的,咱們能夠將 devtools 參數設置爲 True, 這樣每開啓一個界面就會彈出一個調試窗口,很是方便,示例以下:
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())
剛纔說過 devtools 這個參數若是設置爲了 True,那麼 headless 就會被關閉了,界面始終會顯現出來。在這裏咱們新建了一個頁面,打開了百度,界面運行效果以下:
這時候咱們能夠看到上面的一條提示:"Chrome 正受到自動測試軟件的控制",這個提示條有點煩,那咋關閉呢?這時候就須要用到 args 參數了,禁用操做以下:
browser = await launch(headless=False, args=['--disable-infobars'])
這裏就再也不寫完整代碼了,就是在 launch 方法中,args 參數經過 list 形式傳入便可,這裏使用的是 --disable-infobars 的參數。
另外有人就說了,這裏你只是把提示關閉了,有些網站仍是會檢測到是 webdriver 吧,好比淘寶檢測到是 webdriver 就會禁止登陸了,咱們能夠試試:
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())
運行時候進行一下登陸,而後就會彈出滑塊,本身手動拖動一下,而後就報錯了,界面以下:
爬蟲的時候看到這界面是很讓人崩潰的吧,並且這時候咱們還發現了頁面的 bug,整個瀏覽器窗口比顯示的內容窗口要大,這個是某些頁面會出現的狀況,讓人看起來很不爽。
咱們能夠先解決一下這個顯示的 bug,須要設置下 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())
這樣整個界面就正常了:
OK,那剛纔所說的 webdriver 檢測問題怎樣來解決呢?其實淘寶主要經過 window.navigator.webdriver 來對 webdriver 進行檢測,因此咱們只須要使用 JavaScript 將它設置爲 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())
這裏沒加輸入用戶名密碼的代碼,固然後面能夠自行添加,下面打開以後,咱們點擊輸入用戶名密碼,而後這時候會出現一個滑動條,這裏滑動的話,就能夠經過了,如圖所示:
OK,這樣的話咱們就成功規避了 webdriver 的檢測,使用鼠標拖動模擬就能夠完成淘寶的登陸了。
還有另外一種方法能夠進一步免去淘寶登陸的煩惱,那就是設置用戶目錄。平時咱們已經注意到,當咱們登陸淘寶以後,若是下次再次打開瀏覽器發現仍是登陸的狀態。這是由於淘寶的一些關鍵 Cookies 已經保存到本地了,下次登陸的時候能夠直接讀取並保持登陸狀態。
那麼這些信息保存在哪裏了呢?其實就是保存在用戶目錄下了,裏面不只包含了瀏覽器的基本配置信息,還有一些 Cache、Cookies 等各類信息都在裏面,若是咱們能在瀏覽器啓動的時候讀取這些信息,那麼啓動的時候就能夠恢復一些歷史記錄甚至一些登陸狀態信息了。
這也就解決了一個問題:不少朋友在每次啓動 Selenium 或 Pyppeteer 的時候老是是一個全新的瀏覽器,那就是沒有設置用戶目錄,若是設置了它,每次打開就再也不是一個全新的瀏覽器了,它能夠恢復以前的歷史記錄,也能夠恢復不少網站的登陸信息。
那麼這個怎麼來作呢?很簡單,在啓動的時候設置 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())
好,這裏就是加了一個 userDataDir 的屬性,值爲 userdata,即當前目錄的 userdata 文件夾。咱們能夠首先運行一下,而後登陸一次淘寶,這時候咱們同時能夠觀察到在當前運行目錄下又多了一個 userdata 的文件夾,裏面的結構是這樣子的:
具體的介紹能夠看官方的一些說明,如:https://chromium.googlesource.com/chromium/src/+/master/docs/user_data_dir.md,這裏面介紹了 userdatadir 的相關內容。
再次運行上面的代碼,這時候能夠發現如今就已是登陸狀態了,不須要再次登陸了,這樣就成功跳過了登陸的流程。固然可能時間過久了,Cookies 都過時了,那仍是須要登陸的。
好了,本想把 Pyppeteer 的用法詳細介紹完的,結果只 launch 的方法就介紹這麼多了,後面的內容放到其餘文章來介紹了,其餘的內容後續文章會陸續放出,謝謝。
小彩蛋:以上文章摘自即將完稿的《Python3網絡爬蟲開發實戰(第二版)》,敬請期待,謝謝。