Какова наилучшая практика для парсинга удаленного контента с помощью jQuery?

После вызова jQuery ajax для извлечения всего документа XHTML, каков наилучший способ выбора определенных элементов из результирующей строки? Возможно, есть библиотека или плагин, который решает эту проблему?

jQuery может выбирать только элементы XHTML, которые существуют в строке, если они обычно разрешены в div в спецификации W3C; поэтому мне интересно выбирать такие вещи, как <title>, <script> и <style>.

Согласно документации jQuery:

http://docs.jquery.com/Core/jQuery#htmlownerDocument

Строка HTML не может содержать элементы, которые недействительны в div, например html, head, body или элементы заголовка.

Поэтому, поскольку мы установили, что jQuery не предоставляет способ сделать это, как бы я выбрал эти элементы? В качестве примера, если вы можете показать мне, как выбрать удаленный заголовок страницы, это будет идеально!

Спасибо, Пит

Ответ 1

Вместо того, чтобы взломать jQuery для этого, я предлагаю вам отказаться от jQuery в течение минуты и использовать raw XML-методы. Используя методы XML Dom, вы можете сделать это:

  window.onload = function(){ 
    $.ajax({
          type: 'GET', 
          url: 'text.html',
          dataType: 'html',
          success: function(data) {

            //cross platform xml object creation from w3schools
            try //Internet Explorer
              {
              xmlDoc=new ActiveXObject("Microsoft.XMLDOM");
              xmlDoc.async="false";
              xmlDoc.loadXML(data);
              }
            catch(e)
              {
              try // Firefox, Mozilla, Opera, etc.
                {
                parser=new DOMParser();
                xmlDoc=parser.parseFromString(data,"text/xml");
                }
              catch(e)
                {
                alert(e.message);
                return;
                }
              }

            alert(xmlDoc.getElementsByTagName("title")[0].childNodes[0].nodeValue);
          }
    });
  }

Не вмешиваться в iframe и т.д.

Ответ 2

Просто идея, протестированная в FF/Safari, кажется, работает, если вы создадите iframe для временного хранения документа. Конечно, если вы это делаете, может быть разумнее использовать свойство src iframe для загрузки документа и делать все, что вы хотите, в "onload" этого.

  $(function() {
    $.ajax({
      type: 'GET', 
      url: 'result.html',
      dataType: 'html',
      success: function(data) {
        var $frame = $("<iframe src='about:blank'/>").hide();
        $frame.appendTo('body');
        var doc = $frame.get(0).contentWindow.document;
        doc.write(data);
        var $title = $("title", doc);
        alert('Title: '+$title.text() );
        $frame.remove();
      }
    });
  });

Мне пришлось добавить iframe в тело, чтобы получить его .contentWindow.

Ответ 3

Вдохновленный этот ответ, но с отложенным:

function fetchDoc(url) {
  var dfd;
  dfd = $.Deferred();

  $.get(url).done(function (data, textStatus, jqXHR) {

    var $iframe = $('<iframe style="display:none;"/>').appendTo('body');
    var $doc = $iframe.contents();
    var doc = $doc[0];

    $iframe.load(function() {
      dfd.resolveWith(doc, [data, textStatus, jqXHR]);
      return $iframe.remove();
    });
    doc.open();
    doc.write(data);

    return doc.close();
  }).fail(dfd.reject);

  return dfd.promise();
};

И курите его с помощью:

fetchDoc('/foo.html').done(function (data, textStatus, jqXHR) {
  alert($('title', this).text());
});

LIVE DEMO (нажмите "Запустить" )

Ответ 4

Как насчет быстрого переименования тегов?

$.ajax({
    type : "GET",
    url : 'results.html',
    dataType : "html",
    success: function(data) {

        data = data.replace(/html/g, "xhtmlx");
        data = data.replace(/head/g, "xheadx");
        data = data.replace(/title/g, "xtitlex");
        data = data.replace(/body/g, "xbodyx");

        alert($(data).find("xtitlex").text());
    }

});

Ответ 5

Это работает. Я просто разделил строительные блоки для лучшей читаемости.

Проверьте объяснения и встроенные комментарии, чтобы понять, как это работает, и почему это должно быть сделано следующим образом.

Конечно, это невозможно использовать для получения междоменного контента, для которого вам либо необходимо проксировать вызовы с помощью script, либо подумать об интеграции, например, flXHR (кросс-домен Ajax с Flash)

call.html

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>asd</title>
    <script src="jquery.js" type="text/javascript"></script>
    <script src="xmlDoc.js" type="text/javascript"></script>
    <script src="output.js" type="text/javascript"></script>
    <script src="ready.js" type="text/javascript"></script>
  </head>
  <body>
    <div>
      <input type="button" id="getit" value="GetIt" />
    </div>
  </body>
</html>

jquery.js (jQuery 1.3.2 несжатый) test.html действительный XHTML-документ

xmlDoc.js

// helper function to create XMLDocument out of a string
jQuery.createXMLDocument = function( s ) {
  var xmlDoc;
  // is it a IE?
  if ( window.ActiveXObject ) {
    xmlDoc = new ActiveXObject('Microsoft.XMLDOM');
    xmlDoc.async = "false";
    // prevent erros as IE tries to resolve the URL in the DOCTYPE
    xmlDoc.resolveExternals = false;
    xmlDoc.validateOnParse = false;
    xmlDoc.loadXML(s);
  } else {
    // non IE. give me DOMParser
    // theoretically this else branch should never be called
    // but just in case.
    xmlDoc = ( new DOMParser() ).parseFromString( s, "text/xml" );
  }
  return xmlDoc;
};

output.js

// Output the title of the loaded page
// And get the script-tags and output either the
// src attribute or code
function headerData(data) {
  // give me the head element
  var x = jQuery("head", data).eq(0);
  // output title
  alert(jQuery("title", x).eq(0).text());
  // for all scripttags which include a file out put src
  jQuery("script[src]", x).each(function(index) {
    alert((index+1)+" "+jQuery.attr(this, 'src'));
  });
  // for all scripttags which are inline javascript output code
  jQuery("script:not([src])", x).each(function(index) {
    alert(this.text);
  });
}

ready.js

$(document).ready(function() {
  $('#getit').click(function() {
    $.ajax({
      type : "GET",
      url : 'test.html',
      dataType : "xml",
      // overwrite content-type returned by server to ensure
      // the response getst treated as xml
      beforeSend: function(xhr) {
        // IE doesn't support this so check before using
        if (xhr.overrideMimeType) {
          xhr.overrideMimeType('text/xml');
        }
      },
      success: function(data) {
        headerData(data);
      },
      error : function(xhr, textStatus, errorThrown) {
        // if loading the response as xml failed try it manually
        // in theory this should only happen for IE
        // maybe some
        if (textStatus == 'parsererror') {
          var xmlDoc = jQuery.createXMLDocument(xhr.responseText);
          headerData(xmlDoc);
        } else {
          alert("Failed: " + textStatus + " " + errorThrown);
        }
      }
    });
  });
});

В Opera все работает без функций createXMLDocument и beforeSend.

Дополнительная сложность необходима для Firefox (3.0.11) и IE6 (не может тестировать IE7, IE8, другие браузеры), поскольку у них есть проблема, когда возвращаемый сервером Content-Type: не указывает, что он xml, Мой веб-сервер возвратил Content-Type: text/html; charset=UTF-8 для test.html.. В этих двух браузерах jQuery вызывал обратный вызов error с textStatus, говоря parsererror. Потому что в строке 3706 в jQuery.js

data = xml ? xhr.responseXML : xhr.responseText;

data устанавливается в значение null. Как и в FF и IE, значение xhr.responseXML равно null. Это происходит потому, что они не получают, что возвращаемые данные являются xml (как Opera). И только xhr.responseText устанавливается со всем xhtml-кодом. Поскольку данные являются нулевыми, строка 3708

if ( xml && data.documentElement.tagName == "parsererror" )

генерирует исключение, которое помечено в строке 3584, а для статуса установлено значение parsererror.

В FF я могу решить проблему с помощью функции overrideMimeType() перед отправкой запроса.

Но IE не поддерживает эту функцию в объекте XMLHttpRequest, поэтому мне нужно сгенерировать сам XMLDocument, если выполняется обратный вызов ошибки, а ошибка parsererror.

пример для test.html

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <meta http-equiv="content-type" content="text/html; charset=utf-8" />
    <title>Plugins | jQuery Plugins</title>
    <script type="text/javascript" src="jquery.js"></script>
    <script type="text/javascript">var imagePath = '/content/img/so/';</script>
  </head>
  <body>
  </body>
</html>

Ответ 6

Бесстыдно скопировано и адаптировано из другого моего ответа (Простой пример jQuery ajax, не находящий элементы в возвращенном HTML), это выбирает HTML удаленной страницы, затем Функция parseHTML создает для него временный элемент div и помещает внутри, проходит через него и возвращает запрошенный элемент. Затем jQuery уведомляет текст() внутри.

$(document).ready(function(){
  $('input').click(function(){
    $.ajax({
      type : "POST",
      url : 'ajaxtestload.html',
      dataType : "html",
      success: function(data) {
        alert( data ); // shows whole dom
        var gotcha = parseHTML(data, 'TITLE'); // nodeName property returns uppercase
        if (gotcha) {
          alert($(gotcha).html()); // returns null
        }else{
          alert('Tag not found.');
        }
      },
      error : function() {
        alert("Sorry, The requested property could not be found.");
      }
    });
  });
});

function parseHTML(html, tagName) {
  var root = document.createElement("div");
  root.innerHTML = html;
  // Get all child nodes of root div
  var allChilds = root.childNodes;
  for (var i = 0; i < allChilds.length; i++) {
    if (allChilds[i].nodeName == tagName) {
      return allChilds[i];
    }
  }
  return false;
}

Чтобы получить несколько элементов или список тэгов script, скажем, я думаю, вам нужно будет улучшить функцию parseHTML, но эй-доказательство концепции: -)

Ответ 7

Если вы хотите найти значение специально названных полей (т.е. входов в форме), то что-то вроде этого найдет их для вас:

var fields = ["firstname","surname", ...."foo"];

function findFields(form, fields) {
  var form = $(form);
  fields.forEach(function(field) {
    var val = form.find("[name="+field+"]").val();
    ....

Ответ 9

После синтаксического анализа строки XML в XML DOM я либо использовал бы jQuery на нем напрямую (вы можете сделать это, предоставив контекст селектору jQuery, например $(':title', xdoc.rootElement), или используя XPath (работает в Firefox, предположительно есть библиотеки для IE, но у меня не было хорошего успеха с ними). ​​

Ответ 10

$.get('yourpage.html',function(data){
    var content = $('<div/>').append(data).find('#yourelement').html();
});

Вы также можете просто временно обернуть внутри div. Вам даже не нужно добавлять его в DOM.