99精品久久精品一区二区-亚洲熟妇无码?v在线播放-日本国产精品无码字幕在线观看-久久久亚洲永夜AV-亚洲一级无码一区二区一-免费国产成高清人在线视频-中文字幕乱码免费观看-国产毛片精品妇女久久久

ARTICLE DETAIL

資訊詳情

深耕商務(wù)建站與企業(yè)官網(wǎng)運營的一線實戰(zhàn)洞察。

CDP協(xié)議+Electron桌面應(yīng)用自動化:原理、實踐與踩坑指南

CDP協(xié)議+Electron桌面應(yīng)用自動化:原理、實踐與踩坑指南 我們團隊最近在做一個“批量流程自動化”的內(nèi)部項目核心訴求是讓一個Agent能夠像人一樣操作我們公司某款Electron桌面客戶端自動完成錄入、查詢、導(dǎo)出這一連串動作。最開始想的是圖像識別加模擬鍵鼠試了幾天發(fā)現(xiàn)全是坑窗口一遮擋坐標(biāo)就偏、高分屏DPI縮放直接讓點擊位置漂移、頁面元素稍微變個樣式腳本就崩。后來我想到Electron的底層其實是Chromium既然瀏覽器可以通過CDP協(xié)議被人遠(yuǎn)程控制那Electron桌面應(yīng)用理論上也應(yīng)該可以。折騰了兩周最終用CDP協(xié)議打通了一條完全不同的路子——不依賴坐標(biāo)、不依賴圖像識別直接通過協(xié)議精準(zhǔn)操作應(yīng)用內(nèi)的DOM元素。這篇文章就把我從零摸到可用的整個過程、代碼和踩過的坑完整記錄下來希望能給正在做Agent自動化操作桌面應(yīng)用的朋友一些參考。剛開始接觸CDP協(xié)議時我以為它只能調(diào)試網(wǎng)頁但它的能力邊界其實遠(yuǎn)超“F12開發(fā)者工具”的范疇。依賴DOM和運行時求值我們不僅能在Electron應(yīng)用內(nèi)讀取任意頁面數(shù)據(jù)、觸發(fā)點擊和輸入事件還能調(diào)用應(yīng)用內(nèi)已經(jīng)暴露的接口、監(jiān)聽網(wǎng)絡(luò)請求、攔截響應(yīng)甚至直接操作應(yīng)用內(nèi)部狀態(tài)。這對Agent來說是個巨大優(yōu)勢它可以像真實用戶在頁面上完成操作又不用依賴脆弱的視覺算法穩(wěn)定性高出一個數(shù)量級。1. 項目思路為什么選擇CDP方案操作Electron應(yīng)用1.1 桌面應(yīng)用自動化的三條技術(shù)路線對比在動手之前我認(rèn)真比較過市面上主流的桌面自動化方案大致分成三類。第一類是傳統(tǒng)鍵鼠模擬工具這類方案的思路是記住屏幕上的固定位置然后通過調(diào)用系統(tǒng)接口移動鼠標(biāo)、執(zhí)行點擊和鍵盤輸入。優(yōu)點是通用性強幾乎什么軟件都能驅(qū)動缺點是穩(wěn)定性極度依賴窗口狀態(tài)。窗口一移動、一最小化、一被遮擋整個腳本就廢了。再加上高DPI環(huán)境下屏幕坐標(biāo)系會縮放同一個按鈕在不同分辨率屏幕上的位置完全不同維護成本很高。第二類是圖像識別方案原理是持續(xù)截屏然后在截圖中用模板匹配或深度學(xué)習(xí)找目標(biāo)元素。這種方案在游戲自動化領(lǐng)域用得比較多因為游戲環(huán)境里沒有其他控制通道。但在偏辦公類的Electron應(yīng)用里它的問題是性能開銷大、識別有延遲、而且對UI微調(diào)過于敏感。換個按鈕圓角、改個圖標(biāo)配色識別準(zhǔn)確率就會斷崖式下降。第三類就是我要說的CDP協(xié)議方案核心思路是Electron應(yīng)用本質(zhì)上跑著一個完整的Chromium實例Chromium天然支持遠(yuǎn)程調(diào)試協(xié)議那我們完全可以繞過屏幕坐標(biāo)直接通過協(xié)議讀寫應(yīng)用內(nèi)DOM、派發(fā)事件、求值JS代碼。這個方案的好處是穩(wěn)定性極高元素位置隨便變、窗口隨便移動都不影響腳本因為它操作的是頁面內(nèi)部的對象結(jié)構(gòu)而不是屏幕上的像素坐標(biāo)。1.2 CDP方案解決的核心痛點選擇CDP最直接的原因是它解決了我們項目里兩個最難纏的問題。第一個問題是“窗口遮擋”。我們的Agent需要并發(fā)處理多個任務(wù)經(jīng)常會在多個應(yīng)用窗口之間切換傳統(tǒng)鍵鼠方案一旦遇到后臺窗口被遮擋就完全束手無策。而CDP方案操作的是頁面內(nèi)部結(jié)構(gòu)根本不需要窗口處于前臺甚至窗口最小化了它照樣能執(zhí)行點擊和輸入。這個特性對我們來說至關(guān)重要意味著Agent可以同時“操作”多個實例而不用管哪個窗口在最上面。第二個問題是“元素定位精度”。用了CDP之后定位一個輸入框就是一句document.querySelector(#username)的事情穩(wěn)定到不管CSS怎么改都能找到。如果元素加了>[客戶端] ---HTTP GET /json/list--- [Electron應(yīng)用調(diào)試服務(wù)] ---targetId列表--- [客戶端] ---WebSocket連接 ws://...--- [Electron應(yīng)用調(diào)試服務(wù)] ---發(fā)送命令 {id:1,method:DOM.enable}--- [客戶端] ---收到結(jié)果 {id:1,result:{}}---日常使用中90%的命令都是單向請求響應(yīng)模式。協(xié)議里也有事件推送機制比如頁面加載完成、DOM節(jié)點更新、控制臺輸出這些都會主動推給客戶端這點在我們后面做動態(tài)等待的時候會很關(guān)鍵。2.2 為什么Electron應(yīng)用天然支持CDPElectron應(yīng)用之所以能直接用CDP協(xié)議控制根本原因是它本身就是基于Chromium外殼的。Electron官方其實屏蔽了一部分Chromium的調(diào)試能力但只要我們啟動的時候加上一個遠(yuǎn)程調(diào)試端口參數(shù)完整的CDP功能就會暴露出來。具體來說在啟動Electron應(yīng)用的時候加上--remote-debugging-port9222Electron主進(jìn)程就會在9222端口開啟一個調(diào)試服務(wù)。這個調(diào)試服務(wù)不僅能發(fā)現(xiàn)所有已經(jīng)打開的窗口還能讓外部客戶端通過CDP協(xié)議和這些窗口的渲染進(jìn)程建立實時通信通道。這里有個Electron特有的坑要特別注意Electron應(yīng)用可能有多個窗口而每個窗口在CDP協(xié)議里都是一個獨立的Target。所以連接時要先通過HTTP接口拿到所有Target列表再根據(jù)title或url字段篩選出我們要操作的那個窗口而不是默認(rèn)連接第一個。有一點值得說明Electron渲染進(jìn)程和普通Chrome頁面在CDP層面上幾乎沒有區(qū)別原本為瀏覽器自動化設(shè)計的工具和庫在Electron應(yīng)用上都能無縫使用。這意味著生態(tài)里現(xiàn)成的Node.js庫、Python客戶端在這一場景下都能用。2.3 協(xié)議中Agent操作高頻依賴的能力域CDP協(xié)議按功能劃分了幾十個模塊做Agent操作時不需要所有模塊全用熟練掌握下面幾個就夠覆蓋95%以上的場景了。Runtime域用來在頁面上下文里執(zhí)行任意JavaScript代碼這算是整個協(xié)議最核心的入口。Runtime.evaluate可以執(zhí)行表達(dá)式并返回結(jié)果Runtime.callFunctionOn可以在指定對象上調(diào)用函數(shù)。Agent的很多操作本質(zhì)上就是組合各種JS代碼去實現(xiàn)。DOM域負(fù)責(zé)文檔對象模型的查詢和操作。DOM.getDocument拿到文檔根節(jié)點、DOM.querySelector從根節(jié)點往下找目標(biāo)元素、DOMDebugger用來調(diào)試監(jiān)聽等。但我們實際項目里更多是用Runtime.evaluate直接跑document.querySelector來代替DOM域接口因為這樣寫起來更自然而且要獲取的上下文信息更直接。Input域是模擬用戶輸入的關(guān)鍵模塊。Input.dispatchMouseEvent可以派發(fā)鼠標(biāo)按下、釋放、移動事件Input.dispatchKeyEvent派發(fā)鍵盤事件。這套接口模擬的是系統(tǒng)輸入層的事件觸發(fā)的行為比直接調(diào)用JS的element.click()更接近真實用戶操作會觸發(fā)完整的鼠標(biāo)懸停、激活、焦點切換等狀態(tài)流。Page域用來管理系統(tǒng)頁面狀態(tài)。Page.navigate跳轉(zhuǎn)導(dǎo)航、Page.reload刷新頁面、Page.captureScreenshot截圖以及Page.setDeviceMetricsOverride模擬設(shè)備尺寸等。對Agent自動操作桌面應(yīng)用來說Page.captureScreenshot在出了問題時抓現(xiàn)場特別有用。Network域能攔截和查看網(wǎng)絡(luò)請求。Agent在操作過程中可能需要等待某個接口返回后才能繼續(xù)下一步這個域可以監(jiān)聽請求響應(yīng)作為操作完成與否的判據(jù)。這些模塊組合起來就能構(gòu)建一個完整的自動化操作閉環(huán)定位元素、讀取狀態(tài)、執(zhí)行交互、驗證結(jié)果、異常兜底。3. 環(huán)境準(zhǔn)備啟動帶調(diào)試端口的Electron應(yīng)用3.1 修改Electron應(yīng)用啟動方式要讓CDP協(xié)議能連上Electron應(yīng)用第一步是讓應(yīng)用以調(diào)試模式啟動。最簡單的做法是在應(yīng)用入口文件里通過app.commandLine.appendSwitch添加參數(shù)也可以直接在終端啟動命令后面拼接參數(shù)。在終端里啟動的命令是這樣的./your-electron-app --remote-debugging-port9222不過這種方式有個不方便的地方如果Electron應(yīng)用的啟動是通過雙擊桌面圖標(biāo)完成的那命令行參數(shù)就傳不進(jìn)去。我們項目里的做法是在Electron主進(jìn)程代碼里強制開啟調(diào)試端口這樣不管用戶怎么啟動應(yīng)用調(diào)試服務(wù)都會自動掛在預(yù)設(shè)端口上。// main.js const { app } require(electron) app.commandLine.appendSwitch(remote-debugging-port, 9222) app.commandLine.appendSwitch(remote-allow-origins, *) app.whenReady().then(() { // 正常啟動應(yīng)用邏輯 })這里有一個安全相關(guān)的設(shè)置需要格外注意remote-allow-origins參數(shù)如果你不主動設(shè)置新版Chromium會默認(rèn)校驗WebSocket連接的來源域名。我們一開始連接時一直報403錯誤找了好幾輪才發(fā)現(xiàn)是這個配置問題。本地自動化場景直接設(shè)為*通配符就行但如果在公網(wǎng)環(huán)境使用一定要收緊這個配置。3.2 多實例運行時的端口規(guī)劃我們的Agent要支持并發(fā)操作多個Electron實例所以統(tǒng)一的固定端口滿足不了需求。解決方法是讓每個應(yīng)用實例啟動時自動分配一個獨立端口然后把端口號傳給Agent調(diào)度器。Electron應(yīng)用可以通過環(huán)境變量來指定端口號const debugPort process.env.DEBUG_PORT || 9222 app.commandLine.appendSwitch(remote-debugging-port, debugPort)調(diào)度端為每個實例動態(tài)分配可用端口#!/bin/bash BASE_PORT9400 for i in {1..5} do CURRENT_PORT$((BASE_PORT i)) DEBUG_PORT$CURRENT_PORT ./your-electron-app echo Instance $i started on port $CURRENT_PORT done這樣多個實例就能并行跑在各自獨立的調(diào)試端口上Agent可以針對每個端口各建一條CDP連接互不干擾。實測下來單機跑幾十個實例沒有性能瓶頸主要卡在內(nèi)存和CPU資源上。3.3 確認(rèn)調(diào)試服務(wù)已就緒應(yīng)用啟動之后需要用命令行驗證一下調(diào)試服務(wù)是否正常開啟。瀏覽器打開或curl請求下面這個地址curl http://127.0.0.1:9222/json/version如果一切正常會返回一段JSON里面包含Browser版本號、webSocketDebuggerUrl和chrome-version這些關(guān)鍵信息。再訪問/json/list就能看到當(dāng)前應(yīng)用打開的每個頁面窗口的詳細(xì)信息了。[ { description: , devtoolsFrontendUrl: /devtools/inspector.html?ws127.0.0.1:9222/devtools/page/xxxxx, id: A8C1E992F502CF11B6E9C56F33756D2A, title: 工作臺, type: page, url: file:///app/index.html, webSocketDebuggerUrl: ws://127.0.0.1:9222/devtools/page/A8C1E992F502CF11B6E9C56F33756D2A } ]我這里會重點看webSocketDebuggerUrl字段它是后續(xù)建立WebSocket連接的入口。而title字段幫我們從多個窗口里挑出要控制的那一個。4. 核心實操用Node.js手寫一個CDP客戶端4.1 建立WebSocket連接我選擇用Node.js寫客戶端因為Electron應(yīng)用前端本身就是JavaScript環(huán)境而且Node.js的WebSocket生態(tài)非常成熟。項目里用的庫是ws它輕量穩(wěn)定在NPM上可以直接安裝。npm install ws創(chuàng)建一個cdp-client.js文件先實現(xiàn)最基礎(chǔ)的連接邏輯const WebSocket require(ws) class CDPClient { constructor(wsUrl) { this.wsUrl wsUrl this.ws null this.callbacks new Map() this.eventHandlers new Map() this.commandId 0 } connect() { return new Promise((resolve, reject) { this.ws new WebSocket(this.wsUrl, { maxPayload: 256 * 1024 * 1024 }) this.ws.on(open, () { console.log([CDP] WebSocket connected) resolve() }) this.ws.on(message, (data) { const msg JSON.parse(data.toString()) if (msg.id) { const callback this.callbacks.get(msg.id) if (callback) { this.callbacks.delete(msg.id) if (msg.error) { callback.reject(new Error(msg.error.message)) } else { callback.resolve(msg.result) } } } else { // 事件消息 const handlers this.eventHandlers.get(msg.method) if (handlers) { handlers.forEach((handler) handler(msg.params)) } } }) this.ws.on(error, (err) { console.error([CDP] WebSocket error:, err.message) reject(err) }) this.ws.on(close, () { console.log([CDP] WebSocket closed) }) }) } send(method, params {}) { return new Promise((resolve, reject) { const id this.commandId this.callbacks.set(id, { resolve, reject }) this.ws.send(JSON.stringify({ id, method, params })) }) } on(method, handler) { if (!this.eventHandlers.has(method)) { this.eventHandlers.set(method, []) } this.eventHandlers.get(method).push(handler) } }這個實現(xiàn)里藏了一個特別重要的細(xì)節(jié)maxPayload參數(shù)一定要調(diào)大。Chromium的CDP服務(wù)端默認(rèn)消息大小限制很低但我們的自動化場景里經(jīng)常要一次傳輸大量DOM數(shù)據(jù)尤其頁面復(fù)雜時DOM.getDocument接口返回的序列化結(jié)果可能達(dá)到幾十MB。不調(diào)大這個參數(shù)消息一超過限制WebSocket就會靜默斷開排查起來讓人非常抓狂。4.2 常用命令封裝連接建立后我會把高頻操作封裝成一個個獨立方法這樣Agent邏輯層寫起來就像調(diào)用本地函數(shù)一樣自然。class CDPClient { // 獲取文檔樹 async getDocument() { const result await this.send(DOM.getDocument, { depth: -1 }) return result.root } // 通過選擇器查找元素節(jié)點ID async querySelector(nodeId, selector) { const result await this.send(DOM.querySelector, { nodeId, selector }) return result.nodeId } // 執(zhí)行JavaScript表達(dá)式 async evaluate(expression, awaitPromise true) { const result await this.send(Runtime.evaluate, { expression, awaitPromise, returnByValue: true }) if (result.exceptionDetails) { throw new Error(頁面執(zhí)行JS出錯: result.exceptionDetails.text) } return result.result.value } // 模擬鼠標(biāo)點擊基于元素中心坐標(biāo) async clickBySelector(selector) { const expr (() { const el document.querySelector(${selector}) if (!el) return null const rect el.getBoundingClientRect() return { x: rect.left rect.width / 2, y: rect.top rect.height / 2, text: el.innerText } })() const { x, y } await this.evaluate(expr) if (x null) return false await this.send(Input.dispatchMouseEvent, { type: mousePressed, x: Math.round(x), y: Math.round(y), button: left, clickCount: 1 }) await this.send(Input.dispatchMouseEvent, { type: mouseReleased, x: Math.round(x), y: Math.round(y), button: left, clickCount: 1 }) return true } }這里關(guān)于點擊坐標(biāo)有個需要說清楚的關(guān)鍵細(xì)節(jié)頁面里的getBoundingClientRect()拿到的是CSS像素坐標(biāo)而Input.dispatchMouseEvent默認(rèn)按物理像素解釋兩者在非100%縮放比下會不一致。Electron應(yīng)用頁面里如果設(shè)置了zoomFactor或系統(tǒng)DPI縮放坐標(biāo)就會偏移。穩(wěn)妥的做法是在連接建立后先讀取設(shè)備的縮放比例然后把坐標(biāo)手動換算過去。async getDeviceMetrics() { const metrics await this.send(Page.getLayoutMetrics) const { cssScaleFactor } metrics return { scaleFactor: cssScaleFactor, viewportWidth: metrics.cssLayoutViewport.clientWidth, viewportHeight: metrics.cssLayoutViewport.clientHeight } }拿到cssScaleFactor之后凡是需要派發(fā)鼠標(biāo)事件的坐標(biāo)統(tǒng)統(tǒng)用x / scaleFactor換算后再發(fā)給CDP服務(wù)端就能徹底規(guī)避縮放導(dǎo)致的點擊偏移問題。4.3 完整操作流程示例下面給一個Agent自動登錄并執(zhí)行查詢的完整代碼示例這里模擬的場景是打開某個客戶管理桌面應(yīng)用自動登錄、跳轉(zhuǎn)到客戶列表頁、輸入關(guān)鍵字搜索、提取搜索結(jié)果。const CDPClient require(./cdp-client) async function getTargetList(baseUrl) { const res await fetch(http://127.0.0.1:9222/json/list) const targets await res.json() return targets.filter((t) t.type page) } async function startAgent() { const targets await getTargetList(http://127.0.0.1:9222) const target targets.find((t) t.title.includes(客戶管理)) if (!target) { throw new Error(未找到目標(biāo)窗口請檢查應(yīng)用是否已打開客戶管理界面) } const client new CDPClient(target.webSocketDebuggerUrl) await client.connect() // 啟用相關(guān)域 await client.send(Runtime.enable) await client.send(Page.enable) // 1. 等待頁面加載完成 await client.send(Page.reload) await waitForPageLoad(client) // 2. 自動填充登錄表單 await client.evaluate( document.querySelector(#username).value admin document.querySelector(#password).value 123456 ) // 觸發(fā)React/Vue框架的input事件 await client.evaluate( const input document.querySelector(#username) input.dispatchEvent(new Event(input, { bubbles: true })) ) await client.clickBySelector(.login-btn) // 3. 等待登錄跳轉(zhuǎn) await waitForSelector(client, .customer-list-page, 10000) // 4. 輸入搜索關(guān)鍵字 await client.evaluate( const searchInput document.querySelector(#search-input) const nativeInputValueSetter Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, value).set nativeInputValueSetter.call(searchInput, 張三) searchInput.dispatchEvent(new Event(input, { bubbles: true })) ) await client.clickBySelector(.search-button) // 5. 提取搜索結(jié)果表格數(shù)據(jù) const rows await client.evaluate( (() { const items [] document.querySelectorAll(.customer-table tbody tr).forEach((tr) { items.push({ name: tr.querySelector(.col-name).innerText, phone: tr.querySelector(.col-phone).innerText, status: tr.querySelector(.col-status).innerText }) }) return items })() ) console.log(查詢到客戶數(shù)據(jù):, JSON.stringify(rows, null, 2)) // 6. 截圖留檔 const screenshot await client.send(Page.captureScreenshot, { format: png }) require(fs).writeFileSync(result.png, Buffer.from(screenshot.data, base64)) client.ws.close() } async function waitForPageLoad(client) { return new Promise((resolve) { const timer setTimeout(() resolve(), 10000) client.on(Page.loadEventFired, () { clearTimeout(timer) resolve() }) }) } async function waitForSelector(client, selector, timeout 5000) { const start Date.now() while (Date.now() - start timeout) { const exists await client.evaluate(!!document.querySelector(${selector})) if (exists) return true await new Promise((r) setTimeout(r, 200)) } throw new Error(等待超時: 未找到元素 ${selector}) } startAgent().catch((err) { console.error(Agent執(zhí)行出錯:, err) process.exit(1) })這個流程跑起來之后我第一感受是整個過程實在太流暢了。沒有惱人的坐標(biāo)計算沒有脆弱到讓人崩潰的截圖比對每個步驟都像在寫前端測試用例。更關(guān)鍵的是調(diào)試體驗有了質(zhì)的飛升——CDP協(xié)議自帶的DevTools界面能實時看到Agent當(dāng)前操作到了頁面什么位置出問題的時候打開開發(fā)者工具立刻就能看清頁面真實狀態(tài)。5. 更省力的做法用Puppeteer庫連接現(xiàn)成的Electron實例5.1 Puppeteer.connect怎樣復(fù)用已有應(yīng)用人肉手寫WebSocket客戶端的好處是靈活可控所有細(xì)節(jié)都捏在自己手里但日常業(yè)務(wù)開發(fā)我更建議直接選用成熟庫——Puppeteer生態(tài)就是其中一個相當(dāng)省力的選擇。Puppeteer有個很實用的能力不只能自己啟動瀏覽器還能連接一個已經(jīng)在運行的Chromium實例。這個模式和我們在Electron里用CDP的場景簡直是天作之合Electron應(yīng)用自己啟動Puppeteer負(fù)責(zé)連上去控制它。這樣Agent代碼寫起來完全不需要關(guān)心WebSocket底層細(xì)節(jié)。const puppeteer require(puppeteer) async function attachToElectron() { const browser await puppeteer.connect({ browserURL: http://127.0.0.1:9222, defaultViewport: null }) const pages await browser.pages() console.log(發(fā)現(xiàn)窗口數(shù)量:, pages.length) // 找到目標(biāo)頁面 const targetPage pages.find((page) page.title().includes(客戶管理)) // 現(xiàn)在可以用Page對象做一切操作了 await targetPage.type(#username, admin) await targetPage.click(.login-btn) await targetPage.waitForSelector(.customer-list-page, { timeout: 10000 }) const data await targetPage.evaluate(() { return document.querySelector(.customer-info).innerText }) console.log(獲取到數(shù)據(jù):, data) }需要重點說明一個參數(shù)defaultViewport: null這個必須顯式設(shè)置。因為Puppeteer默認(rèn)會給頁面套一個800x600的視口如果不關(guān)掉它頁面布局會和正常桌面應(yīng)用完全不一樣——某些元素會被折疊到響應(yīng)式布局的移動端形態(tài)里點擊定位全都亂套。連Electron應(yīng)用時一定要保持應(yīng)用原有的桌面視口。5.2 和手寫客戶端的選型對比Puppeteer庫本身還是一層殼它和CDP的關(guān)系可以理解成這樣Puppeteer的API把CDP復(fù)雜的協(xié)議細(xì)節(jié)全部包裝成了緊湊方法page.click對應(yīng)點擊坐標(biāo)計算、page.waitForSelector對應(yīng)輪詢DOM查詢、page.evaluate對應(yīng)運行時求值。該封裝的都封裝好了調(diào)用體驗確實比純手寫友好很多。但封裝也意味著控制粒度變粗。如果你需要深度定制協(xié)議行為——比如攔截特定網(wǎng)絡(luò)請求、獲取HTTP響應(yīng)的原始頭部、監(jiān)聽復(fù)雜的底層事件流——Puppeteer提供的接口可能又成了阻礙這時候就得靠page._client().send(Network.getResponseBody)這類半私有接口或者干脆自己建WebSocket連接。兩個方案我給出的適用建議是業(yè)務(wù)邏輯快速驗證、Agent原型搭建直接用Puppeteer庫開發(fā)效率最高需要深度定制協(xié)議行為、集成到生產(chǎn)環(huán)境的Agent底座里手寫一個薄客戶端更可控因為每個連接狀態(tài)、每條命令的結(jié)果都由你自己管理。我們團隊最終的生產(chǎn)實現(xiàn)是兩者結(jié)合外部用Puppeteer做交互編排關(guān)鍵底層節(jié)點通過手寫CDP客戶端做定制擴展。各有各擅長的地方。5.3 連接時的常見坑Puppeteer連接Electron實例時有幾個點是經(jīng)常踩到的。Puppeteer庫的版本是一個關(guān)鍵變量不同大版本的connect實現(xiàn)細(xì)節(jié)會有差異。舊版本可能走舊握手流程新版本對協(xié)議版本的要求也不同。建議優(yōu)先使用當(dāng)前最新的穩(wěn)定版本遇到握手失敗先查版本兼容。連接超時和環(huán)境變量也要注意默認(rèn)情況下Puppeteer連不上會很快就報錯可以手動加大超時參數(shù)。同時檢查啟動Electron應(yīng)用的環(huán)境變量里是否設(shè)置了影響Chromium網(wǎng)絡(luò)棧的參數(shù)。還有一個窗口識別的問題Electron應(yīng)用經(jīng)常會彈出子窗口、加載iframe、開DevTools面板這些都會在Target列表里多出噪點。找目標(biāo)窗口時一定要用title字段做精確匹配甚至通過URL特征過濾避免連到DevTools或隱藏的后臺窗口上。6. 實戰(zhàn)中的棘手問題和排查方法6.1 WebSocket連接被403拒絕這個問題的主要原因是前面提到的remote-allow-origins校驗。Chrome從某個版本開始對WebSocket連接有了來源校驗?zāi)J(rèn)拒絕來自非DevTools前端的連接。Node.js的WebSocket客戶端發(fā)起的握手請求沒有合法的Origin來源會被服務(wù)端攔下。解決辦法是在Electron主進(jìn)程啟動參數(shù)里加上--remote-allow-origins*放開來源限制。如果安全要求比較高也可以把這行參數(shù)改為只允許可信來源但本地自動化場景直接放開是最高效的。6.2 獲取不到任何Target列表/json/list接口返回空數(shù)組的原因通常是Electron應(yīng)用沒有渲染出頁面窗口。Electron應(yīng)用如果創(chuàng)建了后臺隱藏窗口或者頁面還在加載過程中窗口還沒注冊到調(diào)試服務(wù)Target列表就可能短暫為空。處理方案是等待幾秒再刷新列表并過濾掉type不為page的空條目。還有一種原因是Electron應(yīng)用開啟了GPU加速導(dǎo)致的啟動異常渲染進(jìn)程崩潰或者卡在初始化階段。用app.disableHardwareAcceleration()關(guān)掉GPU加速再試如果恢復(fù)了就把這個配置加到生產(chǎn)啟動參數(shù)里。6.3 React/Vue框架輸入框值不生效直接給輸入框設(shè)置value屬性然后dispatch事件這是最經(jīng)典的框架綁定失效場景。React或者Vue的受控組件會維護自己的內(nèi)部狀態(tài)直接設(shè)置DOM的value屬性框架狀態(tài)完全感知不到表單提交的時候值始終是空的。解決辦法是用原生屬性描述符的方式觸發(fā)框架的setterconst nativeInputValueSetter Object.getOwnPropertyDescriptor( window.HTMLInputElement.prototype, value ).set nativeInputValueSetter.call(input, 目標(biāo)值) input.dispatchEvent(new Event(input, { bubbles: true }))實測下來這個方法能穩(wěn)定觸發(fā)React的onChange和Vue的v-model更新。這個技巧在做Agent自動化時幾乎天天用是繞不開的核心技能。6.4 點擊事件不觸發(fā)通過Input.dispatchMouseEvent派發(fā)鼠標(biāo)事件時有個細(xì)節(jié)容易被忽略坐標(biāo)必須換算成相對視口的坐標(biāo)不能用頁面的絕對坐標(biāo)。如果頁面發(fā)生過滾動絕對坐標(biāo)點擊的位置就和視覺上的位置對不上。另一個原因是某些按鈕是通過CSS偽元素覆蓋來實現(xiàn)點擊的鼠標(biāo)事件雖然派發(fā)了但事件被覆蓋層攔截掉底層按鈕收不到。這種情況可以先讓Agent檢查一下目標(biāo)元素在視口中的位置有沒有被其他元素遮擋如果有遮擋就先把遮擋元素移開或隱藏再點擊。6.5 執(zhí)行過程中頁面跳轉(zhuǎn)過快頁面跳轉(zhuǎn)導(dǎo)航時CDP連接會自動隨著頁面Target的更新而重建此時之前建立的連接可能失效。處理邏輯是監(jiān)聽Page.frameNavigated事件一旦發(fā)現(xiàn)主框架導(dǎo)航完成就重新獲取文檔樹和上下文。我之前寫過一個輪詢waitForSelector邏輯也遇到過這個坑后面加了導(dǎo)航監(jiān)聽和重建邏輯后就徹底穩(wěn)定了。Agent在操作過程中還會遇到子框架的多層嵌套。定位元素前先確認(rèn)這個元素是主文檔里的還是iframe里的如果是iframe內(nèi)的元素Runtime.evaluate的計算上下文要切換成對應(yīng)的executionContextId不然JS代碼執(zhí)行時找不到元素。7. 工程化實踐Agent工具的封裝思路7.1 命令層級的拆解到這一步CDP連接和基本操作已經(jīng)跑通了。但生產(chǎn)環(huán)境里的Agent不能是系列零散腳本需要工程化地去封裝。我按職責(zé)拆分成了三個層級從底層往上依次是連接層、能力層、場景層。連接層負(fù)責(zé)管理WebSocket生命周期、多實例連接池、斷線重連。能力層基于CDP協(xié)議封裝了元素定位、文本操作、事件派發(fā)、數(shù)據(jù)提取這些通用原子能力。場景層則是根據(jù)具體業(yè)務(wù)流程做的組合比如“登錄流程”、“查詢流程”、“導(dǎo)出流程”每個流程都是一串能力層動作的有序編排。class AgentConnector { constructor() { this.connections new Map() } async connectInstance(instanceId, port) { const targets await this.getTargets(port) const target targets.find((t) t.title.includes(主窗口)) const client new CDPClient(target.webSocketDebuggerUrl) await client.connect() this.connections.set(instanceId, client) return client } } class ElementActions { constructor(client) { this.client client } async fill(selector, value) { await this.client.evaluate( (() { const el document.querySelector(${selector}) const setter Object.getOwnPropertyDescriptor( window.HTMLInputElement.prototype, value).set setter.call(el, ${value}) el.dispatchEvent(new Event(input, { bubbles: true })) })() ) } async waitText(selector, text, timeout 5000) { /* 輪詢等待文本出現(xiàn) */ } }7.2 斷線重連與操作補償Electron應(yīng)用理論上不會頻繁重啟但運行幾天后內(nèi)存占用上升、渲染進(jìn)程卡死也是常事。Agent底座必須具備斷線檢測和自動重連能力。我的實現(xiàn)里把“斷線重連”分成了兩層WebSocket層面的自動重連和業(yè)務(wù)操作層面的冪等補償。WebSocket層面監(jiān)聽close事件后暫停當(dāng)前任務(wù)按指數(shù)退避策略重新獲取Target列表并重新建立連接。這個過程可能失敗幾次但重試個三五次一般都能恢復(fù)。業(yè)務(wù)層面更復(fù)雜也是經(jīng)驗所在。舉個例子Agent剛點完“提交訂單”按鈕WebSocket斷了重新連上后這個訂單到底提交成功沒有頁面狀態(tài)是什么這時候需要有一個“操作驗證”環(huán)節(jié)檢查頁面上的成功提示或狀態(tài)流轉(zhuǎn)判斷操作是否生效。如果沒生效再按當(dāng)前頁面狀態(tài)重新定位元素或重新執(zhí)行流程。這個補償邏輯是個復(fù)雜的業(yè)務(wù)問題但反過來也說明Agent自動化不能只依賴底層連接更要在業(yè)務(wù)層建立可觀測、可恢復(fù)的機制。7.3 并發(fā)控制的策略我們項目后面從單實例擴展到了多實例并發(fā)并發(fā)控制踩過的坑也很有參考價值。CDP連接本身是異步的WebSocket消息可以亂序到達(dá)但CDP協(xié)議要求命令響應(yīng)必須按發(fā)送順序返回嗎其實不要求每條命令靠id字段關(guān)聯(lián)所以說多實例并發(fā)CDP線程是安全的。真正要注意的是共享資源。Electron應(yīng)用實例之間如果共享同一個本地數(shù)據(jù)庫文件或同一份用戶配置目錄并發(fā)操作就可能出現(xiàn)文件鎖沖突。我的處理是給每個實例分配獨立的數(shù)據(jù)目錄--user-data-dir/tmp/app-instance-1徹底隔離各自狀態(tài)。這一點在部署時要格外重視不然跑著跑著某個實例就玄學(xué)崩潰。8. 這套方案對Agent行業(yè)的實際價值8.1 從“模擬人”走向“操作對象”傳統(tǒng)桌面自動化軟件的核心思路是想方設(shè)法模擬人的行為——移動鼠標(biāo)、敲擊鍵盤、看著屏幕判斷結(jié)果。而CDP方案本質(zhì)上是直接讀寫系統(tǒng)內(nèi)部的對象模型相當(dāng)于跳過了模擬路徑直接針對應(yīng)用的真實語義結(jié)構(gòu)來操作。這種轉(zhuǎn)變帶來的穩(wěn)定性提升不用多說更深遠(yuǎn)的意義是讓Agent開始具備更準(zhǔn)的“結(jié)構(gòu)理解”。它能知道頁面上有一個輸入框叫#phone它的占位符提示是什么它當(dāng)前是否可編輯它的值是否符合校驗規(guī)則——這些信息都能通過一條Runtime.evaluate命令拿到。這個信息量比單純靠屏幕像素猜目標(biāo)高出好幾個維度是所有智能決策的基礎(chǔ)。8.2 對測試自動化和RPA領(lǐng)域的啟發(fā)這個思路對自動化測試行業(yè)同樣有很強的參考價值。傳統(tǒng)UI自動化測試主流方案是各種測試框架它們各有語法糖但核心思路和CDP是一致的——通過協(xié)議操作DOM。Electron應(yīng)用如果直接用CDP做測試優(yōu)勢是彈窗、系統(tǒng)對話框、原生菜單都還能繞過但業(yè)務(wù)功能回歸測試效率提升直接就是數(shù)量級的。RPA工具廠商也越來越多考慮在Electron應(yīng)用上提供CDP通道。既然應(yīng)用是基于Web技術(shù)構(gòu)建的最自然的自動化路徑就是Web自動化協(xié)議本身而不是做圖像識別這種吃力不討好的兜底方案。現(xiàn)在很多RPA產(chǎn)品對接Electron應(yīng)用都是優(yōu)先走CDP路線實在沒有調(diào)試端口才fallback到圖像識別。8.3 對未來AI Agent的啟示我在做這個項目的過程中有一個明顯的體感CDP協(xié)議對AI Agent來說就像給了它一雙免疫屏幕干擾的精準(zhǔn)手柄。未來的終端Agent如果要真正具備操作任意軟件完成復(fù)雜任務(wù)的能力應(yīng)用層協(xié)議通道會比視覺識別更可靠也更容易和AI的能力鏈打通?,F(xiàn)在已經(jīng)有智能Agent框架嘗試通過CDP協(xié)議讓大模型直接控制Electron應(yīng)用了大模型輸出意圖Agent框架將其翻譯為CDP命令序列腳本執(zhí)行操作結(jié)果回傳給模型判斷下一步動作。這套鏈路一旦跑通AI離真正意義上對桌面軟件的自主操作就更近了一步。9. 最后的一點心得整套方案做下來我最大的感受是技術(shù)的核心壁壘往往不在API調(diào)用本身而在于對底層協(xié)議信息的理解深度。CDP文檔洋洋灑灑幾十個域真正打開能力空間的其實是理解它的消息模型、事件機制、上下文限制這些細(xì)節(jié)決定了你能否在復(fù)雜業(yè)務(wù)場景里穩(wěn)定運行。具體到這個方案幾件事值得再做擴展連接池和斷線重連已經(jīng)是穩(wěn)定運行的基石建議任何Agent項目都優(yōu)先做好這一層每步操作之后都加一層狀態(tài)校驗用返回值驅(qū)動決策而不是無腦按時間線執(zhí)行能省掉大量排查爛攤子的時間測試環(huán)境里跑通了先別急著上生產(chǎn)先把異常場景的截圖、日志、記錄全鏈路鋪好出了事才能快速止血操作多實例時務(wù)必隔離數(shù)據(jù)目錄和長期會話資源否則并發(fā)一上來就各種靈異現(xiàn)象。如果你計劃在自己的Electron應(yīng)用上做Agent操作我的建議是最快路徑是先啟動應(yīng)用加上調(diào)試端口用Puppeteer連上去寫一個最小Demo把元素定位、事件派發(fā)、數(shù)據(jù)提取跑通再逐步疊加復(fù)雜場景。先把鏈路打通有了真實手感再談封裝和工程化。CDP這條路一旦走通Agent操作桌面應(yīng)用就不再是卑微的坐標(biāo)游戲而是一場徹底的結(jié)構(gòu)化對話。
返回列表
PREV
查看更多資訊
NEXT
返回資訊列表
丁香五月综合激情久久潮喷| mmm1717.6dbm人人爱人人操| 99热6这里只有精品| 九九视频网| 99精品在线观看| 五月丁香在线偷拍视频| 九九色精品| 久色精品| 思思热这里只有精品| 婷婷五月天色综合翘| 欧美在线ee日韩| 五月婷婷影| 思思网站| 热99国产精品| WWW,五月| WWW.久久久久久久久久久久久| 五月婷婷黄色| 五月久久婷婷丁香| 婷婷久久草| 色色日韩网| 九九久久腿| 99热这里只有精品2| 狠狠操天天日| 五月丁香六月婷婷在线小说视频| 久久五月婷| 91干在线视频| 国产亚洲99久久精品熟| 久久久国产精品黄毛片| 电影91久久久| 丁香性爱在线视频| 久久综合久色欧美综合狠狠| 丁香五月偷拍| 国产一区二区三区影院| 色婷婷香蕉| 婷婷 丁香 精品| 久久99色色| 五月婷婷久久综合| 97色在线| 婷婷综合激情五月中文字幕| 精品无码色| 久草狼人| 色99超碰| 大香线蕉伊人| 欧洲不卡视频| 久久久久久久丁香五月天婷婷| 亚洲国产网站| 97爱综合| 久9久9热久热| 狠狠草天天草| 色五月天成人在线| 第一区久久网站| 青青草原中文字幕| 妻久久久久| 亚洲精品成人| 激情综合播播| 五月丁香好婷婷A片网 | 久激情网| 婷婷五月天丁香| 国产激情在线| 亚洲小视频免费播放| 亚洲顶级VA在线观看-高清完整版在线影院观看-S022AV | 色婷婷五月天天天天天| 亚洲色色图片| 亚洲色五月天在线| 99热无码| 伊人激情综合网| 思思热在线精品视频| 激情五月亚洲| 色婷婷亚洲五月天| 欧州色色| 丁香婷婷基地| 河北真实伦对白精彩脏话| 色婷婷激情| 久久激情五月婷婷| 懂色av粉嫩av蜜臀av| 在线观看免费观看在线9久| 色伦专区97中文字幕| 色婷婷六月| 裸体做A爰片毛片A片免费| 白人荫道BBWBBB大荫道| chaopeng在线人人| 超碰九九热| 五月丁香六月婷婷精品| 丁香五月激情婷婷| 91婷婷五月天嫩女| 天天做天天爱天天高潮| 91久久国产自产拍夜夜91久久精品文字>91麻豆精品国产 | 手机旧版看人妻1025| 亚洲成人无码网站| 色婷婷丁香五月天在线视频| 老司机伊人| 日日撸夜夜操| CHINESE熟女老女人HD视频| 99无码| 九九综合| 99人妻碰碰碰久久久久禁片| 激情综合网 激情五月天| 日日夜夜婷婷| 99综合视频一体| 9热网站| 丁香五月色激情| 99久在线精品99re5热视频| 14色综合婷婷| 五月丁香啪啪啪啪| 婷五月天| 久久久人妻不卡| 天天激情视频| 伊人久久艹| 九九热精品| 六月婷婷青青青视频| 欧美97色| 丁香五月天啪啪| 超碰免费在线| 啪色综合| 五月天停停成人网| 一点色成人网| 99久久综合| 这里只有精品热| 99在线视频在线观看| 五月婷视频在线| 久久久97| 久99久视频| 色久免费| 无码激情AAAAA片-区区| 丁香5月激情网| 色五月女| 色色9 9| 色噜噜狠狠色综| 五月丁香婷在线| 激情五月天电影| 日本久久精品| 久久久人妻不卡| 日日夜夜久| 婷婷五月成人系列| WWW嗯嗯啊啊啊啊| 日夜操B| 激情五月婷婷丁香综合网| 九九伊人网| 国内9l视频自拍老熟女九色| 欧美色狠婷久| 色屌丝中文字幕| 六月婷婷天堂| 日韩在线aaa| 国产乱子轮XXX农村| 免费国产VA国产免费| 日本九九视频| 色色色色五月天| 操一操干一干| 色婷婷综合电影| 久久婷婷五月综合色播| 国产成人精品一区二三区熟女在线 | 亚洲性爱99| 成人五月天视频| ,99视频久久| www.色色五月天.com| 9999热免费视频视频| 五月婷婷狠天天色综合| 欧美大肥婆大肥BBBBB| av线电影| 色婷精品91| 综合九九久久| 思思热高清在线观看| 中文字幕日产A片在线看| 五月天丁香六月综合| 中文字幕av久久爽一区| 五月天激情网址| 成人 AV播放| 久久色六月| 激情丁香淫荡婷婷| 碰久久精品w| 六月丁香色婷婷| 丁香五月色激情| 久久欧洲综合网| 激情婷| 亚洲无码yw| 98色丁香五月婷婷综合网| 色五月激情婷婷| 超碰99热精品| 九九色热| 国产精品久久久海的味道| 99久久精品国产色欲| 色色丁香五月天| 丁香婷婷六月天| 99小视频| 超碰在线50| 五月丁综合在线观看| 小香蕉av| 伊人9草在线观看| 久久与婷婷| 婷婷综合欧美| 色婷婷啪啪| 9久久久久久久久久久| 99热99成人| 激情五月婷婷| 伦乱天堂| 六月丁香六月婷婷欧美| 99久久a线观| 久久九网| 丁香六月激情| 精品久热69| 五月丁香六月婷婷精品| 五月丁香综合影院| 91夫妻视频| 91.com男女操| 久久精品66| 婷婷97碰碰| 久久精品国产AV一区二区三区 | 色婷六月| 天天爽免费视频| 亚洲AV综合在线观看| 丁香五月天堂网| 99热这里全是精品| 丁香五月婷婷呀| 韩国19 主播内部福利vip免费播放| 激情五月小说婷婷| 超碰人人操人人干| 天天日天天插| 色香欲综合| 99久久国产宗和精品1上映| 天天日P天天射P| 久久久久人妻网址| 成人av在线网址| 欧美亚洲操逼| 五月丁香婷婷成人伊人网| 91视频五月丁香| 少妇高潮呻吟A片免费看软件 | 啪啪啪丁香五月| 中文AV网站| 色色色色色网站| 丁香五月成人论坛| 思思久久精品视频| 综合狠狠伊人| 久久免费9| 97sese婷婷| 国产精品久久久久久久久久免费| 狠狠操狠狠| 日本狠狠干| 99.色| 中文网AV| 激情小说五月丁香在线视频观看视频| 影音先锋一区二区三区| 97碰久久| 丁香丁婷五月激情| 天天插天天插天天日| 97色图片中文字幕视频在线观看 | 丁香六月婷婷综合缴| 色 五月婷婷基地| 亚洲视频色色| 激情五月天网页| 男人天堂伊人五月丁香| 丁香五月天色| 狠狠狠人妻| 五月婷六月综合在线观看| 91精品国产91久久久久青草| WWW,五月天| 97干欧美| 五月丁香六月婷婷,婷| 五月丁香啪啪啪| 五月婷婷啪啪啪| 五月婷婷偷拍| 91久久99久久91熟女精品| 婷婷五月电影院| www.五月天色色.com| 亚洲成人av在线播放| 五月天婷婷色色首页| 婷婷六月久久| 丁香婷婷基地| 婷婷丁香六月激情综合| 激情小说婷婷五月| 五月婷婷六月丁香| 日日影院 | 精品亚洲国产成AV人片传媒| 第1影院之五月婷婷| 九草性爱| 综合五月丁香久久| 韩国三级五月天婷婷。| www.91九色| 99久在线精品99re8| 久久婷婷视频| 久久久国产精品黄毛片| 玖色色综合| 六月婷婷九月丁香| 99热在线99| 婷婷激情五月天亚洲综合| 五月激情五月婷婷五月天在线| 婷婷色欧美激情| 久色婷婷200| 国产午夜精品一区二区| 激情综合五月| 丁香五婷| 婷婷舔| a久久免费视频| 1024操逼视频| 无码91中文字幕| 3DAV亚洲香蕉久久 一区二区| 亚洲AV无码成人精品区电影网| 99热这里只有精品1025| 久久综合天天综合| 五月天色婷婷综合| 性爱五月婷婷| 狠狠狠狠狠狠狠狠草| 国产色色色色| 99网| AV操逼网| 色婷婷亚洲五月天| 99视频在线观看地址| 97在线日本| 天天爽夜夜爽夜夜爽精品| 人人爽人人爽人人爽人人爽| 久久久久久综合88| 国产永久一黄| 开心五月婷婷激情网| 99无码| 欧美交换配乱吟粗大25P| 性爱五月婷| 丁香五月婷婷五月天在线| 操射国产日本| 亚洲V国产V欧美V久久久久久| 在线99色| 五月亭亭六月天| 日日操夜夜爽| 26uuu丁香婷婷五月| 婷婷综合五月激情| 天天日天天干天天操| 亚洲国产黄色电影| 中文字幕丁香五月| 天天拍天天操| 狠狠香蕉| 嫩草视频在线观看| 久久天堂| 激情开心五月天| 国产成人综合电影| 天天摸夜夜爽天天做| 亚洲天堂啪啪| 4399高清无码视频| 无码 色| 成人无码髙潮喷水A片| 91九色欧美| 淫荡工a| 五月天综合在线| 国产精品色婷婷99久久精品| 色五月婷婷基地| 婷婷激情久久| 五月丁香婷在线| 色婷婷综合网| 99热香港| 少妇AB又爽又紧无码网站| 久久99操| 黄久久久| 色久综合| 亚州操操| AV在线免费网站| 91久久免费| 夜夜躁爽日| 超碰人人在线观看| 国产VA亚洲VA96| 五月婷婷av在线| 五月激情婷婷六月丁香| 69超碰在线| 情情五月天色| 亚洲丁香婷婷| 五月激情小说网| 欧美日韩色色| 成人va视频| 国产.亚洲.欧洲视频在线| 这里只有精品1| 日本97在线看片| 狠狠操狠狠做| 九色无码| 成人丁香| 日撸夜撸日操| se99热久久一本| 色噜噜狠噜噜视频| 国产精品成人在线| 免费看欧美成人A片无码| 日日夜夜狠狠婷婷色| 天天爽天天日| 无码婷婷五月天| av操B网站| 激情五月,激情综合网| 婷婷五月激情视频网| 26uuu在线观看| 91大屁股精品| 丰满老熟妇BBBBB搡BBB| 丁香五月亚洲| 婷婷永久在线| 色五月婷婷成人| 99精品小视频| www.粉嫩av.com| 色情五月综合婷婷| 亚洲Av入口| 五月丁香婷婷激情爱爱| 97se视频在线| 五月天伊人av| av免费在线观看0| 婷婷久久色| 99色日本| 色五月综合网| 精品无码色| 91人妻人人操| 91精品无码| 欧美狠狠草| 综合色在线| 色色色免费视频| 任你擦免费视频| 另类视频五月天| 亚洲综合婷婷六月丁香五月| 丁香 久久| 99视频内射三四| 五月婷婷就去色| 91热久| 色婷婷六月天| 五月丁香亚洲五月| 91综合网| 少妇性按摩无码中文A片| 激情五月视频| 亚洲在线操| 不卡的AV网站| 爱操人妻| 深爱激清网| 殴美日比视频| 亚洲成Av人片乱码色第1集| 日本三级大片| 色五月婷婷亚洲| 国产xxxxx在线观看| 丁香五月天AV在线 | 久久女人天堂| 26uuu亚洲欧美| 久久久久久久8| 91精品激情9| 中文久久婷婷| 亚洲噜色| 丁香婷婷五月天校园春色| 青青草Avb在线| 91天天操天天干天天射| 久久这里有| 天天爽在线视频| 色综合色婷色基地| 老师高潮流白浆喷水的A片| 色婷婷19| 啪啪黄页网| 婷婷色色播五月天| 五月开心婷婷极品激情| 99色日本| 激情图片久久| 99视频日韩| 五月婷婷丁香六月| 久99久热| 色热久| 丁香五月六月欧美| 五月开心六月婷婷在线播放网站| 天天综合网、天天综合色| 99热99日天天干| 超碰免费人人| 人妻啪啪啪| 久久久com| 丁香五月成人社区| 免费视频这里只有精品| 五月天婷婷永久免费视频| 色综久久久| 亚洲综合视频网| 久久久久久18| 亚洲网综合在线| 激情五月综合网| 日韩人妻在线观看| 五月天丁香成人| 欧美日本黄色| 久久东京热婷婷五月| 天天做好综合色| 五月丁香六月欧美| 天天插天天插天天插天天插| 激情五月天电影| 亚洲综合色五月| 99国产小视频| 五月婷婷啪啪啪啪| 五月情色天| 国产婷婷五月中文字幕高清| 亚洲麻豆乱码国产2028| 久热精品视频| 婷婷五月天六点丁香五月| 草逼大片| 色噜噜狠噜噜视频| 婷婷情色五月天| 91丨九色丨高潮丰满日本| 天天弄天天操| 热久久91| 婷婷五月天色色| 全部老头和老太XXXXX| 五月丁香激情综合网| 五月天婷婷久久| 亚洲五月婷| 六月丁香啪啪| 激情五月婷婷视频| 亚州婷婷五月激情综合| 成人视频一区| 五月开心六月婷婷在线播放网站| 热99re| 色亭亭五月天丁香综合AV - 百度 - 百度| 久久金品黃色| 婷婷色综合| 香蕉综合网| 99riAV成人在线视频| 国产99久9在线+|+传媒| 五月天婷婷爱| 森林影视大全,最好看的2019年视频 | 热婷婷av| 婷婷六月视频| 色婷婷色五月丁香| 五月丁香五月综合欧美| 婷婷综合网| 最新国产AV| 干一干xxxx| 丁香色五月直播| 婷婷五月天激情影片| 五月婷婷第四色| 99久久玖玖| 一本道在线电影| 黄色av高清| 亚洲成人av在线播放| 9l视频自拍九色9l视频自拍九色9l社区| 26uuu.| 婷婷十月激情综合网| A片一曲| 射婷婷中文字幕| 91丨九色丨老农村| 岛国av网| 99热这里只有精品1025| 日韩久久日| 2017人人操| 综合久久丁香婷婷,五月婷婷六月丁香,开心激情综合网,六月丁香在线观看,婷婷丁 | 婷婷天堂站| 成人精品视频99在线观看免费| 丁香六月婷婷开心婷婷网| 91Chinese在线| 深爱五月日韩| 成人做爰高潮A片免费视频| 综合精品99| 激情五月天久久丁香| 色五月视频,小说| 99在线观看精品视频| 九九精品丁香花| 婷婷激情伍月网| 亚洲成人电影在线免费观看| 婷婷亚洲日本| 婷婷的色色五月天| 97luluse| 欧美激情丁香五月天久久婷婷一区| 五月六月伦理| 激情五月丁香五月色| www.久久| 婷婷五月电影院| 久草婷妨| 99热在线成人网站| 丁香性爱在线视频| va婷婷在线| 99久久网站| 久久免费丁香| 精品久久艹| 婷久久久| 日日操,夜夜撸| 99精品国产在热久久| 人人操91色| 久久一级片| 丁香五月激情鲁| 六月婷婷五月丁香| 久久丁香综合香蕉| 无码少妇高潮喷水A片免费| 国产亚洲精品久久久久久豆腐| 日本久久网| 琪琪布丁香社区激情五月天| 天天操天天插天天射| 丁香五月综合激情久久潮喷| 婷婷97碰碰| 婷婷五月天av| 五月色 亚洲| 五月婷婷影| 开心五月婷婷| 色噜噜狠狠色综合日日| 国产成人网站在线观看| 欧美A片在线视频免费观看| 久久一级免费黄色片| 久草五月天| 丁香五月六月久久综合| 激情五月,深深爱五月| 久久视频这里有精品99| 色色色图| 五月天丁香婷婷视频网址 | 婷婷九月丁香| 天天舔天天| www.97碰碰com| 精品五月视频婷婷在线观看| 翔田千里无码| 操操天堂| 无码视频国内精品久久久| 激情五月天色色色| 99热思思在线观看| 天天操天天操天天操天天操天天操天天操天天操天天操天天操 | 亚洲综合婷婷| 在线另类视频| 黄色av网站在线免费播放| 国产精品丝| 色婷婷丁香六月| 色色综合网络| 做爰丰满少妇1313| 91在线视频综合| 热热久久精品视频| 91久久精品无码一区二区三区| 六月婷婷综合| 超色欲天天| 亚洲色情一区二区三区四区| 极品另类| 成人综合视频网址| 久久久色婷婷五月天| 婷婷丁香五月综合网上| 97色色视频| 99视频在线观看视频| 激情 五月 婷婷 丁香| 婷婷五月天综合小说网| 免费视频无码| 五月丁香啪啪婷婷| 天天爽综合网| 亚洲黄网AV| 五月丁香福利| 五月天色官网| 天天插天天射| 色播五月天婷婷老师| 久久国产性爱A V| 激情99| 日韩综合成人| 67194国产| 欧美日韩aaa| 日日操夜夜擼| 狠狠色综合777| 亚洲AV人人操| 欧美久人人| 激情涩播| 五月天·www·com| 色墦五月丁香| 五月丁香无码| 九九热最新| 天天日色情| 开心激情色婷婷五月天| www.久久| 99热主页日本| 久久综合干| 九色PORNY在线精品酒店| www.minyis.com【JT】币址百万U预算可预付QQ2101460746 | 日韩无码专区| 熟女色专区| 色婷婷五月综合| 9月色婷婷| 亚洲热久久| 一本狠婷婷综合| 久久这里99| 无码AV免费精品一区二区三区| 快乐婷婷五月天| 伊人久久婷婷| 婷婷五月花| 综合色播| 欧美熟女99| 五月婷婷婷婷| 色婷婷影视| 久久综合激情| 天天玩夜夜操天天爽| 久久精品视频99| 激情视频91| 内射综合网| 1024手机在线观看看片_日韩精品| 天天色天天日| WWW99热| 91.com男女操| 日本操碰碰| 丁香六月婷婷| 五月丁香在线视频观看| 99国产精品久久久久久久久久久| 激情图片婷婷| 六月婷综合| 夜夜夜夜夜骑撸| ady狠狠入| 丁香五月天资源网| 日韩亚洲视频| 五月色网| 五月婷婷中文网| 色综啪啪| 亚洲碰碰碰| 色色色色色色网站| 日日夜夜狠狠婷婷色| www超碰| 涩涩婷婷五月| 秋霞少妇毛片| 五月丁香婷婷综合久久| 99精品视频在线6| 91打屁股免费看| 噜噜五月天综合| 五月婷婷成人| 青青草五月天| 激情久久综合网| 综合玖玖偷拍| 色综合激情| 亚洲男人的天堂婷婷色五月| 九九精品系列| 97色在线| 五月婷婷无码专区| 性爱激情小说AV五月丁香花| 六月激情网| 欧美婷| 色婷婷久久7777| 综合狠狠干| 婷婷五月激情四月综合| 人妻Av在线| 99久.| 午夜九九电影| 欧美性猛交99久久久99| 我爱大香蕉| 色五月五月丁香| 色五月婷婷激情基地| 色99网| 狠色色狠网| 8区视频在线| 538在线精品| www.夜夜操| 狠狠看狠狠| 久99久视频精选| 综合激情站| 婷婷五月中文字幕国产| 婷婷伊人綜合中文字幕| 潘金莲AAAAAAAAAA| 婷婷五月丁香五月| 五月丁香在线偷拍视频| 99操无码视频观看| 婷婷五月精品| 99视频这里有精品| 91婷婷五月天嫩女| 99精品视频在线| 日韩 中文 欧美| 在线精品97| 五月天激情小说| 99热这里只有精品最新网址| 激情婷婷五月亚洲| 亚洲色图81p| 色五月婷婷777| 超碰成人在线观看| 色插人人| 99色| 婷婷性爱影院| 婷婷五月天av| 久久久久99精品成人片| 婷婷五月天丁香| 天天插天天射天天干| 天久综合91综合首页| 九九大香蕉黄色影院| 九九热这里只有精品9| 天天色播| 大香蕉中文| 玖玖婷婷五月天毛片| 91偷拍视频| 丁香五月婷婷五月天| 九九视频精品在线免费| 五月丁香综合网色欲| 日本久久超碰| 五月天操逼激情| 亚洲V国产V欧美V久久久久久| 91人人网| 亚洲六月色婷婷| 六月丁香VA| 人人草成人视频| 97在线视频人妻九色| 性爱激情小说AV五月丁香花| 伊人大香久久| 婷婷丁香18| 日韩精品AV一区二区三区| 五月天精品| 九九香蕉网| 99操碰| WWW.17C亚洲精品| 激情二色月| 天天做天天要天天爽| www久久久| 婷婷综合五月| 香蕉综合在线| 182TV大香蕉| 五月婷婷黄色毛片| 99干在线| 91视频免费后入强操| 大香蕉在线观看9| 天堂久久大香蕉| 伊人成综合五月婷婷| 五月丁香久久久| 超碰国产一区| 欧美五月婷婷综合| 一区二区传媒视频| 日本情色一区二区| 九九精品碰| 99超碰人人| 无码A片一区二区免费| 欧美在线视频免费播放| 婷婷va| 日噜噜色| 色婷婷超碰| 99噜噜| 综合激情伊人影视在线| 成人五月天综合网| 激情内射人妻1区2区3区| 青青青在线视频国产| 国产在线aaa片一区二区99| 粉嫩AV久久一区二区三区| AA片在线观看视频在线播放| av婷婷六月丁香社区在线观看| 丁香婷婷AV| 激情影院69| 久久婷婷丁香视频网| 怡春院久操| 丁香五月婷婷深五月| 五月伊人综合| 久久九九在线视频| 五月天成人网在线观看| 久久五月天色婷婷| 日日夜夜干| 99年操人人爽| 99久久精品亚洲综合| 日本97在线视频| 伊人色综合影院视频| 日本99视频| 色播五月天天| 五月天四色房丁香亭亭| 久草视频一,二三四| 日本三级日本三级99| 国产看真人毛片爱做A片| 天天爱天天操| 色综啪啪| 任你擦免费视频| 99极品视频| 亚洲性色XXXXX| 182TV大香蕉| 天天爱天天做天天操| 婷婷爱五月| wuyuedingxiang| 99久超碰| 亚洲久久婷婷丁香五月天| 午夜五月天| 久久怕怕视频| 伊人九九68| 色玖玖综合网| 777丁香六月青青草婷婷综合久月| 天天操天天爱天天玩| 99久久99九九九99九他书对| 熟女人妻一区二区三区免费看| 欧美成人日韩| 樱花99视频| 日韩aaaaa| 午夜一区| 操日本三片99| 丁香六月中文| 九九热精品视频| 日产精品一线二线三线芒果| 五月伊人婷婷999| 同性gv国产精品一区二区| 综合色色色色色色| 色情婷婷| 亚洲AV无码成人精品区电影网| www..com色爱| 欧美天堂久久| 天天久综合网永久入口18| 六月婷婷综合| 久久久久久久合一狠狠做深爱| 天天色粽合合合合合合合| 丁香99| 狠狠干总合| 亚洲无码11| 亚洲五月天婷婷综合| 超碰人人操人人干| 日本精品人妻无码77777| 激情六月丁香| 丁香五月天AV| 黄色视频网站在线播放| 日本超碰在线| 噜噜狠狠色综合久| 日韩青青| 欧美久久五月婷婷| 久9久9久9久9久9久9| 日韩三级视频一区二区| 五月婷婷中字在线| 婷婷天堂综合| 91九色超碰| 婷婷色播色五月五色五月天色妇| 狠狠色噜噜| 无码成人AAAAA毛片AI换脸| 99在线69| 琪琪色五月天| 五月婷久久综合| 五月婷久久在线| 狠狠爱综合| 超碰在线成人| 在线视频99| 伊人大香蕉在线视频| 欧美日本一区二区三区| 色五月激情五月| 26UUU| 九九精品免费| 中文婷婷狠狠| www.五月天。com| 欧美日韩精品人妻狠狠躁免费视频| 国产精品久久..4399| 五月丁香六月激情综合| 久久九九re热| 操骚货在线| 亚洲色区17| 久久视频在线| 色狠狠色综合久久久绯色aⅴ影视| 秋霞学生妹一二级| 五月丁香啪啪| 五月丁香激情综合啪| 久久久网站| 99热啪啪| 日韩操人| 性爱先锋AV| 丁香五月天啪啪| 五月丁香| 老司机日日夜夜青草| 婷婷天堂视频| 另类激情五月| 五月天婷婷操逼视频| 久久久ww| AV在线大香蕉| 99热国产这里只有精品| 婷婷综合五月天| 草草视频91| www久| 久久久久人妻| 丁六月激情| 丁香五月天大香蕉啪啪| Www.婷婷五月| 99re免费视频| 五月丁香综合在线| 五月婷啪啪| 超碰人人操人人干| 五月天色婷婷激情综合| 99无码视频| 伊人婷婷五月天av| 99艹精品在线观看| 182TV大香蕉| 六月丁香久久| 欧美成人无码一区二区三区| 99久久久99久久91熟女| 婷婷涩五月天综合| 丁香五月天日韩无码| 99爱爱网| 超碰在线成人| 国产亚洲成AV人片在线观黄桃| 深爱婷婷色| 丁香婷婷五月天成人| 人妻精品久久久久久| 亚洲成片在线观看| 久久综合五月天| site:pzdcoin.com| 国产成人精品一区二三区熟女在线| 丁香五月av在线| 丁香五月九九| 色色色热热热| 很很干在线视频| 久久丁香婷| EEUSS鲁片一区二区三区| 99视频日韩| 99riAv1国产在线观看| 99热这里只有精品亚洲| 五月婷婷香蕉| 精品久久人妻| 啪啪91| 国产人妻777人伦精品HD| 99久久大片| www.91在线观看| 久久人妻视频| 深爱五月最新网址| WWW.久久久久久久| 婷色五月天| 五月天婷久精视频| 99色在线| 久久久五月天婷婷成人网| 五月天精品视频| 欧美99热| 久爱综合| 五月丁香欧美综合| 色亭亭五月天网扯| av在线中文| 久久在线视频免费观看| 色99热| 91dy.av| 欧美日韩国产伦精品日韩人妻一| 99久久这里只有精品| 五月天婷婷激情在线色图| 99精品国产在热久久| 婷婷五月综合网激情| 九九色大香蕉| 色婷婷色情| 超碰免费大香蕉| 亚洲熟妇AV乱码在线观看| 婷婷五月激情中文字幕| 综合五月亭亭9| 五月丁香久久婷| 99精品国产在热久久| 亚洲区视频| 婷婷五月天激情五月天| 色波激情五月天| 色情五月| 少妇人妻人伦A片| 日日干夜夜干| 久久婷婷激情五月天一区二区| 墨西哥毛片内射精| 国产又爽又猛又粗的视频A片| 婷婷五月激情的图片| 99这里只有精彩视频| 大香蕉人妻| 色色综合院| 中文字幕欧美日韩VA免费视频| 91婷婷丁香五月| 操你av| 丁香五月综合色婷婷| 爱iii做iiii日日| 99热精品在线| 九九这里是免费的视频5| 丁香五月激情鲁| 五月婷色丁香| 色色操| 激情综合99| 韩日AV片| 中文字幕网伦射乱中文| 婷婷色在线| 五月婷婷啪啪| 超碰男人色| 色婷婷亚洲婷婷| 亚洲综合婷婷六月丁香五月| 久久aaa| 国产亚洲在线观看| 99热自拍| 色娸娸综合网| 色综合久久综合| h在线看免费版在线看| 色丁香五月婷婷| 色色色干| 婷婷久久丁香五月| 亚洲精品永久久久久久| 婷婷五月天欧美图片在线播放电驴| 五月欧美色色五月| 丁香五月婷婷动漫| 色播五月天天| 欧美成人Va| 亚洲sesesese| 99超级碰碰| 色五月天丁香婷婷色| 久久ri精品视频| 狠狠色丁香乆乆| www.金莲av| 日韩AV在线影片| 日韩在线看AV| 国产精品久久久久9999小说| 国产亚洲精品久久久久久郑州 | 五月丁香六月日逼| 五月天无码| 电影爱拉战争免费观看| 色,激情五月天| 五月丁香六月婷婷,婷| 婷婷综合| 思思热这里只有精品视频666| 激情婷婷丁香五月天| 中文字幕永久在线| 综合久久高清| 五月天婷婷免费| 成人午夜无码视频| 亚洲精品亚洲人成人网| 久久66er久久| 伊人青草成人| 丁香婷婷免费| 色婷婷狠狠18禁| 麻豆科斗777| 欧洲亚洲免费视频区| 99er6| 久色国产| 操逼123网| 伊人五月天在线| 激情五月天综合网| 操操碰| 五月丁香色综合| 色婷婷电影网| 成人无码精品1区2区3区免费看| 亚洲国产精品二二三三区| 亚洲精品a成人在线播放| 久草 tingting| 久久久五月天网站| 91狠狠综合久久久| 午夜丁香六月婷| 婷婷激情网五月天| 欧美激情综合色综合啪啪五月| 69久久99精品久久久久婷婷| 久久色婷婷| 成人精品在线| 亚洲精品又粗又大又爽A片| 婷婷五月在线影院| 激情婷婷综合网| 成人视频一区| 人人操Av| 97韩国久久电影院| 91综合色| 亚洲va成人va成人va在线观看| 国产成人精品一区二三区熟女在线| 97干干干丁香| 日本99在线| 久思思热视频在线观看| 五月六月激情| 色色五月丁香婷婷| 亚洲综合五月天婷婷丁香| 色色综合五月| 久久99久久99精品免观看粉嫩| 综合激情五月综合激情五月激情1| 色婷婷在线视频综合| 丁香五月天堂网| 九九AV| 精品夜夜澡人妻无码AV| 婷婷五月另类网站| 五月花激情网| 97色在线观看视频| 婷婷五月天激情小说网站| 五月天啪啪啪| 北京熟妇搡BBBB搡BBBB| 国产黄色大片| 丁香激惜男女| 亚洲色频| 国产精品色色| 五月亭亭欧美女人| 色五月丁香总合网| 天天做天天爽| www.狠狠| www,色综合| 极品 少妇 内射| 99热这里只有精品国产精品| 国产精品久久..4399| 婷婷成人av| 欧美99热| 成人超碰Av| 五月天婷婷小说| 丁香五月天婷婷91| 热热久久精品视频| 日韩aaaaa| 婷婷99视频在线| 99re思思热在线视频| 第四色婷婷丁香五月| 成人做爰A片免费看网站找不到了 噼里啪啦在线观看免费完整版视频 | 婷婷五月丁香六月综合网| av大香蕉| 97碰碰在线观看视频| 国产日日操夜夜操的肉棒视频| 激情综合激情五月一起草| 96丁香六月婷婷蜜桃综合久久| 97香蕉人人在线观看| 【乱子伦】黄色| 九九色图| 六月丁香婷婷爱| 九九蜜臀精品| 激情综合网色播五月| 深爱激情综合| 日本不卡高字幕在线2019| 精品操逼一区二区| 日本美女天天日天天爽| 99热热这里只精品996小说| 婷婷丁香大香蕉| 99视频色在线观看| 色欲午夜无码久久久久久张津瑜 | 久久婷狠狠色| 五月丁香偷拍| 99色色最新视频| 99热6精品| 黄网免费观看| 婷婷深爱五月| 丁香五月婷婷少妇| 丁香六月婷婷综合色| 五月大香蕉| 51XX午夜影福利| 人人摸人人| 久久丁香网| 色婷婷亚洲在线| 99久久9| www.射伊蕉婷婷| 丁香婷婷六月天| 久久久GOGO无码啪啪艺术| 激情五月天在线观看色婷婷| 99精品国产在热久久婷婷| 色婷婷香蕉丁丁网| 色五月在线播放| 久草x色在线观看99| 四色 爱 婷婷 精品 亚洲 五月天| 色播播五月天| 成人网站在线观看视频| 久久婷婷综合网| 婷婷爱五月| 9九色首页| 9l视频自拍9l视频自拍九色学生| 亚洲综合激情五月| 国产97在线日韩亚洲女人被黑人巨大| 综合视频五月| 激情又色又爽又黄的A片| 色五月天中文字幕| 久久九色| 成人五月天在线观看| 五月天丁香| 五月丁香六月婷婷,婷| 热久精品| 色日本丁香婷婷| 天天干,天天日| 狠狠色情婷婷| 国产成人精品一区二区三区视频| 五月天激日本色情在线| 五月婷婷综合网| 天天操天天日天天爽| 午夜天堂一区人妻| 五月天激情无码高清| 青青操成人福利| 久久这里有精品视频| 精品九九久久| 色婷婷狠狠| 婷婷色偷拍| 狠狠色综合网| 热99这里只是精品| 综合网亚洲| 亚洲熟妇AV综合网五月丁香伊人| 四川BBB搡BBB爽爽视频| 激情久久五月网| 日韩精品呦呦va| 婷婷娌伦网| 国产在线黄色| 丁香五月婷久久| 五月综亚洲| www.色色色com| 激情婷婷六月天| 久久看婷婷| 婷婷五月激情网| 亚洲色婷婷99一9|| 九九精品丁香花| www.minyis.com【JT】实力收量可预付QQ2101460746 | 99在线观看精品视频| 五月色婷婷亚洲| 天天色中文字幕女优AV| 五月天播播| 久久激情五月婷婷| 91久久久久久| 欧美成人精品老美女噜噜噜| 色婷婷操逼网| 97色在线视频| 综合激情五月丁香| 亚洲综合视频八| 伊人婷婷综合| 婷婷丁香五月激情图片| 97搞在线| 色婷婷五月在线| 插插网爽妇五月丁香| 婷婷六月丁香五月图区| 亚洲va欧美va天堂v国产综合| 日本99热| 九月婷婷综合| 丁香六月情| 日日操夜夜操无码免费| 久狠狠| 六月婷婷狠狠色在线观看| 精品综合爱| 天堂成人A片永久免费网站| 色婷婷AV久久久久久久| 99久久综合| 禁片二区|