Pobieranie źródłowego kodu HTML bieżącej strony z rozszerzenia Chrome

85

Mam rozszerzenie do Chrome. muszę analizować ze źródła HTML bieżącej strony. Znalazłem tutaj wszelkiego rodzaju rozwiązania ze stronami w tle i skryptami zawartości, ale żadne mi nie pomogło. oto, co mam do tej pory:
manifest.json:

{
  "name": "Extension",
  "version": "1.0",
  "description": "Extension",
  "browser_action": {
    "default_icon": "bmarkred.ico",
    "popup": "Test.html"
  },
  "content_scripts": [
    {
      "matches": ["http://*/*"],
      "js": ["content.js"]
    }
  ],
  "background": {
    "page": "backgroundPage.html"
  },
  "permissions": [
    "cookies",
    "tabs",
    "http://*/*", 
    "https://*/*"
  ]
}

background.html:

<html>
<head>
<script type="text/javascript">
    try {
        chrome.tabs.getSelected(null, function (tab) {
            chrome.tabs.sendRequest(tab.id, {action: "getSource"}, function(source) {
                alert(source);
            });
        });
    }
    catch (ex) {
        alert(ex);
    }
</script>
</head>
</html>

content.js:

chrome.extension.onRequest.addListener(function(request, sender, callback) {
    if (request.action == "getSource") {
        callback(document.getElementsByTagName('html')[0].innerHTML);
    }
});

Alert zawsze ostrzega niezdefiniowany. nawet jeśli zmienię w pliku content.js funkcję wywołania zwrotnego na:

callback('hello'); 

wciąż ten sam wynik. Co ja robię źle? może robię to w złym kierunku. czego naprawdę potrzebuję to to. Gdy użytkownik otworzy wyskakujące okienko rozszerzenia (i tylko wtedy), potrzebuję kodu HTML bieżącej strony, aby móc ją przeanalizować. jakieś sugestie?

Pan T.
źródło
Problem polega na tym, że kod na stronie w tle jest uruchamiany natychmiast (przed wstrzyknięciem skryptów zawartości). Już wcześniej zadano bardzo podobne / zduplikowane pytanie; Spójrz na odpowiedź w Otwórz nową kartę Google Chrome i uzyskaj źródło .
Rob W.
Dzięki za twoją odpowiedź Rob. Skopiowałem segmenty kodu z dołączonego linku, ale nadal nie działa. Problem polega na tym, że moje rozszerzenie jest wyskakującym okienkiem i muszę pobrać kod HTML tylko wtedy, gdy użytkownik otworzy moje rozszerzenie. na przykład, jeśli bieżącą zakładką jest facebook.com, to dopiero po otwarciu mojego rozszerzenia pobiorę źródło html do mojego pliku js (nie skrypt zawartości ani strona w tle).
Pan T.
Zaktualizuj swoje pytanie aktualnym kodem. Kod musi zawierać komentarze, które podkreślają problem.
Rob W,

Odpowiedzi:

155

Wstaw skrypt na stronę, z której chcesz pobrać źródło i wyślij go z powrotem do wyskakującego okienka ....

manifest.json

{
  "name": "Get pages source",
  "version": "1.0",
  "manifest_version": 2,
  "description": "Get pages source from a popup",
  "browser_action": {
    "default_icon": "icon.png",
    "default_popup": "popup.html"
  },
  "permissions": ["tabs", "<all_urls>"]
}

popup.html

<!DOCTYPE html>
<html style=''>
<head>
<script src='popup.js'></script>
</head>
<body style="width:400px;">
<div id='message'>Injecting Script....</div>
</body>
</html>

popup.js

chrome.runtime.onMessage.addListener(function(request, sender) {
  if (request.action == "getSource") {
    message.innerText = request.source;
  }
});

function onWindowLoad() {

  var message = document.querySelector('#message');

  chrome.tabs.executeScript(null, {
    file: "getPagesSource.js"
  }, function() {
    // If you try and inject into an extensions page or the webstore/NTP you'll get an error
    if (chrome.runtime.lastError) {
      message.innerText = 'There was an error injecting script : \n' + chrome.runtime.lastError.message;
    }
  });

}

window.onload = onWindowLoad;

getPagesSource.js

// @author Rob W <http://stackoverflow.com/users/938089/rob-w>
// Demo: var serialized_html = DOMtoString(document);

function DOMtoString(document_root) {
    var html = '',
        node = document_root.firstChild;
    while (node) {
        switch (node.nodeType) {
        case Node.ELEMENT_NODE:
            html += node.outerHTML;
            break;
        case Node.TEXT_NODE:
            html += node.nodeValue;
            break;
        case Node.CDATA_SECTION_NODE:
            html += '<![CDATA[' + node.nodeValue + ']]>';
            break;
        case Node.COMMENT_NODE:
            html += '<!--' + node.nodeValue + '-->';
            break;
        case Node.DOCUMENT_TYPE_NODE:
            // (X)HTML documents are identified by public identifiers
            html += "<!DOCTYPE " + node.name + (node.publicId ? ' PUBLIC "' + node.publicId + '"' : '') + (!node.publicId && node.systemId ? ' SYSTEM' : '') + (node.systemId ? ' "' + node.systemId + '"' : '') + '>\n';
            break;
        }
        node = node.nextSibling;
    }
    return html;
}

chrome.runtime.sendMessage({
    action: "getSource",
    source: DOMtoString(document)
});
PAEz
źródło
@Gil Tankus Bardzo przepraszam za mój pierwszy post, nie zwróciłem wystarczającej uwagi na komentarze (ponownie) i skończyło się na tym, że powróciłem do tego, co powiedział Rob W. Nowy post powinien mieć to, czego chciałeś.
PAEz
Dzięki, Twoja odpowiedź była bardzo pomocna, mój problem polega na tym, że onMessage jest asynchroniczny. w moim wyskakującym okienku mam wiele innych rzeczy, które są powiązane ze źródłowym kodem HTML. jak mogę zapisać źródło w globalnej zmiennej zmiennej i dopiero wtedy kontynuować korzystanie z funkcji ładowania strony?
Pan T.
Myślę, że nie możesz. Albo będziesz musiał umieścić to w kodzie wywołania zwrotnego, albo w funkcji i wywołać to w wywołaniu zwrotnym ... gdyby tylko JS miał gotopolecenie aye? ; P
PAEz
19
Dlaczego nie po prostu coś takiego jak document.documentElement.outerHTML zamiast funkcji DOMtoString?
djfm,
@djfm To byłoby dobrze przez prawie cały czas. Chodzi o to, że z tego, co mogłem powiedzieć, że funkcja Roba W jest bardziej kompletna ... zwraca typ dokumentu, na przykład, że twoje rozwiązanie nie, twoje tylko pobiera część html.
PAEz
0

Oto moje rozwiązanie:

chrome.runtime.onMessage.addListener(function(request, sender) {
        if (request.action == "getSource") {
            this.pageSource = request.source;
            var title = this.pageSource.match(/<title[^>]*>([^<]+)<\/title>/)[1];
            alert(title)
        }
    });

    chrome.tabs.query({ active: true, currentWindow: true }, tabs => {
        chrome.tabs.executeScript(
            tabs[0].id,
            { code: 'var s = document.documentElement.outerHTML; chrome.runtime.sendMessage({action: "getSource", source: s});' }
        );
    });
Dejan S
źródło