Created
December 2, 2011 10:35
-
-
Save mhawksey/1422745 to your computer and use it in GitHub Desktop.
Google Apps Script to get Friend Follower information from Google+ (Plus) More info http://mashe.hawksey.info/2011/12/google-plus-network-info-to-nodexl/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// modification of Tony Hirst's @psychemedia https://gist.github.com/1288134 | |
// by @mhawksey [https://plus.google.com/u/0/114662816634467534305/about] | |
//# cache time in seconds; if a file is cached and not older than cachetime, that data will be used | |
var defCache=36000; | |
//# Do we want to map the social connections between the friends of the friends ('fr') of the | |
//# target account(s) or the friends of the followers ('fo') of the target account(s) | |
var typ='fr'; | |
var edgeSheetName = "Links"; | |
var verticesSheetName = "Nodes"; | |
var doc = SpreadsheetApp.getActiveSpreadsheet(); | |
var sheetE = doc.getSheetByName(edgeSheetName); | |
var sheetV = doc.getSheetByName(verticesSheetName); | |
var gPlusIDs=[ScriptProperties.getProperty("GooglePlusID")]; | |
var batchSize = 50; | |
function onOpen(){ | |
var menuEntries = []; | |
// When the user clicks on "addMenuExample" then "Menu Entry 1", the function function1 is executed. | |
menuEntries.push({name: "Set Collection ID", functionName: "start"}); | |
menuEntries.push({name: "Start/Continue Collection", functionName: "build"}); | |
menuEntries.push({name: "Reset", functionName: "reset"}); | |
doc.addMenu("Google+", menuEntries); | |
} | |
function start(){ | |
var gID = Browser.inputBox("Visit https://plus.google.com/me and copy paste the url here (or enter a 21 digit user id):"); | |
gID = gID.match(/[0-9]{21}/, "g"); | |
if (gID.length == 0){ | |
Browser.msgBox("Oops - somthing wrong with that ID"); | |
} else { | |
ScriptProperties.setProperty("GooglePlusID",gID[0]); | |
//ScriptProperties.setProperty("debug",Browser.msgBox("Google+ ID set. Start collection", Browser.Buttons.OK_CANCEL)); | |
if (Browser.msgBox("Google+ ID set. Start collection", Browser.Buttons.OK_CANCEL)=="ok"){ | |
build(); | |
} | |
} | |
} | |
function reset(){ | |
ScriptProperties.setProperty("GooglePlusID",""); | |
ScriptProperties.setProperty("cursor",1); | |
sheetE.clearContents(); | |
sheetV.clearContents(); | |
doc.toast("Reset"); | |
} | |
function build(){ | |
if (!ScriptProperties.getProperty("GooglePlusID") || ScriptProperties.getProperty("GooglePlusID") == ""){ | |
start(); | |
return; | |
} | |
var gPlusIDs=[ScriptProperties.getProperty("GooglePlusID")]; | |
doc.toast("Started Collection"); | |
var part = parseInt(ScriptProperties.getProperty("cursor")); | |
var oidNames={}; | |
var nodes = []; | |
var links = []; | |
var doneList = []; | |
for (id in gPlusIDs){ | |
var test = gPlusIDs[id]; | |
Logger.log('Top level run: getting '+typ+','+id+','+getuserName(gPlusIDs[id])); | |
oidNames[gPlusIDs[id]]=getuserName(gPlusIDs[id]); | |
var oids=getoids(gPlusIDs[id], typ); | |
oids = oids.split(","); | |
var oidNames=getUserNames(oids,oidNames); | |
for (oidID in oidNames){ | |
// if (oidID.length>20) { | |
nodes.push([oidID,oidNames[oidID][0],oidNames[oidID][1]]); | |
if (part === 1) { | |
links.push([gPlusIDs[id],oidID]); | |
//links.push([oidNames[gPlusIDs[id]][0],oidNames[oidID]]); | |
} | |
} | |
sheetV.getRange(1,1,nodes.length,3).setValues(nodes); | |
var count=1; | |
var nextToDo = false; | |
var fsize=oids.length; | |
if (fsize>batchSize){ | |
if (!ScriptProperties.getProperty("cursor")){ | |
ScriptProperties.setProperty("cursor", 1); | |
} | |
if (ScriptProperties.getProperty("cursor") == "end"){ | |
ScriptProperties.setProperty("cursor", 1); | |
} | |
var todo = chunks(oids,batchSize); | |
part = parseInt(ScriptProperties.getProperty("cursor")); | |
var oids = todo[part-1]; | |
nextToDo = true; | |
} | |
for (oid in oids){ | |
if (typeof oidNames[oids[oid]] != undefined){ | |
var msg = 'Sub-level run: getting '+count+' '+'of'+' '+oids.length; | |
Logger.log(msg); | |
doc.toast(msg); | |
var foids=getoids(oids[oid]); | |
if (foids != null){ | |
for (oidName in oidNames){ | |
var searchKey = new RegExp(oidName,"gi"); | |
if (foids.search(searchKey) != -1){ | |
links.push([oids[oid],oidName]); | |
//links.push([oidNames[oids[oid]][0],oidNames[oidName][0]]); | |
} | |
} | |
} else { | |
Logger.log("Oops - problem with "+oidNames[oids[oid]]); | |
} | |
doneList.push(["X"]); | |
count=count+1; | |
} | |
} | |
sheetE.getRange(sheetE.getLastRow()+1,1,links.length,2).setValues(links); | |
sheetV.getRange((part-1)*batchSize+1,4,doneList.length,1).setValues(doneList); | |
if (nextToDo){ | |
part++ | |
if (part<=todo.length){ | |
ScriptProperties.setProperty("cursor", part); | |
Browser.msgBox("Still more data to collect. Run Google+ > Start/Continue Collection for results "+(part)+" of "+todo.length); | |
} else { | |
ScriptProperties.setProperty("cursor", "end"); | |
Browser.msgBox("All done!"); | |
} | |
} | |
} | |
} | |
function getuserName(oid){ | |
//http://socialgraph.apis.google.com/lookup?q=https%3A%2F%2Fplus.google.com%2F104253436939071070140%2F&pretty=1&callback= | |
var url='http://socialgraph.apis.google.com/lookup?q=https%3A%2F%2Fplus.google.com%2F'+oid+'%2F&callback='; | |
var xdata = getGenericCachedData(url); | |
var data = Utilities.jsonParse(xdata); | |
var uid='http://profiles.google.com/'+oid; | |
if (uid in data['nodes']){ | |
var name=[data['nodes'][uid]['attributes']['fn'],data['nodes'][uid]['attributes']['photo']] | |
} else { | |
var name=''; | |
} | |
return name | |
} | |
function getUserNames(oids,namelookup){ | |
var oidlookup=[]; | |
for (oid in oids){ | |
if (!(oids[oid] in namelookup) && !(oids[oid] in oidlookup)){ | |
oidlookup.push(oids[oid]); | |
} | |
} | |
var oidblocks = chunks(oidlookup,15); | |
for (oidblock in oidblocks){ | |
var encoids='%2Chttps%3A%2F%2Fplus.google.com%2F'+oidblocks[oidblock].join('%2Chttps%3A%2F%2Fplus.google.com%2F'); | |
//print encoids | |
var url='http://socialgraph.apis.google.com/lookup?q=https%3A%2F%2Fplus.google.com%2F'+encoids+'%2F&callback='; | |
//data=json.load(urllib2.urlopen(url)) | |
var xdata=getGenericCachedData(url); | |
try { | |
var data=Utilities.jsonParse(xdata); | |
} catch(e) { | |
Logger.log(e); | |
data={}; | |
data['nodes']=[]; | |
} | |
for (oid in oidblocks[oidblock]){ | |
var uid='http://profiles.google.com/'+oidblocks[oidblock][oid]; | |
if (uid in data['nodes']){ | |
try { | |
namelookup[oidblocks[oidblock][oid]]=[data['nodes'][uid]['attributes']['fn'],data['nodes'][uid]['attributes']['photo']]; | |
} catch(e) { | |
Logger.log(e); | |
namelookup[oidblocks[oidblock][oid]]=['','']; | |
} | |
} else { | |
namelookup[oidblocks[oidblock][oid]]=['','']; | |
} | |
} | |
} | |
return namelookup; | |
} | |
//--- | |
//based on http://html5example.net/entry/tutorial/simple-python-google-plus-api | |
function getoids(oid,typIn){ | |
var oids = []; | |
var typ = typIn || 'fr'; | |
if (typ=='fr'){ | |
var url='https://plus.google.com/u/0/_/socialgraph/lookup/visible/?o=%5Bnull%2Cnull%2C%22'+oid+'%22%5D&rt=j'; | |
} else if (typ=='fo'){ | |
var url='https://plus.google.com/u/0/_/socialgraph/lookup/incoming/?o=%5Bnull%2Cnull%2C%22'+oid+'%22%5D&n=1000&rt=j'; | |
} else { | |
return; //exit(-1) | |
} | |
//req = urllib2.Request(url) | |
//response = urllib2.urlopen(req) | |
//data = response.read() | |
Logger.log('Fetching '+url); | |
oids = getCachedDataOids(url) | |
//#print data | |
//oids.split(); | |
//oids = list(set(oids)) | |
return oids; | |
} | |
function getCachedDataOids(url) { | |
var cache = CacheService.getPublicCache(); // initialise | |
var oids = cache.get("f"+url.substr(0,240)); // get data if any (url trimmed to prevent error) | |
if (oids == null || oids == "") { // if null or empty means there is no cached data or last fetch failed | |
var requestData = {"method":"GET", "headers": { "User-Agent": "http://docs.google.com"}}; // prepare request | |
var response = UrlFetchApp.fetch(url, requestData); // try fetch | |
if (response.getResponseCode() == 200) { // if response then | |
var resp = response.getContentText(); // get response text | |
var reobg = new RegExp("\\d{21}","g"); // only interested in Google Plus user ID so prepare regexp | |
oids = resp.match(reobg); // get an array of ids | |
if (!oids == null){ // if something stringify it | |
oids = oids.join(","); | |
} | |
try { | |
oids = oids.join(","); | |
cache.put("f"+url.substr(0,240), oids, defCache); // try and put it in cache | |
} catch(e){ | |
Logger.log(e+url) | |
return oids; // if too big just return it | |
} | |
} | |
} | |
return oids; | |
} | |
function getGenericCachedData(url) { | |
var cache = CacheService.getPublicCache(); | |
var data = cache.get("g"+url.substr(0,240)); | |
if (data == null) { | |
var requestData = {"method":"GET", "headers": { "User-Agent": "http://docs.google.com"}}; | |
try { | |
var response = UrlFetchApp.fetch(url, requestData); | |
if (response.getResponseCode() == 200) { | |
var data = response.getContentText(); | |
cache.put("g"+url.substr(0,240), data, defCache); | |
} | |
} catch(e){ | |
Logger.log(e); | |
} | |
} | |
return data; | |
} | |
// http://jsfromhell.com/array/chunk | |
function chunks(a, s){ | |
for(var x, i = 0, c = -1, l = a.length, n = []; i < l; i++) | |
(x = i % s) ? n[c][x] = a[i] : n[++c] = [a[i]]; | |
return n; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment