如何在不使用 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 &
html = html.replace(/&(?!amp;)/g, '&')
// 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 解析所需的文件夹?