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

Posted

技术标签:

【中文标题】如何在不使用 XmlService 的情况下解析 Google Apps 脚本中的 HTML 字符串? [复制]【英文标题】:How to parse an HTML string in Google Apps Script without using XmlService? [duplicate] 【发布时间】:2016-02-26 20:43:57 【问题描述】:

我想使用带有 Google Apps 脚本的 Google 电子表格创建一个抓取工具。我知道这是可能的,并且我已经看过一些关于它的教程和线程。

主要思想是使用:

  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 html 和What 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 并轻松访问它的元素。 其实不是加分项。但是一个简单的谷歌应用脚​​本..

【问题讨论】:

这能回答你的问题吗? Parse HTML string using CSS selectors 【参考方案1】:

我为你的问题做了欢呼。它在 GAS 上作为 Cheerio 工作,它是类 jQuery 的 api。你可以这样做。

const content = UrlFetchApp.fetch('https://example.co/').getContentText();
const $ = Cheerio.load(content);
Logger.log($('p .blah').first().text()); // blah blah blah ...

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

【讨论】:

它不起作用。它甚至不会运行。一旦我添加了库,谷歌脚本甚至不会调试。 @toddmo 有时会运行 v12 非常适合我。 这完全符合我的要求!【参考方案2】:

这已在之前讨论过 - 请参阅 this Q&A。

XML 服务不同,XMLService 对格式错误的 HTML 不太宽容。 Justin Bicknell 的答案中的技巧可以完成这项工作。即使 XML 服务已被弃用,它仍然可以继续工作。

【讨论】:

XMLService 甚至不能容忍格式良好的 html。 html 不是 xml,即使是基本的现代 html,xml 解析器也会窒息。 Xml.parse 已从 Google 脚本中完全删除。【参考方案3】:

我已经在 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;

【讨论】:

【参考方案4】:

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

XmlService 仅适用于有效的 XML 文档,并且大多数 HTML(尤其是 HTML5)不是有效的 XML。 XmlService 的先前版本,简称为Xml,允许“宽松”解析,这也将允许它解析 HTML。该服务于 2013 年终止,但目前仍在运行。参考文档不再可用,但 old tutorial 显示了它的用法。

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

【讨论】:

Kimono 于 2016 年 2 月被收购并关闭。Portia 是一个开源替代品。【参考方案5】:

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

【讨论】:

【参考方案6】:

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

with jQuery 或许你可以更轻松地做到这一点。

【讨论】:

我需要在 googleappscript 上运行它。我没有找到在 appscript 中运行 Jquery 的方法。 我没有仔细阅读 OP - 我写了我的答案,想象您正在制作一个网络应用程序,并且可以在 javascript 文件和您的 google 应用程序脚本文件之间轻松传递数据。我不知道如何在电子表格插件中实现这一结果。【参考方案7】:

也许不是最干净的方法,但简单的字符串处理也可以在没有 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."

【讨论】:

作为初学者,我感谢您使用“香草”结构。【参考方案8】:

我今天刚刚通过按摩html获得了一些好运:

// close unclosed tags
html = html.replace(/(<(?=link|meta|br|input)[^>]*)(?<!\/)>/ig, '$1/>')
// force script / style content into cdata
html = html.replace(/(<(script|style)[^>]*>)/ig, '$1<![CDATA[').replace(/(<\/(script|style)[^>]*>)/ig, ']]>$1')
// change & to &amp;
html = html.replace(/&(?!amp;)/g, '&amp;')
// now it works! (tested with original url)
let document = XmlService.parse(html)

【讨论】:

以上是关于如何在不使用 XmlService 的情况下解析 Google Apps 脚本中的 HTML 字符串? [复制]的主要内容,如果未能解决你的问题,请参考以下文章

如何在不使用 Python 中的外部库的情况下解析 arff 文件

如何在不解析的情况下在javascript中同步包含JSON数据?

如何在不知道终端标量的映射和类型中的键的情况下使用 yaml-cpp 库解析任意 yaml 文件?

node.js 如何在不指定文件夹路径的情况下使用 index.js 解析所需的文件夹?

如何在不丢失值的情况下将 json 解析为 pandas 数据框? [复制]

如何在不编写正则表达式的情况下从字符串中解析日期?