<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      瀏覽器插件功能實現-抓取dom/抓取瀏覽器響應/處理流式響應

      一、需求背景

      實現一個類似于monica的智能體,要求在注入頁面內,當檢測到用戶點擊制定區域后,抓取dom內容,詢問智能體,流式返回內容

      二、實現方案

      1.抓取dom元素

      最開始的構想是直接通過click事件,返回點擊的dom,這里碰到了第一個問題點:用戶點擊的區域是一個iframe,對于iframe,需要考慮是否同源,如果是同源則可以通過contentWindow?.document來獲取。

      直接上代碼

       1  useEffect(() => {
       2     const observer = new MutationObserver(() => {
       3       const iframe = document.querySelector(
       4         "iframe#amzv-web"
       5       ) as HTMLIFrameElement | null;
       6 
       7       if (iframe) {
       8         // 等待 iframe 加載完成
       9         iframe.onload = () => {
      10           const iframeDoc = iframe.contentWindow?.document;
      11 
      12           if (iframeDoc) {
      13             // 綁定事件監聽器只在第一次
      14             if (!(iframe as CustomHTMLIFrameElement)._clickEventBound) {
      15               iframeDoc.addEventListener(
      16                 "click",
      17                 (event) => {
      18                   const target = event.target as HTMLElement;
      19                   console.log("Target:", target);
      20 
      21                   const elements = iframeDoc.querySelectorAll(
      22                     ".right-box>.item>.label"
      23                   ) as NodeListOf<HTMLElement>;
      24 
      25                   console.log("Elements:", elements);
      26 
      27                   const matchedElement = Array.from(elements).find(
      28                     (el) => el.textContent?.trim() === "工單編號"
      29                   );
      30 
      31                   if (matchedElement) {
      32                     const nextSibling =
      33                       matchedElement.nextElementSibling as HTMLElement | null;
      34                     const value =
      35                       nextSibling
      36                         ?.querySelector(".gb-text-ellipsis")
      37                         ?.textContent?.trim() || "";
      38                     console.log("Work Order Value:", value);
      39                   }
      40                 },
      41                 true
      42               );
      43 
      44               // 標記事件已綁定
      45               (iframe as CustomHTMLIFrameElement)._clickEventBound = true;
      46             }
      47           }
      48         };
      49       }
      50     });
      51 
      52     observer.observe(document.body, { childList: true, subtree: true });
      53 
      54     return () => observer.disconnect(); // Clean up observer on unmount
      55   }, []);

      這里目前有個問題,第一次獲取dom后的點擊沒法獲取數據,這里通過定時器延時獲取,我當前dom結構的查詢。

      export function getValueByLabelFromIframe(
          iframeDoc: Document,
          labelText: string,
          timeout = 1500
      ): Promise<string | null> {
          return new Promise((resolve) => {
              const tryFind = () => {
                  const labelElements = iframeDoc.querySelectorAll(".right-box > .item > .label");
                  const matched = Array.from(labelElements).find(
                      (el) => el.textContent?.trim() === labelText
                  );
      
                  if (matched) {
                      const value =
                          matched.nextElementSibling
                              ?.querySelector(".gb-text-ellipsis")
                              ?.textContent?.trim() || null;
                      resolve(value);
                      return true;
                  }
                  return false;
              };
      
              if (tryFind()) return;
      
              const interval = setInterval(() => {
                  if (tryFind()) clearInterval(interval);
              }, 100);
      
              setTimeout(() => {
                  clearInterval(interval);
                  resolve(null);
              }, timeout);
          });
      }

       

      2.通過抓取接口數據

      另一種實現思路是通過抓取接口數據,觀察到在某個接口的response內

      chrome本身的能力webRequest可以抓取的信息有限,并沒有response的內容。當然如果能滿足你的需求只使用它能解決是最好的。

      這里采取了動態腳本注入的方式,world: 'MAIN' 非常關鍵的一個配置項,使用后可以讓注入的content-script和宿主網頁擁有相同的上下文,就可以實現XMR拓展。

      代碼如下

      //background.ts
      chrome.action.onClicked.addListener(function (tab) {
        chrome.scripting.executeScript({
          target: { tabId: tab.id as number },
          files: ["inject.js"],
          world: 'MAIN'
        });
      });
      
      // // inject.js 放在public文件目錄下
      (function (xhr) {
        if (XMLHttpRequest.prototype.sayMyName) return;
        console.log("%c>>>>> replace XMLHttpRequest", "color:yellow;background:red");
      
        var XHR = XMLHttpRequest.prototype;
        XHR.sayMyName = "aqinogbei";
      
        // 記錄原始的 open 和 send 方法
        var open = XHR.open;
        var send = XHR.send;
      
        XHR.open = function (method, url) {
          this._method = method; // 記錄method和url
          this._url = url;
          return open.apply(this, arguments);
        };
      
        XHR.send = function () {
          console.log("send", this._method, this._url);
          this.addEventListener("load", function (event) {
            console.log('XHR response received:', event.target.responseText); // 捕獲響應文本
          });
          return send.apply(this, arguments);
        };
        
      })(XMLHttpRequest);
      
      // 捕獲所有的 fetch 請求
      (function () {
        const originalFetch = window.fetch;
        window.fetch = function (url, options) {
          console.log("fetch request:", url, options);
          return originalFetch(url, options)
            .then(response => {
              response.clone().text().then(body => {
                console.log('fetch response:', body); // 打印響應內容
              });
              return response;
            });
        };
      })();
      //manifest.json配置
      "web_accessible_resources": [
      {
      "resources": ["icons/*", "iconfont/*","inject.js"],
      "matches": ["<all_urls>"]
      }
      ],
       

       看似解決了,但是還有個問題,這個腳本是被注入到網頁內的,不是iframe,iframe內的請求還是拿不到。

      做到這里的時候,回頭又采用了dom抓取的方案,當然順著這個方向繼續排查應該可以有解決方式。

      參考:https://segmentfault.com/a/1190000045278358

       

       3.接口請求

      接口請求參考https://juejin.cn/post/7396933333493170228

      我們的接口請求一定會跨域,所有的fetch行為必須要放在background.ts內處理,然后把響應交給content_script.js

       

       


      ## 4.30更新

      后續就是一些常規的流式數據處理,沒什么難點。

      需求要求實現點擊切換其他卡片的時候中止當前stream,直接AbortController解決。同時封裝請求,改造成單例。

      實現打字特效還是用的每次更新dom,暫時沒想出更好的優化點。

       ## 5.13更新

      關于數據處理,目前把sendmessage方案放棄了,對于sse,使用port更方便且穩定

      // portClient.ts
      let portInstance: chrome.runtime.Port | null = null;
      
      export const getPort = (portName = "stream-channel") => {
        if (!portInstance) {
          portInstance = chrome.runtime.connect({ name: portName });
        }
        return portInstance;
      };

      通過port發送消息

      port.postMessage(message);

       sendMessage使用例

      (async () => {
        // 使用 sendMessage 從 Content 發送消息
        const response = await chrome.runtime.sendMessage({greeting: "hello"});
        console.log(response);
      
        // 使用 onMessage.addListener Content 接收消息
        chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
          console.log(sender.tab ? "from a content script:" + sender.tab.url : "from the extension");
          if (request.greeting === "hello") sendResponse({farewell: "goodbye"});
        });
      
      
        // 使用 connect 從 Content 發送和接收消息
        var port = chrome.runtime.connect({name: "knockknock"});
        port.postMessage({joke: "Knock knock"});
        port.onMessage.addListener(function(msg) {
          if (msg.question === "Who's there?")
            port.postMessage({answer: "Madame"});
          else if (msg.question === "Madame who?")
            port.postMessage({answer: "Madame... Bovary"});
        });
      })();
      sendResponse的回調非常關鍵,如果不寫會造成報錯關閉channel導致組件重新加載

       

       

       


       

       

      posted @ 2025-04-24 14:41  恣肆zisi  閱讀(174)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 亚洲国产日韩欧美一区二区三区| 午夜dv内射一区二区| 亚洲一区成人在线视频| 人人澡人人透人人爽| 亚洲一品道一区二区三区| 99精品伊人久久久大香线蕉| 八宿县| 国产精品久久露脸蜜臀| 东京热人妻无码一区二区av| 思思久99久女女精品| 男女动态无遮挡动态图| 久热色精品在线观看视频| 中文字幕精品人妻av在线 | 高清精品一区二区三区| 久色伊人激情文学你懂的| 比如县| 国产成人午夜福利精品| 久久成人国产精品免费软件| 99热国产这里只有精品9| 国产爆乳无码视频在线观看3| 成人精品视频一区二区三区| 精品国产成人国产在线视| 精品国产自在久久现线拍| 亚洲乱色一区二区三区丝袜| 亚洲成人高清av在线| 青青国产揄拍视频| 国产成人a∨激情视频厨房| 妖精视频亚州无吗高清版| 国产高清精品在线一区二区| 亚洲精品一区二区动漫| 粗大的内捧猛烈进出小视频| 国产一区二区三区精品综合| 国内精品一区二区在线观看| 亚洲精品综合网二三区| 熟女乱一区二区三区四区| 欧美成人精品三级网站| 精品国产欧美一区二区三区在线 | 激情综合网激情五月伊人| 少妇激情一区二区三区视频小说| 精品日本免费一区二区三区| 综合激情丁香久久狠狠|