title: Parsing HTML and Using an xPath Request in Google Scripts
body: <p>In my spreadsheet, I need to be able to pull HTML from a website, and then use an xPath request on it. To that effect, I've written the following code:</p>
  
  <pre><code>var artistPages = [];
  _.forEach(artistLinks, function (d, i) {
    var pageHtml = readRemoteXML(d);
    Logger.log(xPath("/html/body[@class='margin50']/div[@class='container main-page']/div[@class='row']/div[@class='col-xs-12 col-md-6 text-center']/div[@id='listAlbum']/div[@class='album']", pageHtml));
  });
  </code></pre>
  
  <p>Which uses the XPathXMLParsing library, which can be found here: <a href="https://github.com/ConfidentCannabis/sheets-script-xpath" rel="nofollow noreferrer">https://github.com/ConfidentCannabis/sheets-script-xpath</a></p>
  
  <p>The code for those functions is below:</p>
  
  <pre><code>function xPath(path, xmlFile) {
    var root = xmlFile.getRootElement();
    return xPathStep(path, root);
  }
  
  function xPathStep(path, node) {
    // if node is an array, return the result for each entry
    if (Array.isArray(node)) {
      return node.map(function(singleNode) {
        return xPathStep(path, singleNode);
      });
    }
  
    if (!node) {
      return;
    }
  
    var nextNode, nodeValue;
    var paths = path.split('/');
    var firstChild = paths[0];
    var remainingPath = paths.slice(1).join('/');
  
    // if child ends with [\d] - find a list, return this index
    var indexMatch = firstChild.match(/(\w+)\[(\d+)\]/);
    var attributeMatch = firstChild.match(/@(\w+)/);
  
    if (indexMatch) {
      var tagName = indexMatch[1];
      var index = indexMatch[2];
  
      var children = node.getChildren(tagName);
      nextNode = children[index];
    } else if (firstChild === '') {
      // if another name is next, use as a matching tag (and remove from path)
      var tagName = '';
      if (paths.length &gt; 1) {
        tagName = paths[1];
        remainingPath = paths.slice(2).join('/');
      }
      nextNode = node.getChildren(tagName);
    } else if (attributeMatch) {
      // @ means attribute
      var attributeName = attributeMatch[1];
      nodeValue = node.getAttribute(attributeName).getValue();
    } else {
      nextNode = node.getChild(firstChild);
    }
  
    var result;
    if (nodeValue) {
      result = nodeValue;
    } else if (remainingPath !== '') {
      result = xPathStep(remainingPath, nextNode);
    } else {
      result = nextNode.getText &amp;&amp; nextNode.getText();
    }
  
    return result;
  }
  
  function readRemoteXML(xmlFileUrl) {
    var content = UrlFetchApp.fetch(xmlFileUrl).getContentText();
    return XmlService.parse(content);
  }
  </code></pre>
  
  <p>The only problem is that that library works only on pure XML, and I'm trying to parse HTML, which means that there are various problems the parser runs into, such as <code>&lt;link&gt;</code> tags not being closed. <code>UrlFetchApp.fetch().getContentText()</code> returns a string, which then needs to be parsed into an XML object in order to have an xPath query run on it. So what I'm essentially asking is either for an alternative way to run an xPath query, or a way to fix all the XML issues before it is parsed by XmlService. Thank you in advance!</p>