2

I just started using Google App Script and wrote this script (mostly copy and paste) to check the status of my website and its pages.

Sometimes, mostly after opening the spreadsheet, but even if it is open for a while, the script starts very slow, needing half a minute per row to check the status. If I cancel and wait for a bit, it runs normal, taking about 1-2 seconds (in few cases 5 seconds) per row.

I can't really pin down if it is a problem on the webserver side, a problem of inefficiency in the script or something else. I even tried to run the script on a sunday at midnight, when there shouldn't be much stress on the webserver but the script didn't finish because it was running over 6 minutes.

function UpdateAllStatus()
{
var aSheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("URL   Status");

if (aSheet == null)
return;

var row = 4;
var url = aSheet.getRange(row, 2).getValue();

var options1 = {
contentType: "text/html ",
muteHttpExceptions: true
};

// I run the script in a loop until it hits the end of the row where I placed an "!" to show the script this is the end  
while (url != "!")
{

// There are gaps in the URL row, so the script skips to the next row
  if (url == "")
  {
  row++;
  url = aSheet.getRange(row, 2).getValue();
  }

//  if there is an URL in the row, the script gets the response code from the website
  else 
  {
  var response_code = UrlFetchApp.fetch(url, options1).getResponseCode().toString() ;
  var stext = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("URL Status").getRange(row, 1).getValue();
  var ttext = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Title").getRange(row, 1).getValue();
  var dtext = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Description").getRange(row, 1).getValue();

// if the response code is 404 the script writes the code into the according fields    
    if (response_code == "404")
    {
    aSheet.getRange(row, 3).setValue(response_code);
    aSheet.getRange(row, 4).setValue("#N/A");
    aSheet.getRange(row, 5).setValue("#N/A");
    aSheet.getRange(row, 6).setValue("#N/A");

// here the script checks, if the rows in the sheets "Title" and "Description" are the same as in the "URL Status" sheet, just really simple check to find if there is a row too much or missing. The check needs to be done, since the other sheets get their url from "URL Status" and would check the wrong title or description if one row was missing/too much/wrong order.

    if(stext==ttext && stext==dtext)
    {
    aSheet.getRange(row, 7).setBackground('#00FF00');
    }
    else
    {
    aSheet.getRange(row, 7).setBackground('#FF0000');
    }
    row++;
    url = aSheet.getRange(row, 2).getValue();
    }

//if the response code is not 404, the script gets the content from the website to fetch and write the response code, the robots text, view_group and item_group

      else 
      {
      var code = UrlFetchApp.fetch(url, options1).getContentText();
        if (code != "")
        {
        var i1=code.indexOf('<meta name="robots" content="');
        var i2=code.indexOf('">',i1);
        var i3=code.indexOf('"hd_prev_view_group" value="');
        var i4=code.indexOf('">',i3);
        var i5=code.indexOf('"hd_prev_item_group" value="');
        var i6=code.indexOf('">',i5);
        var robottext=code.substring(i1+29,i2);
        var viewtext=code.substring(i3+28,i4);
        var itemtext=code.substring(i5+28,i6);
        aSheet.getRange(row, 3).setValue(response_code);
        aSheet.getRange(row, 4).setValue(robottext);
        aSheet.getRange(row, 5).setValue(viewtext);
        aSheet.getRange(row, 6).setValue(itemtext);  

        if(stext==ttext && stext==dtext)
        {
        aSheet.getRange(row, 7).setBackground('#00FF00');
        }
        else
        {
        aSheet.getRange(row, 7).setBackground('#FF0000');
        }
        row++;
        url = aSheet.getRange(row, 2).getValue();
        }
// if the url is available (no 404) but the html-page is empty, it write these values in the according row (might be rare but could happen          
        else 
        {
        aSheet.getRange(row, 3).setValue("#N/A");
        aSheet.getRange(row, 4).setValue("#N/A");
        aSheet.getRange(row, 5).setValue("#N/A");
        aSheet.getRange(row, 6).setValue("#N/A");
        if(stext==ttext && stext==dtext)
        {
        aSheet.getRange(row, 7).setBackground('#00FF00');
        }
        else
        {
        aSheet.getRange(row, 7).setBackground('#FF0000');
        }
        row++;
        url = aSheet.getRange(row, 2).getValue();
        }
  }
}
}    
return;
}

I'm sure there is plenty of space for improvement as I'm a total Noob in writing scripts. Some suggestions and help would be really appreciated!

Here is how it looks like after the script has run: Sheet with URL status after the script has run As you can see, product 9 and 13 are miss-matched, but it's just of different spelling. If the product was missing in the other sheets, everything afterwards would be red.

In any case, merry christmas

1 Answer 1

1

The first thing to do is to minimize the number of API calls interacting with the sheet, such as getValue() and setValue(). Think of the script as a factory and the sheet as its warehouse across the town. If a worker has to drive to warehouse every time they need a detail, they won't get much done. Instead, they should get all the materials they need at the beginning, and turn in all finished product at the end.

So, instead of the multiple .getRange(row, column).getValue() and .getRange(row, column).setValue(), there should be

  1. one call to get values: e.g., sheet.getDataRange().getValues() which gets you all the data in the sheet. (Or, if you know a specific range you'll be working in, get all values from there). Then loop over this double array, analyzing its contents
  2. one call to set new values with .setValues(), from a double array of the same size (like [['This year', 2015], ['Next year', 2016]]
  3. one call to set backgrounds with .setBackgrounds(), also from a double array.

In between 1 and 2, you work purely in JavaScript, processing one array and forming two others.

Sometimes you can't have just one array in 2 or 3, because, e.g., setValues would overwrite some formulas in the middle of your data. In this case, split the data range into groups of columns that can be handled at once. You will still have only a few setValues calls.

2
  • I saw that it is possible to process an array of data instead if a single cell at a time and i understand the concept (I think) but i can't wrap my head around how to re-arrange my script to do so. I never done any programming and this is my first Javascript.
    – GaRe
    Dec 23, 2015 at 7:50
  • Wrong place to start, then. See Codecademy course in JavaScript, paying particular attention to arrays. Don't expect anyone here to rewrite code for you.
    – user79865
    Dec 23, 2015 at 15:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.