如何在不使用XmlService的情况下解析Google Apps脚本中的HTML字符串?

时间:2015-11-24 11:59:32

标签: javascript parsing google-apps-script google-sheets html-parsing

我想使用Google Spreadsheets和Google Apps脚本创建一个刮刀。我知道这是可能的,我已经看过一些关于它的教程和线程。

主要思想是使用:

  var html = UrlFetchApp.fetch('http://en.wikipedia.org/wiki/Document_Object_Model').getContentText();
  var doc = XmlService.parse(html);

然后开始使用这些元素。但是,方法

XmlService.parse()

对某些页面不起作用。例如,如果我尝试:

function test(){
    var html = UrlFetchApp.fetch("https://www.nespresso.com/br/pt/product/maquina-de-cafe-espresso-pixie-clips-preto-lima-neon-c60-220v").getContentText();
    var parse = XmlService.parse(html);
}

我收到以下错误:

Error on line 225: The entity name must immediately follow the '&' in the entity reference. (line 3, file "")

我尝试使用string.replace()来消除显然导致错误的字符,但它不起作用。出现所有其他错误。以下代码为例:

function test(){
    var html = UrlFetchApp.fetch("https://www.nespresso.com/br/pt/product/maquina-de-cafe-espresso-pixie-clips-preto-lima-neon-c60-220v").getContentText();
    var regExp = new RegExp("&", "gi");
    html = html.replace(regExp,"");

    var parse = XmlService.parse(html);
}

给我以下错误:

Error on line 358: The content of elements must consist of well-formed character data or markup. (line 6, file "")

我认为这是XmlService.parse()方法的问题。

我已经阅读了这篇帖子:

Google App Script parse table from messed htmlWhat is the best way to parse html in google apps script可以使用名为xml.parse()的弃用方法,该方法接受允许解析HTML的第二个参数。但是,正如我所提到的,它已被弃用,我无法在任何地方找到任何文档。 xml.parse()似乎解析了字符串,但由于缺少文档,我无法处理这些元素。而且它也不是最安全的长期解决方案,因为它可以很快停用。

所以,我想知道如何在Google Apps脚本中解析此HTML?

我也尝试过:

function test(){

    var html = UrlFetchApp.fetch("https://www.nespresso.com/br/pt/product/maquina-de-cafe-espresso-pixie-clips-preto-lima-neon-c60-220v").getContentText();
    var htmlOutput = HtmlService.createHtmlOutput(html).getContent();

    var parse = XmlService.parse(htmlOutput);
}

但是它不起作用,我收到了这个错误:

  

格式错误的HTML内容:

我考虑使用开源库来解析HTML,但我找不到任何。

我的最终目标是从一组页面获取一些信息,如价格,链接,产品名称等。我设法使用一系列RegEx来做到这一点:

var ss = SpreadsheetApp.getActiveSpreadsheet();
  var linksSheet = ss.getSheetByName("Links");
  var resultadosSheet = ss.getSheetByName("Resultados");

function scrapyLoco(){

  var links = linksSheet.getRange(1, 1, linksSheet.getLastRow(), 1).getValues();
  var arrayGrandao = [];
  for (var row =  0, len = links.length; row < len; row++){
   var link = links[row];


   var arrayDeResultados = pegarAsCoisas(link[0]);
   Logger.log(arrayDeResultados);
   arrayGrandao.push(arrayDeResultados);
  }   


  resultadosSheet.getRange(2, 1, arrayGrandao.length, arrayGrandao[0].length).setValues(arrayGrandao);

}


function pegarAsCoisas(linkDoProduto) {
  var resultadoArray = [];

  var html = UrlFetchApp.fetch(linkDoProduto).getContentText();
  var regExp = new RegExp("<h1([^]*)h1>", "gi");
  var h1Html = regExp.exec(html);
  var h1Parse = XmlService.parse(h1Html[0]);
  var h1Output = h1Parse.getRootElement().getText();
  h1Output = h1Output.replace(/(\r\n|\n|\r|(^( )*))/gm,"");

  regExp = new RegExp("Ref.: ([^(])*", "gi");
  var codeHtml = regExp.exec(html);
  var codeOutput = codeHtml[0].replace("Ref.: ","").replace(" ","");

  regExp = new RegExp("margin-top: 5px; margin-bottom: 5px; padding: 5px; background-color: #699D15; color: #fff; text-align: center;([^]*)/div>", "gi");
  var descriptionHtml = regExp.exec(html);
  var regExp = new RegExp("<p([^]*)p>", "gi");
  var descriptionHtml = regExp.exec(descriptionHtml);
  var regExp = new RegExp("^[^.]*", "gi");
  var descriptionHtml = regExp.exec(descriptionHtml);
  var descriptionOutput = descriptionHtml[0].replace("<p>","");
  descriptionOutput = descriptionOutput+".";

  regExp = new RegExp("ecom(.+?)Main.png", "gi");
  var imageHtml = regExp.exec(html);
  var comecoDaURL = "https://www.nespresso.com/";
  var imageOutput = comecoDaURL+imageHtml[0];

  var regExp = new RegExp("nes_l-float nes_big-price nes_big-price-with-out([^]*)p>", "gi");
  var precoHtml = regExp.exec(html);
  var regExp = new RegExp("[0-9]*,", "gi");
  precoHtml = regExp.exec(precoHtml);
  var precoOutput = "BRL "+precoHtml[0].replace(",","");

  resultadoArray = [codeOutput,h1Output,descriptionOutput,"Home & Garden > Kitchen & Dining > Kitchen Appliances > Coffee Makers & Espresso Machines",
                    "Máquina",linkDoProduto,imageOutput,"new","in stock",precoOutput,"","","","Nespresso",codeOutput];

  return resultadoArray;
}

但这对于编程非常耗时,很难动态地改变它并且不太可靠。

我需要一种解析此HTML并轻松访问其元素的方法。 它实际上不是一个补充。但是一个简单的谷歌应用程序脚本..

8 个答案:

答案 0 :(得分:8)

我在vanilla js中做过这个。不是真正的HTML解析。只是尝试从字符串(url)中获取一些内容:

function getLKKBTC() {
  var url = 'https://www.lykke.com/exchange';
  var html = UrlFetchApp.fetch(url).getContentText();
  var searchstring = '<td class="ask_BTCLKK">';
  var index = html.search(searchstring);
  if (index >= 0) {
    var pos = index + searchstring.length
    var rate = html.substring(pos, pos + 6);
    rate = parseFloat(rate)
    rate = 1/rate
    return parseFloat(rate);
  }
  throw "Failed to fetch/parse data from " + url;
}

答案 1 :(得分:6)

之前已经讨论过这个问题。见这里:What is the best way to parse html in google apps script

XML 服务不同, XMLService 对格式错误的HTML不太宽容。 Justin Bicknell在答案中的诀窍就是这个。即使 XML 服务已被弃用,它仍然可以继续工作。

答案 2 :(得分:5)

我为你的问题做了cheeriogs。它在GAS上的工作就像jer-like api那样的cheerio。你可以这样做。

function reverse_vowels($word) {
    $vowels = implode(array_filter(str_split($word),
              function ($c) {return preg_match('/[aeiou]/i', $c);}));
    $v = 0;
    $reverse = implode(array_map(
              function ($i) use ($word, $vowels, &$v) {
                 $is_vowel = preg_match('/[aeiou]/i', $word[$i]);
                 return $is_vowel ? $vowels[strlen($vowels) - 1 - $v++] : $word[$i];
              }, range(0, strlen($word) - 1)));
    return $reverse;
}

echo reverse_vowels('The quick brown fox jumps over the lazy dog');

另见https://github.com/asciian/cheeriogs

答案 3 :(得分:2)

请注意,某些网站可能不允许自动抓取其内容,因此请在使用Apps脚本提取内容之前查阅他们的条款或服务。

XmlService仅对有效的XML文档有效,而大多数HTML(尤其是HTML5)都不是有效的XML。先前版本的XmlService,简称为Xml,允许“宽松”解析,这也允许它解析HTML。这项服务在2013年落日,但目前仍在运作。参考文档已不再可用,但此old tutorial显示了它的用法。

另一种选择是使用像Kimono这样的服务,它处理抓取和解析部分,并提供一个简单的API,您可以通过UrlFetchApp调用以检索结构化数据。

答案 4 :(得分:1)

你可以use javascript来解析HTML吗?如果您的Google Apps脚本将html检索为字符串然后将其返回到javascript函数,那么您似乎可以在Google Apps脚本之外解析它。您想要抓取的任何标签,您都可以发送到可以保存内容的专用Google Apps功能。

您可以更轻松地完成此任务with jQuery

答案 5 :(得分:0)

我找到了一个非常巧妙的替代方法,可以使用Google App Script。它被称为PhantomJS Cloud。可以使用urlFetchApp来访问API。这允许在页面上执行Jquery代码,这使生活变得更加简单。

答案 6 :(得分:0)

可能不是最干净的方法,但是简单的字符串处理也可以在没有xmlservice的情况下完成工作:

var url = 'https://somewebsite.com/?q=00:11:22:33:44:55';
var html = UrlFetchApp.fetch(url).getContentText();
// we want only the link text displayed from here:
//<td><a href="/company/ubiquiti-networks-inc">Ubiquiti Networks Inc.</a></td>
var string1 = html.split('<td><a href="/company/')[1]; // all after '<td><a href="/company/'
var string2 = string1.split('</a></td>')[0];           // all before '</a></td>'
var string3 = string2.split('>')[1];                   // all after '>'
Logger.log('link text: '+string3);                     // string3 => "Ubiquiti Networks Inc."

答案 7 :(得分:0)

今天我只是通过按摩html来获得好运:

core.js:4197 ERROR Error: Uncaught (in promise): FirebaseError: [code=invalid-argument]: The value of 
 property "ImageUrl" is longer than 1048487 bytes.
FirebaseError: The value of property "ImageUrl" is longer than 1048487 bytes.