超越Selenium的存在---Pyppeteer

      網友投稿 1131 2025-04-03

      閱讀本文大概需要 10 分鐘。

      如果大家對 Python 爬蟲有所了解的話,想必你應該聽說過 selenium 這個庫,這實際上是一個自動化測試工具,現在已經被廣泛用于網絡爬蟲中來應對 JavaScript 渲染的頁面的抓取。

      selenium 用的時候有個麻煩事,就是環境的相關配置,得安裝好相關瀏覽器,比如 Chrome、Firefox 等等,然后還要到官方網站去下載對應的驅動,最重要的還需要安裝對應的 Python Selenium 庫,確實是不是很方便,另外如果要做大規模部署的話,環境配置的一些問題也是個頭疼的事情。

      那么本節就介紹另一個類似的替代品,叫做 Pyppeteer。注意,是叫做 Pyppeteer,不是 Puppeteer。Puppeteer 是 Google 基于 Node.js 開發的一個工具,有了它我們可以通過 JavaScript 來控制 Chrome 瀏覽器的一些操作,當然也可以用作網絡爬蟲上,其 API 極其完善,功能非常強大。而 Pyppeteer 又是什么呢?它實際上是 Puppeteer 的 Python 版本的實現,但他不是 Google 開發的,是一位來自于日本的工程師依據 Puppeteer 的一些功能開發出來的非官方版本。

      在 Pyppetter 中,實際上它背后也是有一個類似 Chrome 瀏覽器的 Chromium 瀏覽器在執行一些動作進行網頁渲染,首先說下 Chrome 瀏覽器和 Chromium 瀏覽器的淵源。

      Chromium 是谷歌為了研發 Chrome 而啟動的項目,是完全開源的。二者基于相同的源代碼構建,Chrome 所有的新功能都會先在 Chromium 上實現,待驗證穩定后才會移植,因此 Chromium 的版本更新頻率更高,也會包含很多新的功能,但作為一款獨立的瀏覽器,Chromium 的用戶群體要小眾得多。兩款瀏覽器“同根同源”,它們有著同樣的 Logo,但配色不同,Chrome 由藍紅綠黃四種顏色組成,而 Chromium 由不同深度的藍色構成。

      總的來說,兩款瀏覽器的內核是一樣的,實現方式也是一樣的,可以認為是開發版和正式版的區別,功能上基本是沒有太大區別的。

      Pyppeteer 就是依賴于 Chromium 這個瀏覽器來運行的。那么有了 Pyppeteer 之后,我們就可以免去那些繁瑣的環境配置等問題。如果第一次運行的時候,Chromium 瀏覽器沒有安全,那么程序會幫我們自動安裝和配置,就免去了繁瑣的環境配置等工作。另外 Pyppeteer 是基于 Python 的新特性 async 實現的,所以它的一些執行也支持異步操作,效率相對于 Selenium 來說也提高了。

      那么下面就讓我們來一起了解下 Pyppeteer 的相關用法吧。

      安裝

      首先就是安裝問題了,由于 Pyppeteer 采用了 Python 的 async 機制,所以其運行要求的 Python 版本為 3.5 及以上。

      安裝方式非常簡單:

      pip3?install?pyppeteer

      好了,安裝完成之后我們命令行下測試下:

      >>>?import?pyppeteer

      如果沒有報錯,那么就證明安裝成功了。

      快速上手

      接下來我們測試下基本的頁面渲染操作,這里我們選用的網址為:http://quotes.toscrape.com/js/,這個頁面是 JavaScript 渲染而成的,用基本的 requests 庫請求得到的 HTML 結果里面是不包含頁面中所見的條目內容的。

      為了證明 requests 無法完成正常的抓取,我們可以先用如下代碼來測試一下:

      import?requests

      from?pyquery?import?PyQuery?as?pq

      url?=?'http://quotes.toscrape.com/js/'

      response?=?requests.get(url)

      doc?=?pq(response.text)

      print('Quotes:',?doc('.quote').length)

      這里首先使用 requests 來請求網頁內容,然后使用 pyquery 來解析頁面中的每一個條目。觀察源碼之后我們發現每個條目的 class 名為 quote,所以這里選用了 .quote 這個 CSS 選擇器來選擇,最后輸出條目數量。

      運行結果:

      Quotes:?0

      結果是 0,這就證明使用 requests 是無法正常抓取到相關數據的。因為什么?因為這個頁面是 JavaScript 渲染而成的,我們所看到的內容都是網頁加載后又執行了 JavaScript 之后才呈現出來的,因此這些條目數據并不存在于原始 HTML 代碼中,而 requests 僅僅抓取的是原始 HTML 代碼。

      好的,所以遇到這種類型的網站我們應該怎么辦呢?

      其實答案有很多:

      分析網頁源代碼數據,如果數據是隱藏在 HTML 中的其他地方,以 JavaScript 變量的形式存在,直接提取就好了。

      分析 Ajax,很多數據可能是經過 Ajax 請求時候獲取的,所以可以分析其接口。

      模擬 JavaScript 渲染過程,直接抓取渲染后的結果。

      而 Pyppeteer 和 Selenium 就是用的第三種方法,下面我們再用 Pyppeteer 來試試,如果用 Pyppeteer 實現如上頁面的抓取的話,代碼就可以寫為如下形式:

      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())

      運行結果:

      Quotes:?10

      看運行結果,這說明我們就成功匹配出來了 class 為 quote 的條目,總數為 10 條,具體的內容可以進一步使用 pyquery 解析查看。

      那么這里面的過程發生了什么?

      實際上,Pyppeteer 整個流程就完成了瀏覽器的開啟、新建頁面、頁面加載等操作。另外 Pyppeteer 里面進行了異步操作,所以需要配合 async/await 關鍵詞來實現。

      首先, launch 方法會新建一個 Browser 對象,然后賦值給 browser,然后調用 newPage ?方法相當于瀏覽器中新建了一個選項卡,同時新建了一個 Page 對象。然后 Page 對象調用了 goto 方法就相當于在瀏覽器中輸入了這個 URL,瀏覽器跳轉到了對應的頁面進行加載,加載完成之后再調用 content 方法,返回當前瀏覽器頁面的源代碼。然后進一步地,我們用 pyquery 進行同樣地解析,就可以得到 JavaScript 渲染的結果了。

      另外其他的一些方法如調用 asyncio 的 get_event_loop 等方法的相關操作則屬于 Python 異步 async 相關的內容了,大家如果不熟悉可以了解下 Python 的 async/await 的相關知識。

      好,通過上面的代碼,我們就可以完成 JavaScript 渲染頁面的爬取了。

      在這個過程中,我們沒有配置 Chrome 瀏覽器,沒有配置瀏覽器驅動,免去了一些繁瑣的步驟,同樣達到了 Selenium 的效果,還實現了異步抓取,爽歪歪!

      超越Selenium的存在---Pyppeteer

      接下來我們再看看另外一個例子,這個例子可以模擬網頁截圖,保存 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 渲染后的頁面。

      pdf 方法也是類似的,只不過頁面保存格式不一樣,最后得到一個多頁的 pdf 文件,樣例如下:

      可見其內容也是 JavaScript 渲染后的內容,另外這個方法還可以指定放縮大小 scale、頁碼范圍 pageRanges、寬高 width 和 height、方向 landscape 等等參數,導出定制化的 pdf 用這個方法就十分方便。

      最后我們又調用了 evaluate 方法執行了一些 JavaScript,JavaScript 傳入的是一個函數,使用 return 方法返回了網頁的寬高、像素大小比率三個值,最后得到的是一個 JSON 格式的對象,內容如下:

      {'width':?800,?'height':?600,?'deviceScaleFactor':?1}

      OK,實例就先感受到這里,還有太多太多的功能還沒提及。

      總之利用 Pyppeteer 我們可以控制瀏覽器執行幾乎所有動作,想要的操作和功能基本都可以實現,用它來自由地控制爬蟲當然就不在話下了。

      詳細用法

      了解了基本的實例之后,我們再來梳理一下 Pyppeteer 的一些基本和常用操作。Pyppeteer 的幾乎所有功能都能在其官方文檔的 API Reference 里面找到,鏈接為: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網絡爬蟲開發實戰(第二版)》,敬請期待,謝謝。

      本節代碼獲取

      https://github.com/Python3WebSpider/PyppeteerTest

      點擊閱讀原文也可以直達哦!

      “每天學習一點點“

      Python Selenium

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。

      上一篇:快來,這里有23種設計模式的Go語言實現(一)(Go語言設計與實現)
      下一篇:wps表格快速生成文件夾的方法(wps如何生成文件夾)
      相關文章
      在线电影你懂的亚洲| 亚洲精品人成在线观看| 亚洲三级视频在线观看| 久久亚洲日韩精品一区二区三区| 亚洲AV一宅男色影视| 国产成A人亚洲精V品无码性色| 中文字幕不卡亚洲| 亚洲国产综合专区电影在线| 亚洲中文字幕无码一区| 相泽亚洲一区中文字幕| 亚洲欧洲无码AV不卡在线| 亚洲AV无码成人专区| 亚洲AV无码一区二区三区牛牛| 亚洲一区二区三区免费在线观看| 亚洲人成电影在线观看青青| 亚洲成a人片77777群色| 亚洲另类自拍丝袜第1页| 亚洲国产日韩女人aaaaaa毛片在线 | 亚洲成色在线综合网站| 亚洲国产精品乱码一区二区| 精品亚洲一区二区| 亚洲AV日韩AV永久无码下载| 亚洲中文字幕视频国产| 久久夜色精品国产亚洲av| 亚洲色偷偷偷鲁综合| 亚洲成av人影院| 亚洲一区综合在线播放| 亚洲日本在线播放| 亚洲精品第一综合99久久| 亚洲一区二区三区在线观看网站 | 在线亚洲精品福利网址导航| 亚洲乳大丰满中文字幕| 久久亚洲精品无码| 亚洲精品在线电影| 亚洲视频一区调教| 亚洲狠狠狠一区二区三区| 国产成人亚洲合集青青草原精品 | 国产亚洲精品自在久久| 亚洲人成网站影音先锋播放| 亚洲狠狠ady亚洲精品大秀| 亚洲视频无码高清在线|