還有 Selenium 抓不到的內容?

有一些同學在寫爬蟲的時候,過於依賴 Selenium,覺得只要使用模擬瀏覽器,在不被網站屏蔽的情況下,就可以爬到任何內容。

今天我們不討論字體反爬蟲和 CSS 反爬蟲這兩種情況。我們來看一段非常簡單的網頁。這個網頁只有一個 HTML 文件,不加載特殊字體,不加載 CSS 文件。

這個網頁的奇怪之處在哪裏呢?我們試一試使用 XPath Helper 來提取網頁上面的紅色文字,發現 XPath 竟然無法找到這段文字,如下圖所示:

然後我們使用 Selenium 來試一試:

Selenium 果然無法獲取 紅字到內容。我們再打印一下網頁的源代碼:

這一次,Selenium 獲取到的源代碼,竟然跟 Chrome 開發者工具裏面顯示的源代碼不一樣?

這個問題的關鍵,就在開發者工具裏面的這樣一段文字:

因爲這個節點是一個 shadow DOM[1]。shadow DOM 的行爲跟 iframe很像,都是把一段 HTML 信息嵌入到另一個 HTML 中。但不同的是,iframe被嵌入的地址需要額外再搭建一個 HTTP 服務,而 shadow DOM 可以只嵌入一段 HTML 代碼,所以它比 iframe 更節省資源。

在上面的截圖中,通過下面這三行代碼,我們把一個新的<p>標籤嵌入到了原來的 HTML 中:

    var content = document.querySelector('.content');
    var root = content.attachShadow({mode: 'open'});
    root.innerHTML = '<p>你抓不到這段文字的!</p>'

而這個被嵌入的影子標籤,就像 iframe 一樣,是無法直接使用 Selenium 提取的。如果強行提取,那麼,我們需要使用 JavaScript 獲取 shadow DOM,然後再進行提取。我們來看一段可以正常工作的代碼:

shadow = driver.execute_script('return document.querySelector(".content").shadowRoot')
content = shadow.find_element_by_class_name('real_content')
print(content.text)

運行效果如下圖所示:

這段代碼,首先通過 JavaScript 找到shadow-root的父節點元素,然後返回這個元素的.shadowRoot屬性。在 Python 裏面拿到這個屬性以後,使用.find_element_by_class_name()方法獲取裏面的內容。

要特別注意的是,拿到shadow-root節點以後,只能通過 CSS 選擇器進一步篩選裏面的內容,不能用 XPath,否則會導致報錯。

參考資料

[1]

shadow DOM: https://developer.mozilla.org/zh-CN/docs/Web/Web_Components/Using_shadow_DOM

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/Pk9NyE8IIDkgRIkhnN8wLg