1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        強(qiáng)大易用!新一代爬蟲利器 Playwright 的介紹

        共 19158字,需瀏覽 39分鐘

         ·

        2022-01-23 20:20

        Playwright 是微軟在 2020 年初開源的新一代自動化測試工具,它的功能類似于 Selenium、Pyppeteer 等,都可以驅(qū)動瀏覽器進(jìn)行各種自動化操作。它的功能也非常強(qiáng)大,對市面上的主流瀏覽器都提供了支持,API 功能簡潔又強(qiáng)大。雖然誕生比較晚,但是現(xiàn)在發(fā)展得非常火熱。

        因?yàn)?Playwright 是一個類似 Selenium 一樣可以支持網(wǎng)頁頁面渲染的工具,再加上其強(qiáng)大又簡潔的 API,Playwright 同時也可以作為網(wǎng)絡(luò)爬蟲的一個爬取利器。

        fe085bebd1ef13bdc2b8efb3ac06355d.webp

        1. Playwright 的特點(diǎn)

        • Playwright 支持當(dāng)前所有主流瀏覽器,包括 Chrome 和 Edge(基于 Chromium)、Firefox、Safari(基于 WebKit) ,提供完善的自動化控制的 API。
        • Playwright 支持移動端頁面測試,使用設(shè)備模擬技術(shù)可以使我們在移動 Web 瀏覽器中測試響應(yīng)式 Web 應(yīng)用程序。
        • Playwright 支持所有瀏覽器的 Headless 模式和非 Headless 模式的測試。
        • Playwright 的安裝和配置非常簡單,安裝過程中會自動安裝對應(yīng)的瀏覽器和驅(qū)動,不需要額外配置 WebDriver 等。
        • Playwright 提供了自動等待相關(guān)的 API,當(dāng)頁面加載的時候會自動等待對應(yīng)的節(jié)點(diǎn)加載,大大簡化了 API 編寫復(fù)雜度。

        本節(jié)我們就來了解下 Playwright 的使用方法。

        2. 安裝

        Playwright 目前提供了 Python 和 Node.js 的 API,下面我們針對 Python 版的 Playwright 進(jìn)行介紹。

        要使用 Playwright,需要 Python 3.7 版本及以上,請確保 Python 的版本符合要求。

        要安裝 Playwright,可以直接使用 pip3,命令如下:

        pip3?install?playwright

        安裝完成之后需要進(jìn)行一些初始化操作:

        playwright?install

        這時候 Playwrigth 會安裝 Chromium, Firefox and WebKit 瀏覽器并配置一些驅(qū)動,我們不必關(guān)心中間配置的過程,Playwright 會為我們配置好。

        具體的安裝說明可以參考:https://setup.scrape.center/playwright。

        安裝完成之后,我們便可以使用 Playwright 啟動 Chromium 或 Firefox 或 WebKit 瀏覽器來進(jìn)行自動化操作了。

        3. 基本使用

        Playwright 支持兩種編寫模式,一種是類似 Pyppetter 一樣的異步模式,另一種是像 Selenium 一樣的同步模式,我們可以根據(jù)實(shí)際需要選擇使用不同的模式。

        我們先來看一個基本同步模式的例子:

        from?playwright.sync_api?import?sync_playwright

        with?sync_playwright()?as?p:
        ????for?browser_type?in?[p.chromium,?p.firefox,?p.webkit]:
        ????????browser?=?browser_type.launch(headless=False)
        ????????page?=?browser.new_page()
        ????????page.goto('https://www.baidu.com')
        ????????page.screenshot(path=f'screenshot-{browser_type.name}.png')
        ????????print(page.title())
        ????????browser.close()

        首先我們導(dǎo)入了 sync_playwright 方法,然后直接調(diào)用了這個方法,該方法返回的是一個 PlaywrightContextManager 對象,可以理解是一個瀏覽器上下文管理器,我們將其賦值為變量 p。

        接著我們調(diào)用了 PlaywrightContextManager 對象的 chromium、firefox、webkit 屬性依次創(chuàng)建了一個 Chromium、Firefox 以及 Webkit 瀏覽器實(shí)例,接著用一個 for 循環(huán)依次執(zhí)行了它們的 launch 方法,同時設(shè)置了 headless 參數(shù)為 False。

        注意:如果不設(shè)置為 False,默認(rèn)是無頭模式啟動瀏覽器,我們看不到任何窗口。

        launch 方法返回的是一個 Browser 對象,我們將其賦值為 browser 變量。然后調(diào)用 browser 的 new_page 方法,相當(dāng)于新建了一個選項(xiàng)卡,返回的是一個 Page 對象,將其賦值為 page,這整個過程其實(shí)和 Pyppeteer 非常類似。接著我們就可以調(diào)用 page 的一系列 API 來進(jìn)行各種自動化操作了,比如調(diào)用 goto,就是加載某個頁面,這里我們訪問的是百度的首頁。接著我們調(diào)用了 page 的 screenshot 方法,參數(shù)傳一個文件名稱,這樣截圖就會自動保存為該圖片名稱,這里名稱中我們加入了 browser_type 的 name 屬性,代表瀏覽器的類型,結(jié)果分別就是 chromium, firefox, webkit。另外我們還調(diào)用了 title 方法,該方法會返回頁面的標(biāo)題,即 HTML 中 ?title 節(jié)點(diǎn)中的文字,也就是選項(xiàng)卡上的文字,我們將該結(jié)果打印輸出到控制臺。最后操作完畢,調(diào)用 browser 的 close 方法關(guān)閉整個瀏覽器,運(yùn)行結(jié)束。

        運(yùn)行一下,這時候我們可以看到有三個瀏覽器依次啟動并加載了百度這個頁面,分別是 Chromium、Firefox 和 Webkit 三個瀏覽器,頁面加載完成之后,生成截圖、控制臺打印結(jié)果就退出了。

        這時候當(dāng)前目錄便會生成三個截圖文件,都是百度的首頁,文件名中都帶有了瀏覽器的名稱,如圖所示:

        3d581749e5068b4bd88d6e23c5b26acd.webp

        控制臺運(yùn)行結(jié)果如下:

        百度一下,你就知道
        百度一下,你就知道
        百度一下,你就知道

        通過運(yùn)行結(jié)果我們可以發(fā)現(xiàn),我們非常方便地啟動了三種瀏覽器并完成了自動化操作,并通過幾個 API 就完成了截圖和數(shù)據(jù)的獲取,整個運(yùn)行速度是非常快的,者就是 Playwright 最最基本的用法。

        當(dāng)然除了同步模式,Playwright 還提供異步的 API,如果我們項(xiàng)目里面使用了 asyncio,那就應(yīng)該使用異步模式,寫法如下:

        import?asyncio
        from?playwright.async_api?import?async_playwright

        async?def?main():
        ????async?with?async_playwright()?as?p:
        ????????for?browser_type?in?[p.chromium,?p.firefox,?p.webkit]:
        ????????????browser?=?await?browser_type.launch()
        ????????????page?=?await?browser.new_page()
        ????????????await?page.goto('https://www.baidu.com')
        ????????????await?page.screenshot(path=f'screenshot-{browser_type.name}.png')
        ????????????print(await?page.title())
        ????????????await?browser.close()

        asyncio.run(main())

        可以看到整個寫法和同步模式基本類似,導(dǎo)入的時候使用的是 async_playwright 方法,而不再是 sync_playwright 方法。寫法上添加了 async/await 關(guān)鍵字的使用,最后的運(yùn)行效果是一樣的。

        另外我們注意到,這例子中使用了 with as 語句,with 用于上下文對象的管理,它可以返回一個上下文管理器,也就對應(yīng)一個 PlaywrightContextManager 對象,無論運(yùn)行期間是否拋出異常,它能夠幫助我們自動分配并且釋放 Playwright 的資源。

        4. 代碼生成

        Playwright 還有一個強(qiáng)大的功能,那就是可以錄制我們在瀏覽器中的操作并將代碼自動生成出來,有了這個功能,我們甚至都不用寫任何一行代碼,這個功能可以通過 playwright 命令行調(diào)用 codegen 來實(shí)現(xiàn),我們先來看看 codegen 命令都有什么參數(shù),輸入如下命令:

        playwright?codegen?--help

        結(jié)果類似如下:

        Usage:?npx?playwright?codegen?[options]?[url]

        open?page?and?generate?code?for?user?actions

        Options:
        ??-o,?--output??????saves?the?generated?script?to?a?file
        ??--target???????????language?to?use,?one?of?javascript,?python,?python-async,?csharp?(default:?"python")
        ??-b,?--browser???browser?to?use,?one?of?cr,?chromium,?ff,?firefox,?wk,?webkit?(default:?"chromium")
        ??--channel???????????Chromium?distribution?channel,?"chrome",?"chrome-beta",?"msedge-dev",?etc
        ??--color-scheme???????emulate?preferred?color?scheme,?"light"?or?"dark"
        ??--device?????????emulate?device,?for?example??"iPhone?11"
        ??--geolocation???specify?geolocation?coordinates,?for?example?"37.819722,-122.478611"
        ??--load-storage?????load?context?storage?state?from?the?file,?previously?saved?with?--save-storage
        ??--lang?????????????specify?language?/?locale,?for?example?"en-GB"
        ??--proxy-server????????specify?proxy?server,?for?example?"http://myproxy:3128"?or?"socks5://myproxy:8080"
        ??--save-storage?????save?context?storage?state?at?the?end,?for?later?use?with?--load-storage
        ??--timezone????????time?zone?to?emulate,?for?example?"Europe/Rome"
        ??--timeout???????????timeout?for?Playwright?actions?in?milliseconds?(default:?"10000")
        ??--user-agent??????specify?user?agent?string
        ??--viewport-size????????specify?browser?viewport?size?in?pixels,?for?example?"1280,?720"
        ??-h,?--help???????????????????display?help?for?command

        Examples:

        ??$?codegen
        ??$?codegen?--target=python
        ??$?codegen?-b?webkit?https://example.com

        可以看到這里有幾個選項(xiàng),比如 -o 代表輸出的代碼文件的名稱;--target 代表使用的語言,默認(rèn)是 python,即會生成同步模式的操作代碼,如果傳入 python-async 就會生成異步模式的代碼;-b 代表的是使用的瀏覽器,默認(rèn)是 Chromium,其他還有很多設(shè)置,比如 --device 可以模擬使用手機(jī)瀏覽器,比如 iPhone 11,--lang 代表設(shè)置瀏覽器的語言,--timeout 可以設(shè)置頁面加載超時時間。

        好,了解了這些用法,那我們就來嘗試啟動一個 Firefox 瀏覽器,然后將操作結(jié)果輸出到 script.py 文件,命令如下:

        playwright?codegen?-o?script.py?-b?firefox

        這時候就彈出了一個 Firefox 瀏覽器,同時右側(cè)會輸出一個腳本窗口,實(shí)時顯示當(dāng)前操作對應(yīng)的代碼。

        我們可以在瀏覽器中做任何操作,比如打開百度,然后點(diǎn)擊輸入框并輸入 nba,然后再點(diǎn)擊搜索按鈕,瀏覽器窗口如下:

        e59abcfae5631cef27d722cb4ddf9487.webp

        可以看見瀏覽器中還會高亮顯示我們正在操作的頁面節(jié)點(diǎn),同時還顯示了對應(yīng)的選擇器字符串input[name="wd"],右側(cè)的窗口如圖所示:

        ce80831e185d43b12e629bff53296143.webp

        在操作過程中,該窗口中的代碼就實(shí)時變化,可以看到這里生成了我們一系列操作的對應(yīng)代碼,比如在搜索框中輸入 nba,就對應(yīng)如下代碼:

        page.fill("input[name=\"wd\"]",?"nba")

        操作完畢之后,關(guān)閉瀏覽器,Playwright 會生成一個 script.py 文件,內(nèi)容如下:

        from?playwright.sync_api?import?sync_playwright

        def?run(playwright):
        ????browser?=?playwright.firefox.launch(headless=False)
        ????context?=?browser.new_context()

        ????#?Open?new?page
        ????page?=?context.new_page()

        ????#?Go?to?https://www.baidu.com/
        ????page.goto("https://www.baidu.com/")

        ????#?Click?input[name="wd"]
        ????page.click("input[name=\"wd\"]")

        ????#?Fill?input[name="wd"]
        ????page.fill("input[name=\"wd\"]",?"nba")

        ????#?Click?text=百度一下
        ????with?page.expect_navigation():
        ????????page.click("text=百度一下")

        ????context.close()
        ????browser.close()

        with?sync_playwright()?as?playwright:
        ????run(playwright)

        可以看到這里生成的代碼和我們之前寫的示例代碼幾乎差不多,而且也是完全可以運(yùn)行的,運(yùn)行之后就可以看到它又可以復(fù)現(xiàn)我們剛才所做的操作了。

        所以,有了這個功能,我們甚至都不用編寫任何代碼,只通過簡單的可視化點(diǎn)擊就能把代碼生成出來,可謂是非常方便了!

        另外這里有一個值得注意的點(diǎn),仔細(xì)觀察下生成的代碼,和前面的例子不同的是,這里 new_page 方法并不是直接通過 browser 調(diào)用的,而是通過 context 變量調(diào)用的,這個 context 又是由 browser 通過調(diào)用 new_context 方法生成的。有讀者可能就會問了,這個 context 究竟是做什么的呢?

        其實(shí)這個 context 變量對應(yīng)的是一個 BrowserContext 對象,BrowserContext 是一個類似隱身模式的獨(dú)立上下文環(huán)境,其運(yùn)行資源是單獨(dú)隔離的,在做一些自動化測試過程中,每個測試用例我們都可以單獨(dú)創(chuàng)建一個 BrowserContext 對象,這樣可以保證每個測試用例之間互不干擾,具體的 API 可以參考https://playwright.dev/python/docs/api/class-browsercontext。

        5. 移動端瀏覽器支持

        Playwright 另外一個特色功能就是可以支持移動端瀏覽器的模擬,比如模擬打開 iPhone 12 Pro Max 上的 Safari 瀏覽器,然后手動設(shè)置定位,并打開百度地圖并截圖。首先我們可以選定一個經(jīng)緯度,比如故宮的經(jīng)緯度是 39.913904, 116.39014,我們可以通過 geolocation 參數(shù)傳遞給 Webkit 瀏覽器并初始化。

        示例代碼如下:

        from?playwright.sync_api?import?sync_playwright

        with?sync_playwright()?as?p:
        ????iphone_12_pro_max?=?p.devices['iPhone?12?Pro?Max']
        ????browser?=?p.webkit.launch(headless=False)
        ????context?=?browser.new_context(
        ????????**iphone_12_pro_max,
        ????????locale='zh-CN',
        ????????geolocation={'longitude':?116.39014,?'latitude':?39.913904},
        ????????permissions=['geolocation']
        ????)
        ????page?=?context.new_page()
        ????page.goto('https://amap.com')
        ????page.wait_for_load_state(state='networkidle')
        ????page.screenshot(path='location-iphone.png')
        ????browser.close()

        這里我們先用 PlaywrightContextManager 對象的 devices 屬性指定了一臺移動設(shè)備,這里傳入的是手機(jī)的型號,比如 iPhone 12 Pro Max,當(dāng)然也可以傳其他名稱,比如 iPhone 8,Pixel 2 等。

        前面我們已經(jīng)了解了 BrowserContext 對象,BrowserContext 對象也可以用來模擬移動端瀏覽器,初始化一些移動設(shè)備信息、語言、權(quán)限、位置等信息,這里我們就用它來創(chuàng)建了一個移動端 BrowserContext 對象,通過 geolocation 參數(shù)傳入了經(jīng)緯度信息,通過 permissions 參數(shù)傳入了賦予的權(quán)限信息,最后將得到的 BrowserContext 對象賦值為 context 變量。

        接著我們就可以用 BrowserContext 對象來新建一個頁面,還是調(diào)用 new_page 方法創(chuàng)建一個新的選項(xiàng)卡,然后跳轉(zhuǎn)到高德地圖,并調(diào)用了 wait_for_load_state 方法等待頁面某個狀態(tài)完成,這里我們傳入的 state 是 networkidle,也就是網(wǎng)絡(luò)空閑狀態(tài)。因?yàn)樵陧撁娉跏蓟图虞d過程中,肯定是伴隨有網(wǎng)絡(luò)請求的,所以加載過程中肯定不算 networkidle 狀態(tài),所以這里我們傳入 networkidle 就可以標(biāo)識當(dāng)前頁面和數(shù)據(jù)加載完成的狀態(tài)。加載完成之后,我們再調(diào)用 screenshot 方法獲取當(dāng)前頁面截圖,最后關(guān)閉瀏覽器。

        運(yùn)行下代碼,可以發(fā)現(xiàn)這里就彈出了一個移動版瀏覽器,然后加載了高德地圖,并定位到了故宮的位置,如圖所示:

        e3f76901d5e1f18509b46ce80c6dd5f2.webp

        輸出的截圖也是瀏覽器中顯示的結(jié)果。

        所以這樣我們就成功實(shí)現(xiàn)了移動端瀏覽器的模擬和一些設(shè)置,其操作 API 和 PC 版瀏覽器是完全一樣的。

        6. 選擇器

        前面我們注意到 click 和 fill 等方法都傳入了一個字符串,這些字符串有的符合 CSS 選擇器的語法,有的又是 text= 開頭的,感覺似乎沒太有規(guī)律的樣子,它到底支持怎樣的匹配規(guī)則呢?下面我們來了解下。

        傳入的這個字符串,我們可以稱之為 Element Selector,它不僅僅支持 CSS 選擇器、XPath,Playwright 還擴(kuò)展了一些方便好用的規(guī)則,比如直接根據(jù)文本內(nèi)容篩選,根據(jù)節(jié)點(diǎn)層級結(jié)構(gòu)篩選等等。

        文本選擇

        文本選擇支持直接使用text=這樣的語法進(jìn)行篩選,示例如下:

        page.click("text=Log?in")

        這就代表選擇文本是 Log in 的節(jié)點(diǎn),并點(diǎn)擊。

        CSS 選擇器

        CSS 選擇器之前也介紹過了,比如根據(jù) id 或者 class 篩選:

        page.click("button")
        page.click("#nav-bar?.contact-us-item")

        根據(jù)特定的節(jié)點(diǎn)屬性篩選:

        page.click("[data-test=login-button]")
        page.click("[aria-label='Sign?in']")

        CSS 選擇器 + 文本

        我們還可以使用 CSS 選擇器結(jié)合文本值進(jìn)行海選,比較常用的就是 has-text 和 text,前者代表包含指定的字符串,后者代表字符串完全匹配,示例如下:

        page.click("article:has-text('Playwright')")
        page.click("#nav-bar?:text('Contact?us')")

        第一個就是選擇文本中包含 Playwright 的 article 節(jié)點(diǎn),第二個就是選擇 id 為 nav-bar 節(jié)點(diǎn)中文本值等于 Contact us 的節(jié)點(diǎn)。

        CSS 選擇器 + 節(jié)點(diǎn)關(guān)系

        還可以結(jié)合節(jié)點(diǎn)關(guān)系來篩選節(jié)點(diǎn),比如使用 has 來指定另外一個選擇器,示例如下:

        page.click(".item-description:has(.item-promo-banner)")

        比如這里選擇的就是選擇 class 為 item-description 的節(jié)點(diǎn),且該節(jié)點(diǎn)還要包含 class 為 item-promo-banner 的子節(jié)點(diǎn)。

        另外還有一些相對位置關(guān)系,比如 right-of 可以指定位于某個節(jié)點(diǎn)右側(cè)的節(jié)點(diǎn),示例如下:

        page.click("input:right-of(:text('Username'))")

        這里選擇的就是一個 input 節(jié)點(diǎn),并且該 input 節(jié)點(diǎn)要位于文本值為 Username 的節(jié)點(diǎn)的右側(cè)。

        XPath

        當(dāng)然 XPath 也是支持的,不過 xpath 這個關(guān)鍵字需要我們自行制定,示例如下:

        page.click("xpath=//button")

        這里需要在開頭指定xpath=字符串,代表后面是一個 XPath 表達(dá)式。

        關(guān)于更多選擇器的用法和最佳實(shí)踐,可以參考官方文檔:https://playwright.dev/python/docs/selectors。

        7. 常用操作方法

        上面我們了解了瀏覽器的一些初始化設(shè)置和基本的操作實(shí)例,下面我們再對一些常用的操作 API 進(jìn)行說明。

        常見的一些 API 如點(diǎn)擊 click,輸入 fill 等操作,這些方法都是屬于 Page 對象的,所以所有的方法都從 Page 對象的 API 文檔查找就好了,文檔地址:https://playwright.dev/python/docs/api/class-page

        下面介紹幾個常見的 API 用法。

        事件監(jiān)聽

        Page 對象提供了一個 on 方法,它可以用來監(jiān)聽頁面中發(fā)生的各個事件,比如 close、console、load、request、response 等等。

        比如這里我們可以監(jiān)聽 response 事件,response 事件可以在每次網(wǎng)絡(luò)請求得到響應(yīng)的時候觸發(fā),我們可以設(shè)置對應(yīng)的回調(diào)方法獲取到對應(yīng) Response 的全部信息,示例如下:

        from?playwright.sync_api?import?sync_playwright

        def?on_response(response):
        ????print(f'Statue?{response.status}:?{response.url}')

        with?sync_playwright()?as?p:
        ????browser?=?p.chromium.launch(headless=False)
        ????page?=?browser.new_page()
        ????page.on('response',?on_response)
        ????page.goto('https://spa6.scrape.center/')
        ????page.wait_for_load_state('networkidle')
        ????browser.close()

        這里我們在創(chuàng)建 Page 對象之后,就開始監(jiān)聽 response 事件,同時將回調(diào)方法設(shè)置為 on_response,on_response 對象接收一個參數(shù),然后把 Response 的狀態(tài)碼和鏈接都輸出出來了。

        運(yùn)行之后,可以看到控制臺輸出結(jié)果如下:

        Statue?200:?https://spa6.scrape.center/
        Statue?200:?https://spa6.scrape.center/css/app.ea9d802a.css
        Statue?200:?https://spa6.scrape.center/js/app.5ef0d454.js
        Statue?200:?https://spa6.scrape.center/js/chunk-vendors.77daf991.js
        Statue?200:?https://spa6.scrape.center/css/chunk-19c920f8.2a6496e0.css
        ...
        Statue?200:?https://spa6.scrape.center/css/chunk-19c920f8.2a6496e0.css
        Statue?200:?https://spa6.scrape.center/js/chunk-19c920f8.c3a1129d.js
        Statue?200:?https://spa6.scrape.center/img/logo.a508a8f0.png
        Statue?200:?https://spa6.scrape.center/fonts/element-icons.535877f5.woff
        Statue?301:?https://spa6.scrape.center/api/movie?limit=10&offset=0&token=NGMwMzFhNGEzMTFiMzJkOGE0ZTQ1YjUzMTc2OWNiYTI1Yzk0ZDM3MSwxNjIyOTE4NTE5
        Statue?200:?https://spa6.scrape.center/api/movie/?limit=10&offset=0&token=NGMwMzFhNGEzMTFiMzJkOGE0ZTQ1YjUzMTc2OWNiYTI1Yzk0ZDM3MSwxNjIyOTE4NTE5
        Statue?200:?https://p0.meituan.net/movie/da64660f82b98cdc1b8a3804e69609e041108.jpg@464w_644h_1e_1c
        Statue?200:?https://p0.meituan.net/movie/283292171619cdfd5b240c8fd093f1eb255670.jpg@464w_644h_1e_1c
        ....
        Statue?200:?https://p1.meituan.net/movie/b607fba7513e7f15eab170aac1e1400d878112.jpg@464w_644h_1e_1c

        注意:這里省略了部分重復(fù)的內(nèi)容。

        可以看到,這里的輸出結(jié)果其實(shí)正好對應(yīng)瀏覽器 Network 面板中所有的請求和響應(yīng)內(nèi)容,和下圖是一一對應(yīng)的:

        4baef670cfde3610cd3e2b27e5f4177c.webp

        這個網(wǎng)站我們之前分析過,其真實(shí)的數(shù)據(jù)都是 Ajax 加載的,同時 Ajax 請求中還帶有加密參數(shù),不好輕易獲取。

        但有了這個方法,這里如果我們想要截獲 Ajax 請求,豈不是就非常容易了?

        改寫一下判定條件,輸出對應(yīng)的 JSON 結(jié)果,改寫如下:

        from?playwright.sync_api?import?sync_playwright

        def?on_response(response):
        ????if?'/api/movie/'?in?response.url?and?response.status?==?200:
        ????????print(response.json())

        with?sync_playwright()?as?p:
        ????browser?=?p.chromium.launch(headless=False)
        ????page?=?browser.new_page()
        ????page.on('response',?on_response)
        ????page.goto('https://spa6.scrape.center/')
        ????page.wait_for_load_state('networkidle')
        ????browser.close()

        控制臺輸入如下:

        {'count':?100,?'results':?[{'id':?1,?'name':?'霸王別姬',?'alias':?'Farewell?My?Concubine',?'cover':?'https://p0.meituan.net/movie/ce4da3e03e655b5b88ed31b5cd7896cf62472.jpg@464w_644h_1e_1c',?'categories':?['劇情',?'愛情'],?'published_at':?'1993-07-26',?'minute':?171,?'score':?9.5,?'regions':?['中國大陸',?'中國香港']},?
        ...
        'published_at':?None,?'minute':?103,?'score':?9.0,?'regions':?['美國']},?{'id':?10,?'name':?'獅子王',?'alias':?'The?Lion?King',?'cover':?'https://p0.meituan.net/movie/27b76fe6cf3903f3d74963f70786001e1438406.jpg@464w_644h_1e_1c',?'categories':?['動畫',?'歌舞',?'冒險(xiǎn)'],?'published_at':?'1995-07-15',?'minute':?89,?'score':?9.0,?'regions':?['美國']}]}

        簡直是得來全不費(fèi)工夫,我們直接通過這個方法攔截了 Ajax 請求,直接把響應(yīng)結(jié)果拿到了,即使這個 Ajax 請求有加密參數(shù),我們也不用關(guān)心,因?yàn)槲覀冎苯咏孬@了 Ajax 最后響應(yīng)的結(jié)果,這對數(shù)據(jù)爬取來說實(shí)在是太方便了。

        另外還有很多其他的事件監(jiān)聽,這里不再一一介紹了,可以查閱官方文檔,參考類似的寫法實(shí)現(xiàn)。

        獲取頁面源碼

        要獲取頁面的 HTML 代碼其實(shí)很簡單,我們直接通過 content 方法獲取即可,用法如下:

        from?playwright.sync_api?import?sync_playwright

        with?sync_playwright()?as?p:
        ????browser?=?p.chromium.launch(headless=False)
        ????page?=?browser.new_page()
        ????page.goto('https://spa6.scrape.center/')
        ????page.wait_for_load_state('networkidle')
        ????html?=?page.content()
        ????print(html)
        ????browser.close()

        運(yùn)行結(jié)果就是頁面的 HTML 代碼。獲取了 HTML 代碼之后,我們通過一些解析工具就可以提取想要的信息了。

        頁面點(diǎn)擊

        剛才我們通過示例也了解了頁面點(diǎn)擊的方法,那就是 click,這里詳細(xì)說一下其使用方法。

        頁面點(diǎn)擊的 API 定義如下:

        page.click(selector,?**kwargs)

        這里可以看到必傳的參數(shù)是 selector,其他的參數(shù)都是可選的。第一個 selector 就代表選擇器,可以用來匹配想要點(diǎn)擊的節(jié)點(diǎn),如果傳入的選擇器匹配了多個節(jié)點(diǎn),那么只會用第一個節(jié)點(diǎn)。

        這個方法的內(nèi)部執(zhí)行邏輯如下:

        • 根據(jù) selector 找到匹配的節(jié)點(diǎn),如果沒有找到,那就一直等待直到超時,超時時間可以由額外的 timeout 參數(shù)設(shè)置,默認(rèn)是 30 秒。
        • 等待對該節(jié)點(diǎn)的可操作性檢查的結(jié)果,比如說如果某個按鈕設(shè)置了不可點(diǎn)擊,那它會等待該按鈕變成了可點(diǎn)擊的時候才去點(diǎn)擊,除非通過 force 參數(shù)設(shè)置跳過可操作性檢查步驟強(qiáng)制點(diǎn)擊。
        • 如果需要的話,就滾動下頁面,將需要被點(diǎn)擊的節(jié)點(diǎn)呈現(xiàn)出來。
        • 調(diào)用 page 對象的 mouse 方法,點(diǎn)擊節(jié)點(diǎn)中心的位置,如果指定了 position 參數(shù),那就點(diǎn)擊指定的位置。

        click 方法的一些比較重要的參數(shù)如下:

        • click_count:點(diǎn)擊次數(shù),默認(rèn)為 1。
        • timeout:等待要點(diǎn)擊的節(jié)點(diǎn)的超時時間,默認(rèn)是 30 秒。
        • position:需要傳入一個字典,帶有 x 和 y 屬性,代表點(diǎn)擊位置相對節(jié)點(diǎn)左上角的偏移位置。
        • force:即使不可點(diǎn)擊,那也強(qiáng)制點(diǎn)擊。默認(rèn)是 False。

        具體的 API 設(shè)置參數(shù)可以參考官方文檔:https://playwright.dev/python/docs/api/class-page/#pageclickselector-kwargs。

        文本輸入

        文本輸入對應(yīng)的方法是 fill,API 定義如下:

        page.fill(selector,?value,?**kwargs)

        這個方法有兩個必傳參數(shù),第一個參數(shù)也是 selector,第二個參數(shù)是 value,代表輸入的內(nèi)容,另外還可以通過 timeout 參數(shù)指定對應(yīng)節(jié)點(diǎn)的最長等待時間。

        獲取節(jié)點(diǎn)屬性

        除了對節(jié)點(diǎn)進(jìn)行操作,我們還可以獲取節(jié)點(diǎn)的屬性,方法就是 get_attribute,API 定義如下:

        page.get_attribute(selector,?name,?**kwargs)

        這個方法有兩個必傳參數(shù),第一個參數(shù)也是 selector,第二個參數(shù)是 name,代表要獲取的屬性名稱,另外還可以通過 timeout 參數(shù)指定對應(yīng)節(jié)點(diǎn)的最長等待時間。

        示例如下:

        from?playwright.sync_api?import?sync_playwright

        with?sync_playwright()?as?p:
        ????browser?=?p.chromium.launch(headless=False)
        ????page?=?browser.new_page()
        ????page.goto('https://spa6.scrape.center/')
        ????page.wait_for_load_state('networkidle')
        ????href?=?page.get_attribute('a.name',?'href')
        ????print(href)
        ????browser.close()

        這里我們調(diào)用了 get_attribute 方法,傳入的 selector 是a.name,選定了 class 為 name 的 a 節(jié)點(diǎn),然后第二個參數(shù)傳入了 href,獲取超鏈接的內(nèi)容,輸出結(jié)果如下:

        /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx

        可以看到對應(yīng) href 屬性就獲取出來了,但這里只有一條結(jié)果,因?yàn)檫@里有個條件,那就是如果傳入的選擇器匹配了多個節(jié)點(diǎn),那么只會用第一個節(jié)點(diǎn)。

        那怎么獲取所有的節(jié)點(diǎn)呢?

        獲取多個節(jié)點(diǎn)

        獲取所有節(jié)點(diǎn)可以使用 query_selector_all 方法,它可以返回節(jié)點(diǎn)列表,通過遍歷獲取到單個節(jié)點(diǎn)之后,我們可以接著調(diào)用單個節(jié)點(diǎn)的方法來進(jìn)行一些操作和屬性獲取,示例如下:

        from?playwright.sync_api?import?sync_playwright

        with?sync_playwright()?as?p:
        ????browser?=?p.chromium.launch(headless=False)
        ????page?=?browser.new_page()
        ????page.goto('https://spa6.scrape.center/')
        ????page.wait_for_load_state('networkidle')
        ????elements?=?page.query_selector_all('a.name')
        ????for?element?in?elements:
        ????????print(element.get_attribute('href'))
        ????????print(element.text_content())
        ????browser.close()

        這里我們通過 query_selector_all 方法獲取了所有匹配到的節(jié)點(diǎn),每個節(jié)點(diǎn)對應(yīng)的是一個 ElementHandle 對象,然后 ElementHandle 對象也有 get_attribute 方法來獲取節(jié)點(diǎn)屬性,另外還可以通過 text_content 方法獲取節(jié)點(diǎn)文本。

        運(yùn)行結(jié)果如下:

        /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx
        霸王別姬?-?Farewell?My?Concubine
        /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIy
        這個殺手不太冷?-?Léon
        /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIz
        肖申克的救贖?-?The?Shawshank?Redemption
        /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWI0
        泰坦尼克號?-?Titanic
        /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWI1
        羅馬假日?-?Roman?Holiday
        /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWI2
        唐伯虎點(diǎn)秋香?-?Flirting?Scholar
        /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWI3
        亂世佳人?-?Gone?with?the?Wind
        /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWI4
        喜劇之王?-?The?King?of?Comedy
        /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWI5
        楚門的世界?-?The?Truman?Show
        /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIxMA==
        獅子王?-?The?Lion?King

        獲取單個節(jié)點(diǎn)

        獲取單個節(jié)點(diǎn)也有特定的方法,就是 query_selector,如果傳入的選擇器匹配到多個節(jié)點(diǎn),那它只會返回第一個節(jié)點(diǎn),示例如下:

        from?playwright.sync_api?import?sync_playwright

        with?sync_playwright()?as?p:
        ????browser?=?p.chromium.launch(headless=False)
        ????page?=?browser.new_page()
        ????page.goto('https://spa6.scrape.center/')
        ????page.wait_for_load_state('networkidle')
        ????element?=?page.query_selector('a.name')
        ????print(element.get_attribute('href'))
        ????print(element.text_content())
        ????browser.close()

        運(yùn)行結(jié)果如下:

        /detail/ZWYzNCN0ZXVxMGJ0dWEjKC01N3cxcTVvNS0takA5OHh5Z2ltbHlmeHMqLSFpLTAtbWIx
        霸王別姬?-?Farewell?My?Concubine

        可以看到這里只輸出了第一個匹配節(jié)點(diǎn)的信息。

        網(wǎng)絡(luò)劫持

        最后再介紹一個實(shí)用的方法 route,利用 route 方法,我們可以實(shí)現(xiàn)一些網(wǎng)絡(luò)劫持和修改操作,比如修改 request 的屬性,修改 response 響應(yīng)結(jié)果等。

        看一個實(shí)例:

        from?playwright.sync_api?import?sync_playwright
        import?re

        with?sync_playwright()?as?p:
        ????browser?=?p.chromium.launch(headless=False)
        ????page?=?browser.new_page()

        ????def?cancel_request(route,?request):
        ????????route.abort()

        ????page.route(re.compile(r"(\.png)|(\.jpg)"),?cancel_request)
        ????page.goto("https://spa6.scrape.center/")
        ????page.wait_for_load_state('networkidle')
        ????page.screenshot(path='no_picture.png')
        ????browser.close()

        這里我們調(diào)用了 route 方法,第一個參數(shù)通過正則表達(dá)式傳入了匹配的 URL 路徑,這里代表的是任何包含.png.jpg?的鏈接,遇到這樣的請求,會回調(diào) cancel_request 方法處理,cancel_request 方法可以接收兩個參數(shù),一個是 route,代表一個 CallableRoute 對象,另外一個是 request,代表 Request 對象。這里我們直接調(diào)用了 route 的 abort 方法,取消了這次請求,所以最終導(dǎo)致的結(jié)果就是圖片的加載全部取消了。

        觀察下運(yùn)行結(jié)果,如圖所示:

        b167a63fea526defed49487739c3dcaa.webp

        可以看到圖片全都加載失敗了。

        這個設(shè)置有什么用呢?其實(shí)是有用的,因?yàn)閳D片資源都是二進(jìn)制文件,而我們在做爬取過程中可能并不想關(guān)心其具體的二進(jìn)制文件的內(nèi)容,可能只關(guān)心圖片的 URL 是什么,所以在瀏覽器中是否把圖片加載出來就不重要了。所以如此設(shè)置之后,我們可以提高整個頁面的加載速度,提高爬取效率。

        另外,利用這個功能,我們還可以將一些響應(yīng)內(nèi)容進(jìn)行修改,比如直接修改 Response 的結(jié)果為自定義的文本文件內(nèi)容。

        首先這里定義一個 HTML 文本文件,命名為 custom_response.html,內(nèi)容如下:

        html>
        <html>
        ??<head>
        ????<title>Hack?Responsetitle>
        ??head>
        ??<body>
        ????<h1>Hack?Responseh1>
        ??body>
        html>

        代碼編寫如下:

        from?playwright.sync_api?import?sync_playwright

        with?sync_playwright()?as?p:
        ????browser?=?p.chromium.launch(headless=False)
        ????page?=?browser.new_page()

        ????def?modify_response(route,?request):
        ????????route.fulfill(path="./custom_response.html")

        ????page.route('/',?modify_response)
        ????page.goto("https://spa6.scrape.center/")
        ????browser.close()

        這里我們使用 route 的 fulfill 方法指定了一個本地文件,就是剛才我們定義的 HTML 文件,運(yùn)行結(jié)果如下:

        121b048b574fd42ef68cd99bf0cd0466.webp

        可以看到,Response 的運(yùn)行結(jié)果就被我們修改了,URL 還是不變的,但是結(jié)果已經(jīng)成了我們修改的 HTML 代碼。

        所以通過 route 方法,我們可以靈活地控制請求和響應(yīng)的內(nèi)容,從而在某些場景下達(dá)成某些目的。

        8. 總結(jié)

        本節(jié)介紹了 Playwright 的基本用法,其 API 強(qiáng)大又易于使用,同時具備很多 Selenium、Pyppeteer 不具備的更好用的 API,是新一代 JavaScript 渲染頁面的爬取利器。

        本節(jié)代碼:https://github.com/Python3WebSpider/PlaywrightTest。

        我們的文章到此就結(jié)束啦,如果你喜歡今天的Python 實(shí)戰(zhàn)教程,請持續(xù)關(guān)注Python實(shí)用寶典。

        有任何問題,可以在公眾號后臺回復(fù):加群,回答相應(yīng)紅字驗(yàn)證信息,進(jìn)入互助群詢問。

        原創(chuàng)不易,希望你能在下面點(diǎn)個贊和在看支持我繼續(xù)創(chuàng)作,謝謝!

        點(diǎn)擊下方閱讀原文可獲得更好的閱讀體驗(yàn)

        Python實(shí)用寶典?(pythondict.com)
        不只是一個寶典
        歡迎關(guān)注公眾號:Python實(shí)用寶典

        瀏覽 37
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評論
        圖片
        表情
        推薦
        點(diǎn)贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            一道本激情视频| 北条麻妃无码在线| 四虎午夜福利| 亚洲成人色色| 国产高清无码在线观看| 欧美v在线观看| 日韩AV免费电影| 欧美激情网站| 一级黄色免费视频| 一本一道无码| 人妻无码精品久久人妻成人| 国产精品欧美一区二区| 嫩草在线精品| 国产一区二区波多野结衣| 一级一级一级做a免费一级做a| 国产视频久久| 成人黄网站免费视频| 怡红院男人天堂| 不卡精品| 北条麻妃被躁57分钟视频在线| 超碰人人在线| 国产欧美日韩在线播放| h片在线观看免费| 欧美黄色操逼| 自拍偷拍精品| 高清无码学生妹| 呦小性Free小U女HD| 日本中文字幕无码| 欧美午夜电影| 欧美大屌视频| 天堂综合网久久| 国产三级在线观看视频| 99cao| 少妇厨房愉情理伦BD在线观看| 美日韩毛片| 91亚洲国产成人精品一区二区三 | 无码福利视频| 99热这里都是精品| 成人黄网站免费视频| 亚洲AV成人无码精在线| 激情视频小说| 五月天最新网址| 曰韩一级片| 抽插视频免费| 午夜视频无码| 人人操碰| 亚洲成人精品一区| 一二三四在线视频| 动漫av网站| 亚洲天堂视频在线| 91ThePorn国产| 日韩无码一区二区三区四区| 激情开心站| 91九色视频| 亚洲二区无码| 黄片免费视频在线观看| 熟女影音先锋| 亚洲视频播放| 亚洲真人无码| 国产精品视频免费| 五十路老国产| av在线资源| 久久这里只有精品9| 中文字幕成人| 免费视频久久| 中文字幕在线观看一区二区三区| 久久久久无码精品国产91福利| 亚洲色视频在线观看| 欧美一级性爱视频| 欧美久色| 精品人妻一区二区三区-国产精品| 91精品国产综合久久久久久久| 国内自拍第一页| 欧美一级a视频免费放| 成人无码中文字幕| www俺来也com| 欧美五月在线网址| 欧美少妇视频| 久操电影网| A片视频在线观看| 91久久精品日日躁夜夜躁欧美| 亚洲av影院| 超碰在线精品| 污污污污污www网站免费民国| 亚洲三级电影| 国产精品人人人人| 特一级黄色片| 中文字幕在线电影| 日韩视频免费看| 亚洲有码在线| 九色91PORNY国产| 欧美色色网| 天天爽天天搞| 日韩AV手机在线观看| 色五月av| 91亚洲精品国产成人| 欧美老妇性猛交| 五月丁香婷婷色色| 欧美一级片免费看| 一区二区三区四区久久| 草逼视频免费看| 加勒比日韩在线| 色欲一区| 另类老妇性BBwBBw图片| 婷婷综合缴情亚洲另类在线| 日本A片免费看| 亚洲AV无码乱码国产精品| 一级黄片免费看| 日韩AAA在线| 亚洲最新无码| 先锋AV资源| 色九| 91九色口爆吞精| 先锋影音AV在线| 白浆av| 日本A片在线观看| 毛片网站在线观看| 亚洲日本黄色网址| 精品一区二区三区蜜桃臀www| 免费一级婬片AA片观看| 国产,亚洲91| 91精品国产综合久久久蜜臀粉嫩| 国产亚洲精品久久久波多野结衣 | 久久午夜夜伦鲁鲁一区二区| 日韩天堂| 亚洲第一中文字幕网| 久久久无码精品亚洲日韩男男| 亚洲午夜AV| 日韩干| 天天舔天天操| 亚洲中文字幕视频在线| 丰满熟妇人妻中文字幕| 69国产| 天天日天天插| 久久三级| 免费看AV大片| 日皮视频在线观看免费| 日韩中字幕无码| 一级黄色性爱视频| 亚洲天堂网在线视频| 国产女人18毛片水18精| 日韩五月天| 久久精品6| 手机不卡黄色视频在线| 免费看特别黄色视频| 亚洲日本中文字幕在线观看| 色噜噜人妻av中文字幕| 欧美精品在线观看视频| 国产一级A片久久久免费看快餐| 亚洲人妻少妇| caobi视频| 东京热久久综合色五月老师| 日韩高清精品在线| 日本亚洲精品秘入口A片| 亚洲精品秘一区二区三区影| 精品人妻一区二区三区在线视频不卡 | 国产免看一级a一片成人aⅴ| 久久秘成人久久无码| 少妇精品久久久久久久久久 | 中文原创麻豆传媒md0052| 一级a一级a爰片免费免免中国A片| 蜜臀久久99精品久久一区二区| 先锋资源男人站| 色播欧美| 中文字幕一区二区三区日本在线| 亚洲无码成人在线| 水蜜桃视频网| 日韩美女毛片| 91AV在线观看视频| 亚洲色鬼| 欧美性夜黄A片爽爽免费视频| 全国最大成人网| www.四虎成人网站| 就去色色五月天| 老熟女搡BBBB搡BBBB视频| 亚洲精品国产成人AV在线| 亚洲日韩欧美一区二区| 重庆美女揉BBBB搡BBBB| 中文字幕免费| 欧美爱爱免费看| 特一级A片| 日韩偷拍网| 一级日逼| 黄色日逼视频| 水蜜桃成人在线| 亚洲国产另类无码| 久久久久亚洲AV无码麻豆| 青青操B| 一区二区三区四区在线| 蜜桃无码视频小说网站| 天天干天天日| 福利视频二区| 黄色视频网站免费观看| 成年人性生活免费视频| 蜜芽成人精品久久久视频| 波多野结衣亚洲视频| 波多野结衣视频在线| 国产xxxx| 国产伦精品一区二区三区色大师| 色哟哟在线观看| 伊人成人小说| 久草在线| 国产美女激情视频| 五月丁香视频在线观看| 无码一道本一区二区无码| 亚洲高清无码视频在线观看| 免费的一级A片| 午夜视频网站| 嫩BBB揍BBB揍BBB| 亚洲WWW| 欧美久久一区二区三区四区视频| 操小嫩逼视频| 丁香五月天啪啪| 美女被操免费网站| 精品人妻系列| 草逼视频网站| 国内老熟妇对白HDXXXX| 99性爱视频| 精品多人P群无码视频| AV色图| 加勒比无码| 亚洲九九| 免费国产精品视频| 韩国中文无码| 俺来也俺也啪www色| 蜜桃成人AV| 成人无码网站在线观看| 天天操夜操| 国产福利AV| 能看的操逼网站| 自拍视频国产| www.超碰在线| 亚洲高清无码免费在线观看| 国产夫妻AV| 亚洲精品国产av| 国产视频一区二区三区四区| 激情视频在线播放| 肏屄视频免费| 涩婷婷| 天天操大香蕉| 潮喷在线观看| 一级片日韩| 777偷窥盗摄00000| 国产日韩一区二区三区| 久久久久网站| jizz免费视频| 美日韩一区二区三区| 免费看黃色AAAAAA片| 国产精品久久久久久久久久二区三区| 麻豆一二三区| 欧美视频中文字幕| 亚洲乱码国产乱码精品天美传媒 | 国产成人无码精品一区秘二区| 豆花视频logo| 炮友露脸青楼传媒刘颖儿| 91人人妻人人澡人人爽人人精品| 亚洲中文婷婷| 成人国产精品秘在线看| 成人一级黄色片| av一区二区三区四区| 天天干天天干| 色综合色综合色综合| 欧美成人免费网站| 国产中文字幕片| 天天插天天拍| 国产欧美一区二区三区四区| 日本黄色视频官网| 中文字幕人妻在线中文乱码怎么解决| 国产在线不卡| 能看的黄色视频| 欧美射精视频| 欧美精品99久久久| 国产精品国产三级国产专区52| 香蕉国产2023| 爱搞视频在线观看| 国产欧美日韩一区| 99久在线精品99re8热| 亚洲精品中文字幕在线| 天天爆操| 日韩免费一区| 91精品国产乱码| 江苏妇搡BBB搡BBBB| 日本精品人妻无码77777| 波多野结衣无码网站| 91麻豆精品国产| 屌国产精品| 日韩欧美一区二区在线观看| 人妻体内射精一区二区| 精品九九九九九九| 91精品在线免费观看| 影音先锋成人电影| 久久午夜无码人妻精品蜜桃冫| 26∪u∪成人网站| 婷婷五月电影| 四虎AV在线| 色婷婷亚洲| 俄罗斯老熟妇与子伦| 夜夜操天天操| 天天爽天天爽夜夜爽| 人妻精品久久久久中文字幕69| 操东北老女人| 亚洲香蕉影院| 黄色av免费在线观看| 免费AV资源在线观看| 三级片中文| 91大铭哥| 日韩情色| 神马午夜福利视频| 国产中文字幕在线免费观看| AV资源在线播放| 性爱无码AV| 欧美亚洲动漫| 日韩三级AV在线观看| 国产精品秘ThePorn| 91香蕉视频在线播放| 狠狠操天天操| 天天操人妻| 欧美黄色免费| 3344在线观看免费下载视频| 色色色无码| 成人午夜福利高清视频| eeuss| 狠狠躁日日躁夜夜躁A片男男视频 精品无码一区二区三区蜜桃李宗瑞 | 999一区二区三区| 精品中文在线视频| 欧美后门菊门交3p| 一区二区三区免费| 国产黄色小视频在线观看| 色噜噜噜| 大香蕉久久伊人| 免费无码高清视频| 国产精品午夜在线| 天天爽| 激情五月天亚洲| 成人做爰黄AAA片免费直播岛国| 国产91小视频| 精品人妻| 亚洲色婷| 无码AV一区| 在线观看黄片网站| 欧美一区二区三区精品| 亚洲日韩国产AV| 欧美日韩国产中文字幕| 色综合五月| 亚州天堂| 亚洲精品一区中文字幕乱码| 国产嫩草精品A88AV| 91国产视频在线播放| 九九热av| 特级毛片AAAAAA蜜桃| 亚洲美女免费视频| 蜜桃视频91| 91九色在线| 韩无码| 激情黄色视频| 99精品一区二区三区| 99精品久久久久久无码| 思思精品在线| 亚洲国产精品久久| 中文字幕在线播放第一页| 久久久福利视频| 亚洲人人18XXX—20HD| 欧美另类激情| av手机在线| 国产在线激情视频| 天天躁夜夜躁av| www.四虎成人网站| 大香蕉综合视频| 免费看欧美日黄片| 狼人香蕉网| 日日夜夜天天操| 成人做爰A片免费看网站| 日逼导航| 久久久无码精品亚洲| jzzijzzij亚洲成熟少妇在线观看| 日韩欧美中文在线| 99热这里是精品| 久久精品一区二区三区四区| 国产女人高潮毛片| 亚洲日韩在线播放| 蜜桃av秘无码一区三| 久久久XXX| 欧美丰满人妻| 天天日天天干天天爽| 日日夜夜天天| 亚州视频在线观看| 刘玥91精品一区二区三区| 日韩免费a| 德美日三级片在线观看| 哪里能看毛片| 亚洲天堂在线观看视频网站| 亚洲成人日韩| 中字AV| 91午夜福利| 先锋影音在线| 亚洲无吗在线视频| 欧美成人三级| 中文资源在线a| 国产又黄又爽| 熟女人妻ThePorn| 北条麻妃无码一区三区| 在线视频免费观看| 黄色小说在线播放| 欧美精品久久久久久| 五月丁香花视频| 欧美国产在线观看综合| 成人亚洲A片V一区二区三区蜜月| 丰满人妻一区二区三区精品高清| 国产一区二区AV| 超碰极品| 视色视频在线观看| www.777av| 国产欧美另类| 九九热九九| 熟女综合网| 欧美性爱无码在线| 一级女婬片A片AAAA片| 久久综合在线| 欲撸视频| 亚洲成人网在线观看| 黄色视频免费观看国产| 国产成人精品AV| 九九久久99| 日韩精品成人在线视频| 欧洲一区二区| 大香蕉这里只有精品| 日韩无码视频二区| 97精品综合久久| 日韩四区| 国产三级性爱| 成人视频无码| 超碰一区二区三区| 亚洲精品天堂无码AV片| 欧美午夜三级| 九九精品免费视频| 亚洲欧美日韩黑料吃瓜在线观看 | 婷婷色色婷婷| 国产系列第一页| 狠狠的操| 97人妻人人揉人人躁人人| 欧美黄色一级网站| 亚洲黄色av| 日韩黄色视频网站| 一级片网址| 国产又粗又大又爽91嫩草| 亚洲小说欧美激情另类A片小说 | 丁香五月在线| 中文字幕无码视频在线观看| 狠狠狠狠狠狠狠狠狠狠| 无码高清视频| 欧美精品成人免码在线| 亚洲.www| 午夜精品久久久久久不卡8050| AV资源在线播放| 天天爆操| 牛牛影视一区二区| 日韩欧美色图| 二区三区免费视频| 国产又粗又猛又黄又爽无遮挡 | 91在线无码精品秘入口男同| 熟妇操逼视频| 久久精品一区二区三区四区五区| 午夜天堂网| 国产女人与禽zOz0性| 国产高清在线| 91香蕉麻豆| 久色天堂| 亚洲精品天堂无码| 午夜熟睡乱子伦视频| www.俺去| 国产无码午夜| 西西掰穴| 操操日| 天天干人人干| 久久国产偷拍| 成人自拍偷拍视频| 久久AV秘一区二区三区水生| 五月丁香六月色| www.五月天婷婷| 久久无码一区| 翔田千里50岁无码| 大地资源第5页在线| 熟女探花精选| 97超碰人人操| 欧美肥臀| 亚洲天堂一| a色视频| 亚洲午夜视频在线观看| AV在线一区二区三区| 国产主播一区二区| 国产欧美一区二区三区视频| 蜜臀99久久精品久久久久久软件| 日韩视频久久| 成人一区二区电影| 日韩一级无码| AV资源在线播放| 黄色电影天堂网| 成人综合大香蕉| 在线免费观看黄色视频| 国产肏逼视频| 丁香婷婷激情五月| 乱伦播放五月天| 一道本一区二区三区免费视频| 精品人妻一区二区免费蜜桃视频 | 亚洲视频免费看| 国产经典午夜福利视频合集| 无码AV电影在线观看| 永久免费看A人片无码精| 97久久精品国产熟妇高清网 | 中国老熟女2老女人| 久久无码电影| www.超碰在线| 国产精品揄拍500视频| 日韩超清无码| 操日视频| 久久福利导航| 黄色一级片免费看| 久久精品苍井空免费一区二| 亚洲黄色视频在线观看网站| 免费看三级网站| 亚洲四区| 刘玥91精一区二区三区| 国产TS在线| 天天爽夜夜操| 亚洲一级av无码毛片精品| 中文字幕视频2023| 国产精品免费看| 免费观看黄色视频网站| 韩国gogogo高清在线完整版| 午夜精品成人| 无码操逼| 欧美一级婬片免费视频黄| 嫩BBB槡BBBB槡BBBB视频-百度 | 欧美日韩三区| 欧美久久视频| 日中国老太太B| 美女十八禁| 成人免费区一区二区三区| 狠狠操av| 香蕉三级片| 福利逼站| 日爽夜爽| 亚洲网站在线免费观看| 国产嫩草久久久一二三久久免费观看 | 火淫玖玖免费精品| 操操操操操| 91成人小电影| 欧美日韩v| 中文字幕AV一区| 国产白嫩精品久久久久久| 蜜桃成人AV| 久热大香蕉| 黄色成人片| 久久久人妻| 亚洲黄色电影网| 伊人色五月天| 亚洲AV资源在线| 色色免费黄色视频| 狠狠肏| 国产成人精品一区二三区熟女在线 | 麻豆传媒av| 大鸡巴午夜爽视频电影| 人人干人人爽| 人妻大屁股-91Porn| 国产V视频| 东方av在线免费观看| 国产一级婬乱A片| 老太色HD色老太HD.| 91牛视频| 黄色激情网站| 亚洲AV成人无码精在线| 久久艹视频| 国产精品a久久久久| 2026国产精品视频| 国产中文字幕av| 国内自拍欧美| 成人久久大香蕉| 亚洲国产女人| 国产高清秘成人久久| 3D动漫精品啪啪一区二区免费| 无码免费视频在线观看| 亚洲欧洲免费| 91狠狠色丁香婷婷综合久久精品| 91亚洲免费视频| 无码熟妇人妻无码AV在线天堂| 精品多人P群无码视频| 国产人成视频免费观看| 色欲国产精品欧美在线密| 欧美三级片视频| 亚洲精品国产AV| 久久久偷拍| 国产在线拍偷自揄拍无码一区二区| 无码视频在线看| 91艹逼| 国产三级精品三级在线观看| av天堂资源| 精品人妻无码一区二区三区| 午夜美女视频| 亚洲va在线∨a天堂va欧美va | 狠狠色噜噜狠狠狠888| 日逼无码视频| 老司机在线免费视频| 无码啪啪| 日本高清色清di免费观看| 中文字幕的| 99热最新网址| 无码视频免费看| 一道本一区| 无码狠狠躁久久久久久久91| 日逼日逼日逼| 亚洲成人无码一区| 黑人猛躁白人BBBBBBBBB| 日韩天堂在线观看| 波多野结衣在线观看一区二区| 国精产品一区一区三区有限公司杨| 日韩第三页| 欧美日韩综合| 天天日夜| 中文字幕自拍偷拍| 久艹久| 亚洲三区视频| 欧美性久久久久| 北条麻妃久久视频在线播放| 国产黄色在线观看| 欧美色色网| 男人天堂网站| 五月丁香六月| 国产在线激情| 亚洲无码高清在线观看| 五月天激情婷婷| 国产秘久久一区二区| 国产成人精品无码免费| 插插菊花综合网| 99精品在线免费观看| 天天操一操| 高清无码在线观看免费| 69国产精品| 黄片亚洲| 在线免费观看黄色小视频| 日本三级中文字幕| 国产毛片网| 久久久精品亚洲| 欧美丰满人妻| 国产AV黄片| 乱伦网址| 九色PORNY蝌蚪视频| 99这里有精品视频| 少妇bbw搡bbbb搡bbbb| 中文字幕一区二区三区四区五区六区| 亚洲无码大全| 熟女18p| 日韩在线观看一区| 中文字幕人成人乱码亚洲电影| 国产三级av在线| 日批免费网站| 另类老妇videos另类| 男女视频网站| 国产精品一区在线| 国产精品自拍小视频| 九色PORNY国产成人| 欧美一级婬片A片免费软件| 久久午夜视频| 东方AV在线观看| 欧美日韩黄色| 四川BBBB擦BBBB| 91视频网址| 欧美日韩久久| 日韩强操逼网| 国产亚洲欧美一区二区| AV天堂小说| 久久无码影视| 欧美色图狠狠操| 簧片网站免费| 91婷婷在线| 秋霞丝鲁片一区二区三区手机在绒免 | 天天色色| 91乱子伦国产乱子伦无码| 伊人婷婷色香综合| 毛片自拍| A片欧美| 亚洲av黄| 操逼网123首页| 亚洲精品成人网站| 婷婷五月天激情电影| 国产精品高潮呻吟| 超碰人人操人人摸| 国产精品三级视频| 青草综合| 免费视频91蜜桃| 亚洲,制服,综合,中文| 日韩电影无码| 国产高清视频在线| 五月天干美女| 亚洲AAAAAA| 91AV免费看| 婷婷色色五月| 日本黄A三级三级三级| 自拍偷拍图区| 久久久久久久久久成人永久免费视频| 精品孕妇孕交无码专区| 亚洲福利网站| 日日夜夜天天| 黄网在线看| 国产色婷婷| 国产V精品| 国产一区在线播放| 久久久久黄| 免费伊人大香蕉| 亚洲高清成人| 日韩黄色三级| 少妇人妻无码| 高清无码人妻| 亚洲综合激情五月久久| 日韩亚洲中文在线| 一级国产片| 国产精品777777| 中文字幕在线观看有码| 午夜激情免费| 久久AV秘一区二区三区水生| 成人午夜福利电影| 国产综合激情| 91在线观看18| 国产人与禽zoz0性伦| 大香蕉电影网站| 黄频视频| 成人AV片导航| 久久精品一区二区三区蜜芽的特点| 人人摸人人操人人| 亚洲av电影在线观看| 久久黄色网络| 国产精品黑人ThePorn| 91精品视频在线免费观看| 久久情| 一区二区无码区| 男女无码视频| 国产成人午夜| 中文字幕在线观看完整av| 免费福利在线视频| 无码人妻一区二区三一区免费n狂飙 | 国产性生活视频| 国产欧美岛国| 国产三级AV在线| 香蕉久久网| 国产一级内射| 亚洲成人69| 免费无码婬片AAAAA片| 免费看的操逼视频| 玖玖资源站中文字幕| 玖玖爱国产| 爱爱动态图| 麻豆MD传媒MD0071| 午夜视频无码| 可以免费看的黄色视频| AV久草| 婷婷六月激情| 天天日夜夜爽| 操美女一区二区| 丁香婷婷社区| 少妇人妻精品| 日韩操逼网站| 色噜噜狠狠色综无码久久合欧美| 黄色视频在线观看亚洲一区二区三区免费| 九月丁香| 四季AV之日韩人妻无码| 欧美一区二区三区成人片在线| 国产理伦| 中文字幕一区二区无码成人| 俺来俺去www色婷婷| 一本道精品在线| 成人午夜福利高清视频| 蜜桃av在线| 黄片网站免费| 97热| 91在线一区| 精品五月天| 特黄aaaaaaaa真人毛片| 在线高清无码| 亚洲视频三区| 少妇人妻AV| 91丨熟女丨对白| 影音先锋亚洲资源| 大秀91视频| 拍拍拍免费视频| 亚洲A片一区二区三区电影网 | 米奇色色色| 69成人天堂无码免费| 久99久热| 亚洲AV免费在线观看| 日韩一级爱爱| 天天日天天干美女| 亚洲色久悠悠| 精品中文字幕在线观看| 91无码一区二区三区在线| 蜜臀久久99久久久久久宅男| 在线国产福利| 亚洲xxxxx| 国产操屄视频| 宅男看片| 日韩无码AV一区二区三区| 日韩AV中文字幕在线| www.蜜桃| 国产在线内射| 国产1区2区3区中文字幕| 男人天堂视频网| 黄色电影免费在线观看| 日韩精品无码av| 免费无码毛片一区二区A片| 亚洲AV无码| 成人网在线视频| 色哟哟一区二区三区四区| 欧美老女人操逼视频| 少妇bbw搡bbbb搡bbbb| 亚洲AV免费| 五月天国产视频| 亚洲无码性爱视频| 美女综合网| 五月丁香欧美性爱| 欧美性综合网| 少妇搡BBBB搡BBB搡造水多| 中文字幕在线观看av| 五月天乱伦网| 中文字幕日本人妻| 九九乱伦| i美女福利视频| 玖玖资源在线观看| se婷婷| 99精品视频在线免费观看| 可以免费看AV的网站| 国产成人精品免费视频| 婷婷开心五月天| 蜜桃Av噜噜一区二区三区四区| 伊人无码在线| 偷拍99| 人妻体体内射精一区二区| 麻豆自拍偷拍视频| 先锋影音AV在线| 国产成人AV免费观看| 影音先锋三级资源| 亚洲中文字幕在线看| 综合网久久| 蜜桃av无码| 中国特级毛片| 亚洲,制服,综合,中文| 日韩美女毛片| 国产A级毛片久久久久久| 国产一卡二卡在线| 亚洲麻豆| a片免费网址| 五月丁香狠狠爱| 内射少妇18| 丰满的人妻一区二区10| 熟女少妇视频| 国产酒店自拍| AV在线一区二区| 伊人久久免费| 啪啪啪免费网站| 日韩欧美成人网| 成全在线观看高清的| 中文字字幕在线中文乱码更新时间 | 天堂精品在线| av牛牛| 国产精视频| 一级国产黄色视频| 日韩四区| 夜夜草视频| 国产精品久久久久国产A级| 色综合999| 丝瓜视频| 粉嫩小泬BBBB免费看| 无码在线高清| A片视频网站| www.黄色大片| 激情五月天导航| 无码一区二区三区四区五区| 西西人体444www| 亚洲涩情91日韩一区二区| 久久久久久久久久久久久久久久久久久久| 人人爽人人爽人人| 99免费在线观看视频| 亚洲欧美日韩黑料吃瓜在线观看| XXXX国产| 残忍另类BBWBBWBBW| 91精品国产成人www| 日日艹夜夜艹| 国产精品一区二区视频| 欧美日韩成人网| 999一区二区三区| 亚洲一区| 国产在线看| 蜜臀导航| 蜜芽av在线观看| 波多无码在线| 男女爱爱动态图| 国产黄片在线视频| 成人特级毛片| 激情无码五月天| 日韩WWW| 亚洲无码免费在线| 欧美熟妇性爱视频| 午夜激情免费|