在谷歌应用程序脚本中解析 html 的最佳方法是啥

Posted

技术标签:

【中文标题】在谷歌应用程序脚本中解析 html 的最佳方法是啥【英文标题】:What is the best way to parse html in google apps script在谷歌应用程序脚本中解析 html 的最佳方法是什么 【发布时间】:2013-10-27 14:55:14 【问题描述】:
var page = UrlFetchApp.fetch(contestURL);
var doc = XmlService.parse(page);

上面的代码在使用时会出现解析错误,但是如果我将 XmlService 类替换为已弃用的 Xml 类,并设置了 lenient 标志,它会正确解析 html

var page = UrlFetchApp.fetch(contestURL);
var doc = Xml.parse(page, true);

这个问题主要是因为html的javascript部分没有CDATA,解析器报错如下。

The entity name must immediately follow the '&' in the entity reference.

即使我使用正则表达式删除了所有<script>(.*?)</script>,它仍然会抱怨,因为<br> 标签没有关闭。 有没有一种将 html 解析为 DOM 树的干净方法。

【问题讨论】:

见***.com/a/1732454/362634 ... 您可能会获取页面并使用解析器去除不需要的标签,然后启动另一个解析,这可能是可能的。我确实知道 XmlService 运行良好,但有点问题。 【参考方案1】:

我遇到了同样的问题。我可以通过首先使用已弃用的Xml.parse 来规避它,因为它仍然有效,然后选择 body XmlElement,然后将其 Xml 字符串传递给新的 XmlService.parse 方法:

var page = UrlFetchApp.fetch(contestURL);
var doc = Xml.parse(page, true);
var bodyHtml = doc.html.body.toXmlString();
doc = XmlService.parse(bodyHtml);
var root = doc.getRootElement();

注意:如果旧的 Xml.parse 从 Google 脚本中完全删除,此解决方案可能不起作用。

【讨论】:

结构完全不正确且无法验证的“html”文档和 XmlService.parse 只会阻塞它们怎么办? doc.html.body 出于某种原因对我来说是一个数组,每个元素似乎都不一样 我认为 javascript 导致元素提前结束 这很棒,因为它允许您使用记录良好的 XmlService,否则它将无法使用,因为它无法解析 HTML 文件。如果有人想知道如何选择元素,这将有所帮助:sites.google.com/site/scriptsexamples/learn-by-example/…(抱歉新提交,编辑为时已晚..) 2020 年的注意事项:Xml.parse 确实已被删除。【参考方案2】:

在 2021 年,我所知道的在 .gs 端解析 HTML 的最佳方法是......

    单击库旁边的 + 输入 1ReeQ6WO8kKNxoaA_O0XEQ589cIrRvEBA9qcWpNqdOP17i47u6N9M5Xh0 点击“查找” 点击添加 示例用法:
const contentText = UrlFetchApp.fetch('https://www.somesite.com/').getContentText();
const $ = Cheerio.load(contentText);

$('.some-class').first().text();

就是这样——这可能是我们在 GAS 中最接近 jQuery 的 DOM 选择了。 .first() 很重要,否则您可能会提取比预期更多的内容(将其视为使用 querySelector() 而不是 querySelectorAll())。

信用到期:https://github.com/tani/cheeriogs

【讨论】:

这是我今天在所有搜索中找到的最佳答案。我对它进行了编码,它工作得非常好。将此信息与媒体 (medium.com/@stefanhyltoft/…) 上的博客和一些 Cheerio 文档相结合,我能够使其适用于解析 covid 医院数据的非常复杂的 HTML 表格。【参考方案3】:

从 2020 年 5 月起,您现在可以使用 Cheerio library for Google Apps Script 执行此操作。

返回***主页的内容

const content = getContent_('https://en.wikipedia.org');
const $ = Cheerio.load(content);
Logger.log($('#mp-right').text());

返回***首页第一段<p>的内容

const content = getContent_('https://en.wikipedia.org');
const $ = Cheerio.load(content);
Logger.log($('p').first().text());

要添加到您的项目中:

在 Google Apps 脚本编辑器中选择 Resources - Libraries...。在Add a library字段中输入项目密钥1ReeQ6WO8kKNxoaA_O0XEQ589cIrRvEBA9qcWpNqdOP17i47u6N9M5Xh0,然后单击“添加”。选择最高版本号,点击“保存”。

【讨论】:

奇怪的是,这个答案比一年后基本相同的答案得票少。信用到期。 @TNguyen 因为这篇文章是从另一个观点较少的问题合并而来的。【参考方案4】:

我发现在 google 应用程序中解析 html 的最佳方法是避免使用 XmlService.parse 或 Xml.parse。 XmlService.parse 不适用于某些网站的不良 html 代码。

这里有一个基本示例,说明如何在不使用 XmlService.parse 或 Xml.parse 的情况下轻松解析任何网站。在此示例中,我从“wikipedia.org/wiki/President_of_the_United_States”检索总统列表 使用常规的 javascript document.getElementsByTagName(),并将值粘贴到我的 google 电子表格中。

1- 创建一个新的 Google 表格;

2- 单击菜单工具 > 脚本编辑器... 打开一个带有代码编辑器窗口的新选项卡,并将以下代码复制到您的 Code.gs 中:

function onOpen() 
 var ui = SpreadsheetApp.getUi();
    ui.createMenu("Parse Menu")
      .addItem("Parse", "parserMenuItem")
      .addToUi();




function parserMenuItem() 
  var sideBar = HtmlService.createHtmlOutputFromFile("test");
  SpreadsheetApp.getUi().showSidebar(sideBar);



function getUrlData(url) 
 var doc = UrlFetchApp.fetch(url).getContentText()
 return doc                               


function writeToSpreadSheet(data) 
 var ss = SpreadsheetApp.getActiveSpreadsheet();
 var sheet = ss.getSheets()[0];
 var row=1

   for (var i = 0; i < data.length; i++) 
   var x = data[i];
   var range = sheet.getRange(row, 1)
   range.setValue(x);
   var row = row+1
    

3- 将 HTML 文件添加到您的 Apps 脚本项目中。打开脚本编辑器并选择文件 > 新建 > Html 文件,并将其命名为“test”。然后将以下代码复制到您的 test.html 中

<!DOCTYPE html>
<html>
<head>    
</head>
<body>
<input id= "mButon" type="button" value="Click here to get list"
onclick="parse()">
<div hidden id="mOutput"></div>
</body>
<script>

window.onload = onOpen;

function onOpen() 
 var url = "https://en.wikipedia.org/wiki/President_of_the_United_States"
 google.script.run.withSuccessHandler(writeHtmlOutput).getUrlData(url)
 document.getElementById("mButon").style.visibility = "visible";


function writeHtmlOutput(x) 
 document.getElementById('mOutput').innerHTML = x;


function parse() 

var list = document.getElementsByTagName("area");
var data = [];

   for (var i = 0; i < list.length; i++) 
   var x = list[i];
   data.push(x.getAttribute("title"))
    

google.script.run.writeToSpreadSheet(data);
 
</script> 
</html>

4- 保存您的 gs 和 html 文件并返回到您的电子表格。重新加载您的电子表格。单击“解析菜单”-“解析”。然后点击侧边栏中的“单击此处获取列表”。

【讨论】:

这似乎存在一些风险,当您在 mOutput div 下添加任何内容时,允许您通过网络读取的任何内容运行脚本。如果您从第 3 方服务器加载 html,我建议将输出粘贴在禁用脚本的沙盒 iframe 中。 或者我想更好的是在字符串上使用 DOMParser 来创建一个 Document 而无需将其添加到活动 DOM 中。 developer.mozilla.org/en-US/docs/Web/API/DOMParser【参考方案5】:

Xml.parse() 有一个打开宽松解析的选项,这有助于解析 HTML。请注意,Xml 服务已被弃用,而较新的 XmlService 没有此功能。

【讨论】:

【参考方案6】:

对于简单的任务,例如从网页中获取一个值,您可以使用正则表达式。正则表达式在解析 HTML 方面是出了名的糟糕,因为它可能会出现各种奇怪的情况,但如果您对正在访问的 HTML 有信心,这有时可能是最简单的方法。

这是一个获取页面&lt;title&gt;标签内容的示例:

var page = UrlFetchApp.fetch(contestURL);
var regExp = new RegExp("<title>(.*)</title>", "gi");
var result = regExp.exec(page.getContentText());
// [1] is the match group when using parenthesis in the pattern
var value = result ? result[1] : 'No title found';

【讨论】:

最后一行为我返回null 使用正则表达式解析html/xml通常是一个非常糟糕的主意(tm) :blog.codinghorror.com/parsing-html-the-cthulhu-way @jtatria 你是对的。但如果是标题,它可能是安全的。非贪婪的版本可能会更好。【参考方案7】:

我知道这不是 OP 所要求的,但我在寻找一些 html 解析选项时发现了这个问题 - 所以它也可能对其他人有用。

有一个easy to use the library for TEXT parsing。如果您只想从 html(xml) 代码中获取一条信息,这很有用。

EDIT 2021: 脚本库 ID 为: 1Mc8BthYthXx6CoIz90-JiSzSafVnT6U3t0z_W3hLTAX5ek4w0G_EIrNw

如上图所示

function getData() 
    var url = "https://chrome.google.com/webstore/detail/signaturesatori-central-s/fejomcfhljndadjlojamaklegghjnjfn?hl=en";
    var fromText = '<span class="e-f-ih" title="';
    var toText = '">';
  
    var content = UrlFetchApp.fetch(url).getContentText();
    var scraped = Parser
                    .data(content)
                    .from(fromText)
                    .to(toText)
                    .build();
    Logger.log(scraped);
    return scraped;

【讨论】:

脚本说解析器没有定义。 2021:脚本id为:1Mc8BthYthXx6CoIz90-JiSzSafVnT6U3t0z_W3hLTAX5ek4w0G_EIrNw【参考方案8】:

如果你正在使用

Google Apps 脚本的 Cheerio 库

Source code

Library page(⭐星吧!)

按库 ID 安装:

1ReeQ6WO8kKNxoaA_O0XEQ589cIrRvEBA9qcWpNqdOP17i47u6N9M5Xh0

获取当前表情符号from unicode.org的函数:

function getEmojis() 
  var t = new Date();
  var url = 'https://unicode.org/emoji/charts/full-emoji-list.html';
  var fetch = UrlFetchApp.fetch(url);
  var contentText = fetch.getContentText();
  //console.log(new Date() - t);
  
  // Cherio
  var $ = Cheerio.load(contentText);
  var data = [];
  $("table > tbody > tr").each((index, element) => 
    var row = [];
    $(element).find("td").each((index, child) => 
      row.push($(child).text());
    );
    if (row.length > 0) 
      data.push(row);
        
  );

  //console.log(data);
  //console.log(new Date() - t);

  // Result
  return data;


↑示例代码展示了如何解析表格并将其放入[[array]]

可用作自定义函数:

奖金

解析网站可能是一项耗时的操作 + 您可能会达到极限。 这是一个包含完整版脚本的测试文件:

https://docs.google.com/spreadsheets/d/1iO7YjYWyfseQu_YCfRbGDPg7NskOgMu_iO1iGjr7KxY/edit#gid=93365395

↑ 它使用CasheService 来减少调用次数。

【讨论】:

【参考方案9】:

本来就没有办法,除非你做你已经尝试过的事情,如果 html 不符合 xml 格式,那将无法工作。

【讨论】:

【参考方案10】:

有两种选择

a) 一种是使用 JavaScript 的字符串函数。首先使用string.indexOf() 定位您的标签,然后使用string.substring() 提取您想要的数据。

b) 另一种选择是使用Xml Service。

【讨论】:

选项 a) 不够方便。如果选择了 XPath,选项 b) 会很好。我找不到这样的功能。【参考方案11】:

无法在 Apps 脚本中创建 HTML DOM 服务器端。使用正则表达式可能是您的最佳选择,至少对于简单的解析而言。

【讨论】:

以上是关于在谷歌应用程序脚本中解析 html 的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章

在谷歌地图上覆盖 ESRI shapefile 的最佳方法?

在 C# 中解析 html 的最佳方法是啥? [关闭]

在 C# 中解析 html 的最佳方法是啥? [关闭]

在 Bash 中解析命令行参数的最佳方法是啥?

如果在设定的时间内未按下键盘,您是否可以触发在谷歌应用程序脚本中运行的功能?

保持车辆连续定位的最佳方法