Selenium及Headless Chrome抓取動態HTML頁面

来源:https://www.cnblogs.com/linxiyue/archive/2019/01/04/10215912.html
-Advertisement-
Play Games

一般的的靜態HTML頁面可以使用requests等庫直接抓取,但還有一部分比較複雜的動態頁面,這些頁面的DOM是動態生成的,有些還需要用戶與其點擊互動,這些頁面只能使用真實的瀏覽器引擎動態解析,Selenium和Chrome Headless可以很好的達到這種目的。 Headless Chrome ...


一般的的靜態HTML頁面可以使用requests等庫直接抓取,但還有一部分比較複雜的動態頁面,這些頁面的DOM是動態生成的,有些還需要用戶與其點擊互動,這些頁面只能使用真實的瀏覽器引擎動態解析,Selenium和Chrome Headless可以很好的達到這種目的。

Headless Chrome

Headless Chrome 是 Chrome 瀏覽器的無界面形態,可以在不打開瀏覽器的前提下,使用所有Chrome支持的特性,在命令行中運行你的腳本。以前在爬蟲要使用Phantomjs來實現這些功能,但Phantomjs已經暫停開發,現在可以使用Headless Chrome來代替。

使用很簡單,保證chrome命令指向chrome瀏覽器的安裝路徑,ubuntu下為google-chrome。

    輸出html:

google-chrome --headless --dump-dom https://www.cnblogs.com/

   將目標頁面截圖:

google-chrome --headless --disable-gpu --screenshot https://www.cnblogs.com/  
# 規定大小
google-chrome --headless --disable-gpu --screenshot --window-size=640,960 https://www.cnblogs.com/

 保存為pdf:

google-chrome --headless --disable-gpu --print-to-pdf https://www.cnblogs.com/

 以上文件會保存於當前目錄。

   還可以使用--remote-debugging-port參數進行遠程調試:

google-chrome --headless --disable-gpu --no-sandbox --remote-debugging-port=9222 --user-data-dir='/d/cnblogs' http://www.cnblogs.com

 --user-data-dir參數可以設定保存目錄,--user-agent參數可以設定請求agent。上述的命令打開了一個websocket調試介面對當前Tab內頁面的DOM、網路、性能、存儲等等進行調試。

打開http://127.0.0.1:9222/鏈接可以看到可檢查的網頁,可以點擊它們並看到使用了哪種Headless渲染。

還有一系列地址:

   http://127.0.0.1:9222/json 查看已經打開的Tab列表:

[ {
   "description": "",
   "devtoolsFrontendUrl": "/devtools/inspector.html?ws=127.0.0.1:9222/devtools/page/5C7774203404DC082182AF4563CC7256",
   "id": "5C7774203404DC082182AF4563CC7256",
   "title": "博客園 - 代碼改變世界",
   "type": "page",
   "url": "https://www.cnblogs.com/",
   "webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/page/5C7774203404DC082182AF4563CC7256"
} ]

  http://127.0.0.1:9222/json/version : 查看瀏覽器版本信息

{
   "Browser": "HeadlessChrome/71.0.3578.98",
   "Protocol-Version": "1.3",
   "User-Agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/71.0.3578.98 Safari/537.36",
   "V8-Version": "7.1.302.31",
   "WebKit-Version": "537.36 (@15234034d19b85dcd9a03b164ae89d04145d8368)",
   "webSocketDebuggerUrl": "ws://127.0.0.1:9222/devtools/browser/ed156c0d-805c-4849-99d0-02e454260c17"
}

   http://127.0.0.1:9222/json/new?http://www.baidu.com : 新開Tab打開指定地址

   http://127.0.0.1:9222/json/close/8795FFF09B01BD41B1F2931110475A67 : 關閉指定Tab,close後為tab頁面的id

   http://127.0.0.1:9222/json/activate/5C7774203404DC082182AF4563CC7256 : 切換到目標Tab

tab頁面信息中有一個devtoolsFrontendUrl,是開發者工具的前端地址,可以打開:

http://127.0.0.1:9222/devtools/inspector.html?ws=127.0.0.1:9222/devtools/page/CE2E627C634EAAE3CE9193DC374C7B4A

在開發者工具里切換到Performance,勾選Screenshots,點刷新圖標,重新載入完成就可以看到逐幀載入的截圖:

Selenium

 Selenium 是用於測試 Web 應用程式用戶界面的常用框架,它支持各種瀏覽器,包括 Chrome,Safari,Firefox 等,支持多種語言開發,比如 Java,C,Ruby等等,當然也有Python。

pip install selenium

使用時還需要下載瀏覽器驅動,以chromedriver為例,下載地址:

chromedriver

國內鏡像:

鏡像

下載時註意與電腦的chrome版本保持一致,然後將chromedriver置於環境變數之中。

打開一個淘寶商品網頁:

from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://market.m.taobao.com/app/dinamic/h5-tb-detail/index.html?id=568217064643')

瀏覽器會自動打開並訪問網頁。

使用headless模式:

from selenium import webdriver
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
browser = webdriver.Chrome(options=chrome_options)
browser.get('https://market.m.taobao.com/app/dinamic/h5-tb-detail/index.html?id=568217064643')
data = browser.page_source

  page_souce屬性可以獲取html網頁源碼。

可以看到獲取的源碼都是些js與css語句,dom並未生成,需要模擬瀏覽器滾動來生成dom:

for i in range(1, 11):
    browser.execute_script(
        "window.scrollTo(0, document.body.scrollHeight/10*%s);" % i
    )
    time.sleep(0.5)

execute_script方法可以用來執行js腳本。

現在獲取的源碼基本是完整的,還存在一些小問題,比如網頁為了讓img延遲載入,img的地址是放在data-img屬性上的,等到瀏覽器滑動至圖片時才修改src屬性,可以使用pyquery修改:

import time

from selenium import webdriver
from pyquery import PyQuery as pq


base_dir = os.path.dirname(__file__)
chrome_options = webdriver.ChromeOptions()
chrome_options.add_argument('--no-sandbox')
chrome_options.add_argument('--headless')
chrome_options.add_argument('--disable-gpu')
browser = webdriver.Chrome(options=chrome_options)
# browser.implicitly_wait(10)
browser.get('https://market.m.taobao.com/app/dinamic/h5-tb-detail/index.html?id=568217064643')
for i in range(1, 11):
    browser.execute_script(
        "window.scrollTo(0, document.body.scrollHeight/10*%s);" % i
    )
    time.sleep(0.5)
data = browser.page_source.encode('utf-8')
doc = pq(data)
for img in doc('img'):
    img = pq(img)
    if img.attr['data-img']:
        img.attr.src = img.attr['data-img']
data = doc.html(method='html').replace('src="//', 'src="http://')
f = open(os.path.join(base_dir, 'detail.html'), 'w')
f.write(data.encode('utf-8'))
f.close()

 保存為html後打開可以看到網頁爬取成功。

 selenium還提供了很多element提取介面:

提取單個element:

elem = browser.find_element_by_id("description")

提取多個:

elem = browser.find_elements_by_class_name("detail-desc")

批量爬取

可以使用concurrent.futures 線程池進行多線程批量爬取:

# -*- coding: utf-8 -*-
import threading
import time
import os

from concurrent.futures import ThreadPoolExecutor, as_completed
from pyquery import PyQuery as pq


class TaobaoCrawler(object):
    def __init__(self, ids):
        self.ids = ids
        self.browsers = {}
        self.timeout_spus = []
        self.url = 'https://market.m.taobao.com/app/dinamic/h5-tb-detail/index.html?id='

    def _create_new_browser(self):
        from selenium import webdriver
        chrome_options = webdriver.ChromeOptions()
        chrome_options.add_argument('--no-sandbox')
        chrome_options.add_argument('--headless')
        chrome_options.add_argument('--disable-gpu')
        # chrome_options.add_argument('--blink-settings=imagesEnabled=false')
        browser = webdriver.Chrome(options=chrome_options)
        return browser

    def get_browser(self):
        current_thread_id = threading.currentThread().ident
        existed = self.browsers.get(current_thread_id)
        if existed:
            return existed
        new_browser = self._create_new_browser()
        self.browsers[current_thread_id] = new_browser
        return new_browser

    def close_browsers(self):
        for _, browser in self.browsers.iteritems():
            browser.quit()
        self.browsers = {}

    def scroll_browser(self, browser, num):
        '''模擬瀏覽器滾動 保證js全部執行完成'''
        for i in range(1, num+1):
            browser.execute_script(
                "window.scrollTo(0, document.body.scrollHeight/%d*%d);" % (
                    num, i)
            )
            time.sleep(0.5)

    def handle_detail_doc(self, detail):
        doc = pq(detail)
        for img in doc('img'):
            img = pq(img)
            if img.attr['data-img']:
                img.attr.src = img.attr['data-img']
        detail = doc.html(method='html')
        detail = detail.replace('src="//', 'src="http://')
        return detail

    def crawl_taobao_detail(self, taobao_id):
        browser = self.get_browser()
        url = self.url + str(taobao_id)
        browser.execute_script("window.stop();")
        browser.get(url)
        self.scroll_browser(browser, 20)
        data = browser.page_source.encode('utf-8')
        data = self.handle_detail_doc(data)
        return taobao_id, data

    def start_crawl(self):
        if not self.ids:
            return
        with ThreadPoolExecutor(max_workers=4) as executor:
            futures = [executor.submit(self.crawl_taobao_detail, _)
                       for _ in self.ids]
            for task in as_completed(futures):
                if task.done():
                    taobao_id, data = task.result()
                    base_dir = os.path.dirname(__file__)
                    f = open(os.path.join(base_dir, str(taobao_id) + '.html'), 'w')
                    f.write(data.encode('utf-8'))
                    f.close()
        self.close_browsers()


def test_crawl():
    ids = [568217064643, 584126060993, 581555053584, 581002124614]
    c = TaobaoCrawler(ids)
    c.start_crawl()

if __name__ == '__main__':
    test_crawl()

  


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 網路編程協議 1.osi七層模型 應用層 表示層 會話層 傳輸層 網路層 數據鏈路層 物理層 2.套接字 socket 有兩類,一種基於文件類型,一種基於網路類型 3.Tcp和udp協議 Tcp協議:面向連接,數據可靠,傳輸效率低,面向位元組流 建立連接與斷開連接的過程(三次握手,四次揮手) 建立連接 ...
  • 1.學習爬蟲,為什麼必須會正則表達式? 我們爬取一些網頁具體內容時,只需要這個網頁某個標簽的一部分內容就足夠,或者是這個標簽的某個屬性的值時,用普通的 xpath 或者css.selector是不能完成的,此時我們就需用到正則表達式去匹配獲取。2.正則表達式官方簡介? 正則表達式,又稱規則表達式。( ...
  • 前言 函數指針是什麼?如何使用函數指針?函數指針到底有什麼大用?本文將一一介紹。 如何理解函數指針 如果有int *類型變數,它存儲的是int類型變數的地址;那麼對於函數指針來說,它存儲的就是函數的地址。函數也是有地址的,函數實際上由載入記憶體的一些指令組成,而指向函數的指針存儲了函數指令的起始地址。 ...
  • 《從零開始PYTHON3》學習資源包下載 課程連載已經完全結束。 經過整理校對,這裡把在課程中出現過的源碼和練習答案示例源碼全部打包提供下載: 提取碼:f3r6 壓縮包解壓密碼:https://formoon.github.io 資源包中還包含了64位Windows版本的Python3安裝包,是驗證 ...
  • 周末小實踐,vue+樹莓派+一言API 一直有個想法,讓樹莓派做後端,實現一個有趣的網路服務。可是,苦於不會前端,遲遲無法動手。最近由於工作任務需要研究了一下前端。 問過前端大佬們,個個都說你得用vue.js,當前最流行、最熱門的前端框架,就用它!聽我的,沒錯! 在大佬的指導下,花了半天時間領略到了 ...
  • 1.簡介 HBase是一個基於HDFS的、分散式的、面向列的非關係型資料庫。 HBase的特點 1.海量數據存儲,HBase表中的數據能夠容納上百億行*上百萬列。 2.面向列的存儲,數據在表中是按照列進行存儲的,能夠動態的增加列並對列進行各種操作。 3.準實時查詢,HBase在海量的數據量下能夠接近 ...
  • 題意 "題目鏈接" Sol 一道咕咕咕了好長時間的題 題解可以看 "這裡" cpp include define LL long long using namespace std; const int MAXN = 1e7 + 5e6 + 10, mod = 1e9 + 7, mod2 = 1e9 ...
  • 方法引用(Method reference)和invokedynamic指令詳細分析 是jvm指令集裡面最複雜的一條。本文將詳細分析 指令是如何實現方法引用(Method reference)的。 具體言之,有這樣一個方法引用: 使用 查看對應位元組碼: 使用 指令生成encode對象,然後存入 局部 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...