首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >问答首页 >获取文档中的所有链接

获取文档中的所有链接
EN

Stack Overflow用户
提问于 2013-09-10 11:32:08
回答 6查看 17.5K关注 0票数 16

给定Google /Drive中的“普通文档”(例如段落、列表、表),其中包含分散在内容中的外部链接,那么如何使用Google脚本编译一个链接列表?

具体来说,我希望通过在每个url中搜索 oldText 来更新文档中所有破损的链接,并在每个url中用 newText 替换它,而不是文本。

我不认为开发文档中的替换文本部分是我所需要的--我需要扫描文档的每个元素吗?我可以只使用editAsText并使用html正则表达式吗?请举例说明。

EN

回答 6

Stack Overflow用户

发布于 2016-11-21 13:52:41

对于您的第一个问题,我提供另一个简短的答案,涉及遍历文档正文中的所有链接。这个有指导意义的代码返回当前文档正文中的一个平面链接数组,其中每个链接都由一个对象表示,该对象的条目指向文本元素(text)、包含它的段落元素或列表项元素(paragraph)、链接出现的文本中的偏移索引(startOffset)和URL本身(url)。希望,你会发现它很容易适应你自己的需要。

它使用getTextAttributeIndices()方法,而不是对文本的每个字符进行迭代,因此预期执行速度要比以前的书面答案快得多。

编辑:自从最初发布这个答案以来,我对函数做了几次修改。它现在还(1)包括每个链接的endOffsetInclusive属性(请注意,对于扩展到文本元素末尾的链接,它可以是null --在这种情况下,可以使用link.text.length-1 );(2)在文档的所有部分中查找链接,而不仅仅是正文,(3)包括sectionisFirstPageSection属性,以指示链接的位置;(4)接受参数mergeAdjacent,当设置为true时,该参数将只返回链接到同一个URL的连续文本段的单个链接条目(例如,如果文本的一部分与另一部分的样式不同,则该链接项将被视为单独的)。

为了将链接包含在所有部分中,引入了一个新的实用函数iterateSections()

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/**
 * Returns a flat array of links which appear in the active document's body. 
 * Each link is represented by a simple Javascript object with the following 
 * keys:
 *   - "section": {ContainerElement} the document section in which the link is
 *     found. 
 *   - "isFirstPageSection": {Boolean} whether the given section is a first-page
 *     header/footer section.
 *   - "paragraph": {ContainerElement} contains a reference to the Paragraph 
 *     or ListItem element in which the link is found.
 *   - "text": the Text element in which the link is found.
 *   - "startOffset": {Number} the position (offset) in the link text begins.
 *   - "endOffsetInclusive": the position of the last character of the link
 *      text, or null if the link extends to the end of the text element.
 *   - "url": the URL of the link.
 *
 * @param {boolean} mergeAdjacent Whether consecutive links which carry 
 *     different attributes (for any reason) should be returned as a single 
 *     entry.
 * 
 * @returns {Array} the aforementioned flat array of links.
 */
function getAllLinks(mergeAdjacent) {
  var links = [];

  var doc = DocumentApp.getActiveDocument();


  iterateSections(doc, function(section, sectionIndex, isFirstPageSection) {
    if (!("getParagraphs" in section)) {
      // as we're using some undocumented API, adding this to avoid cryptic
      // messages upon possible API changes.
      throw new Error("An API change has caused this script to stop " + 
                      "working.\n" +
                      "Section #" + sectionIndex + " of type " + 
                      section.getType() + " has no .getParagraphs() method. " +
        "Stopping script.");
    }

    section.getParagraphs().forEach(function(par) { 
      // skip empty paragraphs
      if (par.getNumChildren() == 0) {
        return;
      }

      // go over all text elements in paragraph / list-item
      for (var el=par.getChild(0); el!=null; el=el.getNextSibling()) {
        if (el.getType() != DocumentApp.ElementType.TEXT) {
          continue;
        }

        // go over all styling segments in text element
        var attributeIndices = el.getTextAttributeIndices();
        var lastLink = null;
        attributeIndices.forEach(function(startOffset, i, attributeIndices) { 
          var url = el.getLinkUrl(startOffset);

          if (url != null) {
            // we hit a link
            var endOffsetInclusive = (i+1 < attributeIndices.length? 
                                      attributeIndices[i+1]-1 : null);

            // check if this and the last found link are continuous
            if (mergeAdjacent && lastLink != null && lastLink.url == url && 
                  lastLink.endOffsetInclusive == startOffset - 1) {
              // this and the previous style segment are continuous
              lastLink.endOffsetInclusive = endOffsetInclusive;
              return;
            }

            lastLink = {
              "section": section,
              "isFirstPageSection": isFirstPageSection,
              "paragraph": par,
              "textEl": el,
              "startOffset": startOffset,
              "endOffsetInclusive": endOffsetInclusive,
              "url": url
            };

            links.push(lastLink);
          }        
        });
      }
    });
  });


  return links;
}

/**
 * Calls the given function for each section of the document (body, header, 
 * etc.). Sections are children of the DocumentElement object.
 *
 * @param {Document} doc The Document object (such as the one obtained via
 *     a call to DocumentApp.getActiveDocument()) with the sections to iterate
 *     over.
 * @param {Function} func A callback function which will be called, for each
 *     section, with the following arguments (in order):
 *       - {ContainerElement} section - the section element
 *       - {Number} sectionIndex - the child index of the section, such that
 *         doc.getBody().getParent().getChild(sectionIndex) == section.
 *       - {Boolean} isFirstPageSection - whether the section is a first-page
 *         header/footer section.
 */
function iterateSections(doc, func) {
  // get the DocumentElement interface to iterate over all sections
  // this bit is undocumented API
  var docEl = doc.getBody().getParent();

  var regularHeaderSectionIndex = (doc.getHeader() == null? -1 : 
                                   docEl.getChildIndex(doc.getHeader()));
  var regularFooterSectionIndex = (doc.getFooter() == null? -1 : 
                                   docEl.getChildIndex(doc.getFooter()));

  for (var i=0; i<docEl.getNumChildren(); ++i) {
    var section = docEl.getChild(i);

    var sectionType = section.getType();
    var uniqueSectionName;
    var isFirstPageSection = (
      i != regularHeaderSectionIndex &&
      i != regularFooterSectionIndex && 
      (sectionType == DocumentApp.ElementType.HEADER_SECTION ||
       sectionType == DocumentApp.ElementType.FOOTER_SECTION));

    func(section, i, isFirstPageSection);
  }
}
票数 9
EN

Stack Overflow用户

发布于 2013-09-13 13:11:10

我正在玩,并加入了@Mogsdad的回答 --这是一个非常复杂的版本:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var _ = Underscorejs.load(); // loaded via http://googleappsdeveloper.blogspot.com/2012/11/using-open-source-libraries-in-apps.html, rolled my own
var ui = DocumentApp.getUi();

// #region --------------------- Utilities -----------------------------

var gDocsHelper = (function(P, un) {
  // heavily based on answer https://stackoverflow.com/a/18731628/1037948

  var updatedLinkText = function(link, offset) {
    return function() { return 'Text: ' + link.getText().substring(offset,100) + ((link.getText().length-offset) > 100 ? '...' : ''); }
  }

  P.updateLink = function updateLink(link, oldText, newText, start, end) {
    var oldLink = link.getLinkUrl(start);

    if(0 > oldLink.indexOf(oldText)) return false;

    var newLink = oldLink.replace(new RegExp(oldText, 'g'), newText);
    link.setLinkUrl(start || 0, (end || oldLink.length), newLink);
    log(true, "Updating Link: ", oldLink, newLink, start, end, updatedLinkText(link, start) );

    return { old: oldLink, "new": newLink, getText: updatedLinkText(link, start) };
  };

  // moving this reused block out to 'private' fn
  var updateLinkResult = function(text, oldText, newText, link, urls, sidebar, updateResult) {
    // and may as well update the link while we're here
    if(false !== (updateResult = P.updateLink(text, oldText, newText, link.start, link.end))) {
       sidebar.append('<li>' + updateResult['old'] + ' &rarr; ' + updateResult['new'] + ' at ' + updateResult['getText']() + '</li>'); 
    }

    urls.push(link.url); // so multiple links get added to list
  };

  P.updateLinksMenu = function() {
    // https://developers.google.com/apps-script/reference/base/prompt-response
    var oldText = ui.prompt('Old link text to replace').getResponseText();
    var newText = ui.prompt('New link text to replace with').getResponseText();

    log('Replacing: ' + oldText + ', ' + newText);
    var sidebar = gDocUiHelper.createSidebar('Update All Links', '<h3>Replacing</h3><p><code>' + oldText + '</code> &rarr; <code>' + newText + '</code></p><hr /><ol>');

    // current doc available to script
    var doc = DocumentApp.getActiveDocument().getBody();//.getActiveSection();

    // Search until a link is found
    var links = P.findAllElementsFor(doc, function(text) {
      var i = -1, n = text.getText().length, link = false, url, urls = [], updateResult;

      // note: the following only gets the FIRST link in the text -- while(i < n && !(url = text.getLinkUrl(i++)));

      // scan the text element for links
      while(++i < n) {

        // getLinkUrl will continue to get a link while INSIDE the stupid link, so only do this once
        if(url = text.getLinkUrl(i)) {
          if(false === link) {
            link = { start: i, end: -1, url: url };
            // log(true, 'Type: ' + text.getType(), 'Link: ' + url, function() { return 'Text: ' + text.getText().substring(i,100) + ((n-i) > 100 ? '...' : '')});
          }
          else {
            link.end = i; // keep updating the end position until we leave
          }
        }
        // just left the link -- reset link tracking
        else if(false !== link) {
          // and may as well update the link while we're here
          updateLinkResult(text, oldText, newText, link, urls, sidebar);
          link = false; // reset "counter"
        }

      }

      // once we've reached the end of the text, must also check to see if the last thing we found was a link
      if(false !== link) updateLinkResult(text, oldText, newText, link, urls, sidebar);

      return urls;
    });

    sidebar.append('</ol><p><strong>' + links.length + ' links reviewed</strong></p>');
    gDocUiHelper.attachSidebar(sidebar);

    log(links);
  };

  P.findAllElementsFor = function(el, test) {
    // generic utility function to recursively find all elements; heavily based on https://stackoverflow.com/a/18731628/1037948

    var results = [], searchResult = null, i, result;
    // https://developers.google.com/apps-script/reference/document/body#findElement(ElementType)
    while (searchResult = el.findElement(DocumentApp.ElementType.TEXT, searchResult)) {
      var t = searchResult.getElement().editAsText(); // .asParagraph()

      // check to add to list
      if(test && (result = test(t))) {
        if( _.isArray(result) ) results = results.concat(result); // could be big? http://jsperf.com/self-concatenation/
        else results.push(result);
      }
    }
    // recurse children if not plain text item
    if(el.getType() !== DocumentApp.ElementType.TEXT) {
      i = el.getNumChildren();

      var result;
      while(--i > 0) {
        result = P.findAllElementsFor(el.getChild(i));
        if(result && result.length > 0) results = results.concat(result);
      }
    }

    return results;
  };

  return P;  
})({});

// really? it can't handle object properties?
function gDocsUpdateLinksMenu() {
  gDocsHelper.updateLinksMenu();
}

gDocUiHelper.addMenu('Zaus', [ ['Update links', 'gDocsUpdateLinksMenu'] ]);

// #endregion --------------------- Utilities -----------------------------

为了完整起见,我在下面包括用于创建菜单、侧边栏等的“额外”实用程序类:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
var log = function() {
  // return false;

  var args = Array.prototype.slice.call(arguments);

  // allowing functions delegates execution so we can save some non-debug cycles if code left in?

  if(args[0] === true) Logger.log(_.map(args, function(v) { return _.isFunction(v) ? v() : v; }).join('; '));
  else
    _.each(args, function(v) {
      Logger.log(_.isFunction(v) ? v() : v);
    });
}

// #region --------------------- Menu -----------------------------

var gDocUiHelper = (function(P, un) {

  P.addMenuToSheet = function addMenu(spreadsheet, title, items) {
    var menu = ui.createMenu(title);
    // make sure menu items are correct format
    _.each(items, function(v,k) {
      var err = [];

      // provided in format [ [name, fn],... ] instead
      if( _.isArray(v) ) {
        if ( v.length === 2 ) {
          menu.addItem(v[0], v[1]);
        }
        else {
          err.push('Menu item ' + k + ' missing name or function: ' + v.join(';'))
        }
      }
      else {
        if( !v.name ) err.push('Menu item ' + k + ' lacks name');
        if( !v.functionName ) err.push('Menu item ' + k + ' lacks function');

        if(!err.length) menu.addItem(v.name, v.functionName);
      }

      if(err.length) {
        log(err);
        ui.alert(err.join('; '));
      }

    });

    menu.addToUi();
  };

  // list of things to hook into
  var initializers = {};

  P.addMenu = function(menuTitle, menuItems) {
    if(initializers[menuTitle] === un) {
      initializers[menuTitle] = [];
    }
    initializers[menuTitle] = initializers[menuTitle].concat(menuItems);
  };

  P.createSidebar = function(title, content, options) {
    var sidebar = HtmlService
    .createHtmlOutput()
    .setTitle(title)
    .setWidth( (options && options.width) ? width : 350 /* pixels */);

    sidebar.append(content);

    if(options && options.on) DocumentApp.getUi().showSidebar(sidebar);
    // else { sidebar.attach = function() { DocumentApp.getUi().showSidebar(this); }; } // should really attach to prototype...

    return sidebar;
  };

  P.attachSidebar = function(sidebar) {
    DocumentApp.getUi().showSidebar(sidebar);
  };


  P.onOpen = function() {
    var spreadsheet = SpreadsheetApp.getActive();
    log(initializers);
    _.each(initializers, function(v,k) {
      P.addMenuToSheet(spreadsheet, k, v);
    });
  };

  return P;
})({});

// #endregion --------------------- Menu -----------------------------

/**
 * A special function that runs when the spreadsheet is open, used to add a
 * custom menu to the spreadsheet.
 */
function onOpen() {
  gDocUiHelper.onOpen();
}
票数 1
EN

Stack Overflow用户

发布于 2017-08-05 10:56:02

很难找到莫茨爸爸的解决方案。具体来说,它错过了结束其父元素的链接,因此没有一个尾随的非链接字符来终止它。我实现了一些解决这个问题的方法,并返回了一个标准范围元素。在这里分享,以防有人发现它有用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
function getAllLinks(element) {
  var rangeBuilder = DocumentApp.getActiveDocument().newRange();

  // Parse the text iteratively to find the start and end indices for each link
  if (element.getType() === DocumentApp.ElementType.TEXT) {
    var links = [];
    var string = element.getText();
    var previousUrl = null; // The URL of the previous character 
    var currentLink = null; // The latest link being built
    for (var charIndex = 0; charIndex < string.length; charIndex++) {
      var currentUrl = element.getLinkUrl(charIndex);
      // New URL means create a new link
      if (currentUrl !== null && previousUrl !== currentUrl) {
        if (currentLink !== null) links.push(currentLink);
        currentLink = {};
        currentLink.url = String(currentUrl);
        currentLink.startOffset = charIndex;
      }
      // In a URL means extend the end of the current link
      if (currentUrl !== null) {
        currentLink.endOffsetInclusive = charIndex;
      }
      // Not in a URL means close and push the link if ready
      if (currentUrl === null) {
        if (currentLink !== null) links.push(currentLink);
        currentLink = null;
      }
      // End the loop and go again
      previousUrl = currentUrl;
    }
    // Handle the end case when final character is a link
    if (currentLink !== null) links.push(currentLink);
    // Convert the links into a range before returning
    links.forEach(function(link) {
      rangeBuilder.addElement(element, link.startOffset, link.endOffsetInclusive);
    });
  }

  // If not a text element then recursively get links from child elements
  else if (element.getNumChildren) {
    for (var i = 0; i < element.getNumChildren(); i++) {
      rangeBuilder.addRange(getAllLinks(element.getChild(i)));
    }
  }

  return rangeBuilder.build();
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18727341

复制
相关文章
图像翻转
算法:图像翻转是用于对图像进行镜像翻转处理。水平翻转用于对图像进行水平方向上镜像处理;垂直翻转用于对图像进行垂直方向上镜像处理。对角翻转用于对水平方向上和垂直方向上镜像处理。图像翻转不是图像反转。图像翻转可以通过图像旋转获得。图像翻转应用在图像增强、网页设计等领域。
裴来凡
2022/05/28
1.3K0
图像翻转
opencv图像翻转、图像旋转
淼学派对
2023/10/14
3330
opencv图像翻转、图像旋转
LeetCode - 翻转图像
原题地址:https://leetcode-cn.com/problems/flipping-an-image/
晓痴
2019/08/06
5500
LeetCode - 翻转图像
SilverLight实现绕垂直中线翻转效果
主要通过StoryBoard来控制PlaneProjection的RotationY来实现
MJ.Zhou
2022/05/07
4430
Silverlight制作逐帧动画
打算用sl来制作一个游戏,我曾经有flash开发游戏的经验.现在想用sl来做.打算记录下我开发游戏探索的过程. 打开http://www.emu-zone.org/www3/host/emugif/
用户1172164
2018/01/16
1K0
Silverlight制作逐帧动画
Silverlight初级教程-动画
Silverlight初级教程 动画 在这一节里将要用到在“认识工作区”中提到的时间抽了。这里将和学习flash的过程一样做一个最简单的动画。 silverlight中的动画是以“storyboard”的形式表现出来。“storyboard”你可以认为是flash中的时间轴。  flash中只有一个时间轴,而在silverlight中却可以有多个“storyboard”。 在flash中是有一个帧频的概念的,例每秒12帧、每秒30帧等。而在silverlight中则是完全的基于时间的,例
用户1172164
2018/01/16
6190
用silverlight做动画-相机
用silverlight做动画-相机 适合初学者学习 做一个相机的动画 和做flash动画一样,准备好素材 将素材放入项目中 开始正式制作前为了方便以后重用,就把这个动画做成usercontrol(和flash中的‘MovieClip’概念是一样的) 创建一个UserControl 图片放入舞台中并且调整好位置 用钢笔在最上层画一个图像做遮罩使用,和flash中的概念一样。 讲上边的小图放入一个Canvas容器中 选中Canvas容器和刚刚画出的图形制作遮罩 将刚刚遮罩过的Canvas容器
用户1172164
2018/03/01
8610
CSS骰子翻转动画
---- <!doctype html> <html> <head> <meta charset="utf-8"> <title>CSS3骰子翻转动画</title> <style> body { font-family: Avenir, Helvetica, Arial, sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; text-align: center; col
我不是费圆
2020/12/17
5940
OpenCV之图像翻转
python代码: import cv2 as cv import numpy as np src = cv.imread("./test.png") cv.namedWindow("input", cv.WINDOW_AUTOSIZE) cv.imshow("input", src) # X Flip 倒影 dst1 = cv.flip(src, 0); cv.imshow("x-flip", dst1); # Y Flip 镜像 dst2 = cv.flip(src, 1); cv.imshow(
MachineLP
2021/07/19
3040
OpenCV之图像翻转
[Python图像处理] 六.图像缩放、图像旋转、图像翻转与图像平移
该系列文章是讲解Python OpenCV图像处理知识,前期主要讲解图像入门、OpenCV基础用法,中期讲解图像处理的各种算法,包括图像锐化算子、图像增强技术、图像分割等,后期结合深度学习研究图像识别、图像分类、目标检测应用。
Eastmount
2021/12/02
5.8K0
[Python图像处理] 六.图像缩放、图像旋转、图像翻转与图像平移
LeetCode 832. 翻转图像
水平翻转图片就是将图片的每一行都进行翻转,即逆序。例如,水平翻转 [1, 1, 0] 的结果是 [0, 1, 1]。
freesan44
2020/06/03
2300
OpenCV图像处理(八)---图像缩放VS图像翻转
牛顿第三运动定律的常见表述是:相互作用的两个物体之间的作用力和反作用力总是大小相等,方向相反,作用在同一条直线上。该定律是由艾萨克·牛顿在1687年于《自然哲学的数学原理》一书中提出的。牛顿第三运动定律和第一、第二定律共同组成了牛顿运动定律,阐述了经典力学中基本的运动规律。
用户5410712
2022/06/01
7620
OpenCV图像处理(八)---图像缩放VS图像翻转
[UWP]使用CompositionAPI的翻转动画
在 使用GetAlphaMask和ContainerVisual制作长阴影(Long Shadow) 这篇文章里我介绍了一个包含长阴影的番茄钟,这个番茄钟在状态切换时用到了翻转动画,效果如上所示,还用到了弹簧动画,可以看到翻转后有点回弹。本来打算自己这个动画效果写的,但火火已经写好了这个FlipSide控件,Github地址在这里,这篇文章就介绍下这个控件的部分原理。
dino.c
2019/11/14
7610
LeetCode 0832. 翻转图像
水平翻转图片就是将图片的每一行都进行翻转,即逆序。例如,水平翻转 1, 1, 0 的结果是 0, 1, 1。
Yano_nankai
2021/02/23
2040
LeetCode 0832. 翻转图像
cv图像翻转,EmguCV图像旋转「建议收藏」
i use this codeprivate void button12_Click(object sender, EventArgs e)
全栈程序员站长
2022/07/25
7270
Qt官方示例-Qml翻转动画
❝示例演示翻转动画(沿着x轴或y轴翻转动画)。❞ 主要代码: import QtQuick 2.0 Flipable { id: container property alias source: frontImage.source property bool flipped: true property int xAxis: 0 property int yAxis: 0 property int angle: 0 width: front.
Qt君
2023/03/17
7630
Qt官方示例-Qml翻转动画
Python图像增强(翻转和旋转)
在训练神经网络的时候,经常需要对原始图像做各种各样的增强来增加数据量,最常见的也就是旋转和翻转操作了,实现这两种操作也多种多样,本博客就是来探究不同操作带来的结果
用户7886150
2021/01/05
2.4K0
TensorFlow 图像预处理(二) 图像翻转,图像色彩调整
本文介绍了TensorFlow中的图像预处理方法,包括图像翻转、图像色彩调整、图像归一化等操作。通过这些操作,可以有效地提高图像的质量和可用性。同时,文章还介绍了如何使用TensorFlow进行图像增强和预处理,以便在训练模型时获得更好的性能。
chaibubble
2018/01/02
1.9K0
TensorFlow 图像预处理(二) 图像翻转,图像色彩调整
09:图像旋转翻转变换
09:图像旋转翻转变换 总时间限制: 1000ms 内存限制: 65536kB描述 给定m行n列的图像各像素点灰度值,对其依次进行一系列操作后,求最终图像。 其中,可能的操作及对应字符有如下四种: A:顺时针旋转90度; B:逆时针旋转90度; C:左右翻转; D:上下翻转。 输入第一行包含两个正整数m和n,表示图像的行数和列数,中间用单个空格隔开。1 <= m <= 100, 1 <= n <= 100。 接下来m行,每行n个整数,表示图像中每个像素点的灰度值,相邻两个数之间用单个空格隔开。灰度值
attack
2018/04/03
1.8K0
Leetcode#832. Flipping an Image(翻转图像)
水平翻转图片就是将图片的每一行都进行翻转,即逆序。例如,水平翻转 [1, 1, 0] 的结果是 [0, 1, 1]。
武培轩
2018/09/28
3780

相似问题

翻转动画后Silverlight屏幕模糊

10

Android翻转图像动画

20

CSS动画:如何翻转图像?

226

jQuery动画图像翻转

33

在动画过程中翻转图像

123
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文