ImportXML using Google Spreadsheets and Save Results Over-Time - google-apps-script

I'm using ImportXML on Google Spreadsheets to scrape YouTube search results and capture the rankings of a video I'm tracking.
=IMPORTXML("https://www.youtube.com/results?search_query=control+theory&page=1"),"//div[#class='yt-lockup-byline']//a")
The scraping works well (and I get all the results for the first page). I then lookup the position of the YouTube results and save that in a cell.
However, the issue I'm having is when I'm trying to track that ranking over-time. Not sure how to do this but I'm looking for a way to run the =ImportXML function every day (is there a refresh command?) and save the result from that cell in a new row each time the function is run.
Any ideas?

importXML output is automatically updated (about every two hours), but you still need a script to save the past values somewhere: usually, on another sheet. Enter the following using Tools > Script Editor, adjusting the sheet names and the cell with the value of interest. Set it to run daily or weekly using Resources > Current project's triggers.
function storeValue() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1'); // where importXML is
var value = sheet.getRange("B3").getValue(); // where the cell of interest is
var sheet2 = ss.getSheetByName('Sheet2'); // where to store the data
var height = sheet2.getLastRow();
sheet2.insertRowAfter(height);
sheet2.getRange(height+1, 1, 1, 2).setValues([[new Date(), value]]);
}
At each run, the script appends two cells at the bottom of Sheet2: one with the timestamp (should be formatted as such), one with the value corresponding to that timestamp.
A version for storing a range of values such as B3:B7:
function storeValue() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var values = sheet.getRange("B3:B7").getValues()[0];
var sheet2 = ss.getSheetByName('Sheet2'); // where to store the data
var height = sheet2.getLastRow();
sheet2.insertRowAfter(height);
sheet2.getRange(height+1, 1, 1, 6).setValues([[new Date()].concat(values)]);
// setting 6 cells instead of 2 on the previous line
}

Related

Google app script to cut and paste a range from a google sheet to another google sheet

I'm trying to put together a script that cuts a range from a google spreadsheet and pastes it to another spreadsheet. The script works fine as long as the source sheet is the first sheet in my document. How can I fix it so that the script grabs the right sheet no matter where it is in my document?
I'm very very new at this and mostly used code I found online. Probably made a very obvious mistake..
function CopyRangeTo_OtherSheet() {
var sss = SpreadsheetApp.openById('1O1gML-IeOkx6n6RhCavay6Zg9zflalcVs0qkB2yu7bE');
var ss = sss.getSheetByName('Sheet1');
var range = ss.getRange(2,4,sss.getLastRow()-1,9);
var data = range.getValues();
var tss = SpreadsheetApp.openById('1WHoETM3QxV166ndnPW4Qv37Y6SBGPVw-CcEQxNpw0Co');
var ts = tss.getSheetByName('Sheet3');
ts.getRange(ts.getLastRow()+1, 1,50,9).setValues(data);
var spread = SpreadsheetApp.openById('1O1gML-IeOkx6n6RhCavay6Zg9zflalcVs0qkB2yu7bE');
var sheet = spread.getSheetByName("Sheet1");
sheet.getRange("D2:H").clearContent();
}
The number of rows will be different in each sheet. .getLastRow() on a spreadsheet class only returns the last row in first sheet. getLastRow of sheet instead
ss.getLastRow()
Your line var ss = sss.getSheetByName('Sheet1'); implies that you take the sheet with the name Sheet1.
This is the sheet you use to obtain your values from range and subsequently copy them.
If you want to copy / cut and paste values from a different sheet, simply paste the correct name of the sheet in var ss = sss.getSheetByName('PASTE HERE THE NAME OF THE SHEET');
Small annotation:
If the range you want to clear is the same from which you copied the
values, you do not need to define it again. You can simply replace the
lines
var spread = SpreadsheetApp.openById('1O1gML-IeOkx6n6RhCavay6Zg9zflalcVs0qkB2yu7bE');
var sheet = spread.getSheetByName("Sheet1");
sheet.getRange("D2:H").clearContent();
through
range.clearContents();
Also note:
setValues() only works if the range into which the values shall be set has the same dimension like the value range, i.e. the original range.
If your original range is 2,4,ss.getLastRow()-1,9, then the number of rows is ss.getLastRow()-1 and the number of columns 9.
Thus, you need to define
ts.getRange(ts.getLastRow()+1, 1,ss.getLastRow()-1,9).setValues(data);
to adapt the row number dynamically.

How to make google sheets index from values in column and hyperlink those to sheets

I have a bunch of data I want to put in to multiple sheets, and to do it manually would take time and I would like to learn scripting too.
So say I have a sheet with the states in one column.
I would like to have a script make new sheets based off the values of that column, and make a hyperlink to those sheets, and sort the sheets alphabetically.
In each sheet, I need to have the A1 cell the same name as the sheet.
Here is an example of states
Any suggestions would be helpful
Edit:
This is code that can make sheets based on the values of the columns.
function makeTabs() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var last = sheet.getLastRow();//identifies the last active row on the sheet
//loop through the code until each row creates a tab.
for(var i=0; i<last; i++){
var tabName = sheet.getRange(i+2,1).getValue();//get the range in column A and get the value.
var create = ss.insertSheet(tabName);//create a new sheet with the value
}
}
(note the "sheet.getRange(i+2,1" assumes a header, so pulls from the first column, starting on the second row)
I still need to:
Add a hyper link in the index sheet to the State's sheet: example: A2 on the Index sheet
would be =HYPERLINK("#gid=738389498","Alabama")
Also I need the A1 cell of the State's page to have the same info as
the index. example: Alabama's A1 cell would be =Index!A2
You could take a look at this script:
function createSheets(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var indexSheet = ss.getSheetByName('Index');
var indexSheetRange = indexSheet.getDataRange();
var values = indexSheetRange.getValues();
var templateSheet = ss.getSheetByName('TEMPLATE_the_state');
templateSheet.activate();
var sheetIds = [],
state,
sheetId,
links = [];
for (var i = 1 ; i < values.length ; i++){
state = values[i][0];
sheetId = undefined;
try{
var sheet = ss.insertSheet(state, {template: templateSheet});
SpreadsheetApp.flush();
sheet.getRange("A1:B1").setValues([['=hyperlink("#gid=0&range=A' +(i+1)+'","go back to index")',state]]);
sheetId = sheet.getSheetId();
}
catch (e) { Logger.log('Sheet %s already exists ' , sheet)}
sheetIds.push([sheetId,state]);
}
sheetIds.forEach(function(x){
links.push(['=HYPERLINK("#gid='+x[0]+'&range=A1","'+x[1]+'")']);
});
indexSheet.getRange(2,1,links.length,links[0].length).setValues(links) // in this case it is clear to us from the outset that links[0].length is 1, so we could have written 1
}
Note that in my version, I created a template sheet from which to base all the state sheets from. This wasn't what you asked for, but I wanted to see what it would do.
The resulting sheet is here: https://docs.google.com/spreadsheets/d/1Rk00eXPzkfov5e3D3AKOVQA2UdvE5b8roG3-WeI4znE/edit?usp=sharing
Indeed, I was surprised at how long it took to create the full sheet with all the states - more than 250 secs. I looked at the execution log, which I have added to the sheet in its own tab. There it is plain to see that the code is quick, but sometimes (why only sometimes, I don't know) adding a new tab to the spreadsheet and/or flushing the formulas on the spreadsheet is very slow. I don't know how to speed it up. Any suggestions welcome. (I could try the Google Sheets API v4, probably would be much faster ... but that is much more work and tougher to do.)

Save data from one Google spreadsheet to another to create a database

I'm new to Google App Scripts and JavaScript as I work on the marketing side. I'm currently trying to create a database in Google Sheets to use in DataStudio as I want to blend data from various sources.
I have Google Sheets with a sheet to feed the data (through add-ons) and another sheet which I intend to use as the database. What I'm trying to do is to save data in the database, new data being saved at the end of the spreadsheet each time (I will set a time-based trigger when my script works). I've written a script using Google's documentation but it's only copying the first line of my Data sheet to the database. I would need it to copy all the content from the data sheet to the database sheet.
Here is the script I have for now :
// function to save data
function saveData() {
// starts with active sheet for data entry
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Data");
// collects values in data entry row
var url = sheet.getRange("Data!A1:A").getValues();
var follower_count = sheet.getRange("Data!B1:B").getValues();
var date = sheet.getRange("Data!C1:C").getValues();
// identifies and adds data to Database
var data1 = ss.getSheetByName("Database");
var aVals = data1.getRange("A1:A").getValues();
var aLastRow = aVals.filter(String).length;
Logger.log(aLastRow);
var newAttData = [[url,follower_count,date]];
Logger.log(newAttData);
data1.getRange(aLastRow +1,1,1,3).setValues(newAttData);
}
I've tried to change the range width and length in the last line but I always get an error:
Incorrect range height, was 1 but should be 3 (line 31, file "Code")
I've spent a lot of time on this but I can't get it to work. Any help would be great !
As Described in the documentation:
setValues(values)
Sets a rectangular grid of values (must match dimensions of this range).
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
// The size of the two-dimensional array must match the size of the range.
var values = [
[ "2.000", "1,000,000", "$2.99" ]
];
var range = sheet.getRange("B2:D2");
range.setValues(values);
Reference

How to define range that could vary in google apps script

I'm trying to make a copy of one spreadsheet using a (time driven) Google App Script. I found a very useful piece of code here, but my concern is that the range varies frequently (new rows are added). How can I copy the entire spreadsheet no matter if the range changes?. because as you can see in the code below I have defined the range, but when this changes the information out of the defined range will be discarded. What can I do?
Thanks in advance.
Here the code I'm using:
function saveAsSpreadsheet(){
var timestamp = new Date();
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var range = sheet.getRange('sheetName!A1:AO158');
sheet.setNamedRange('sheetName', range);
var TestRange = sheet.getRangeByName('sheetName').getValues();
Logger.log(TestRange);
var destFolder = DriveApp.getFolderById("folderID");
DriveApp.getFileById(sheet.getId()).makeCopy("FileName:"+ timestamp, destFolder);
}
If you need to get the range object defined by the first cell of column A and the last cell in column A that contains data, use this code:
var range = sheet.getRange(1, 1, sheet.getLastRow(), 1);
The getRange() method has several overloads and can accept numerical values defining the boundaries of the range as well. More on this here https://developers.google.com/apps-script/reference/spreadsheet/sheet#getRange(Integer,Integer)
If you need to copy everything, use getDataRange() method.

Bills sheet scripts

I am trying to create a spreadsheet to create bills. I have previously done this in Excel, and now I want to use Google Sheets, however I am having some problems.
I tried to create a function to be activated each time that a person clicks a specific range
I have to save the sheet with a specific name (for example the value of a A1 Row + A2 ROW) and save it into my Google drive account
I used importRange() to import the data of a Google Form, however if I make a copy of the sheet I have to give permission to display the data. (How can I do that but without the permission?)
Each of these is almost a question on it own. However, Here's an answer for your questions:
1) I tried to create a function that have to be activated each time that a person click a specific range
Use the onEdit function to perform whatever function you are wishing to perform. However, values will have to be changed in order to perform this function. There's no onClick() fucntion in GAS (As mentioned here). However, Here's a simple example for onEdit:
function onEdit()
{
var sheet = SpreadsheetApp.getActiveSheet();
var cell = sheet.getActiveCell();
var actRow = cell.getRow();
var actCol = cell.getColumn();
//Assuming you want the range to be between rows 5-11 and column 2-9
if (actRow >= 11 && actRow<=5 && actCol >=2 && actCol<=9)
{
//Implement your function
}
}
2) I have to save the sheet with a specific name ( for example the value of a A1 Row + A2 ROW) and save it into my google drive account
Here's a simple sample. You will have to modify it according to your use case but this should give you a good idea regarding how to do it:
function moveRows(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var numRows = sheet.getLastRow();
var sRange = sheet.getRange(2, 1, numRows, 1); //Leaving out header rows
var data = sRange.getValues();
var destFolder = DriveApp.getFolderById('*****');
var newSpSheet = SpreadsheetApp.create("Billing Target");
var newRows = "A"+(1+numRows);
var newRange = "A2:"+newRows; //Leaving out header rows
newSpSheet.getRange(newRange).setValues(data);
//If you need the file to be in a specific folder
DriveApp.getFileById(newSpSheet.getId()).makeCopy("Billing Target", destFolder);
}
3) I used import range to import the data of a google form however if a make a copy of the sheet y have to give permision to display the data (How can I do that but without the permision)
As far as I know, You can't do that without a permission.