├── .DS_Store ├── README ├── google_scraper.js ├── has_google_results.js ├── html_files └── fish_oil.html └── seomoz.js /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chrisle/seer.js-public/92587da39890016ee62c05c6edf00c917f4ebf22/.DS_Store -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | A public collection of Seer Interactive's Google Docs functions 2 | 3 | google_scraper.js: Google Scraper for Google Docs Spreadsheet. 4 | has_google_results.js: Determine if there are any results for a given a keyword / site. 5 | seomoz.js: Wrapper for SEOmoz API 6 | -------------------------------------------------------------------------------- /google_scraper.js: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------- 2 | * google_scraper.js 3 | * More info: http://bit.ly/wDFMn5 4 | * 5 | * @desc Google Scraper for Google Docs Spreadsheet. 6 | * @author Chris Le - @djchrisle - chrisl at seerinteractive.com 7 | * @license MIT (see: http://www.opensource.org/licenses/mit-license.php) 8 | * @version 1.2 9 | * 10 | * Change Log: 11 | * 1.2.0 - Added ability to offset results (thanks @johnsee) 12 | * 1.1.0 - Added ability to change the TLD 13 | * 1.0.1 - Fixed Non-URL universal searches getting returned (Thanks Gareth Brown) 14 | * 1.0.0 - Initial release 15 | * -------------------------------------------------------------------------*/ 16 | 17 | var SeerJs_GoogleScraper = (function() { 18 | 19 | var errorOccurred; 20 | 21 | /** 22 | * Gets stuff inside two tags 23 | * @param {string} haystack String to look into 24 | * @param {string} start Starting tag 25 | * @param {string} end Ending tag 26 | * @return {string} Stuff inside the two tags 27 | */ 28 | function getInside(haystack, start, end) { 29 | var startIndex = haystack.indexOf(start) + start.length; 30 | var endIndex = haystack.indexOf(end); 31 | return haystack.substr(startIndex, endIndex - startIndex); 32 | } 33 | 34 | /** 35 | * Fetch keywords from Google. Returns error message if an error occurs. 36 | * @param {string} kw Keyword 37 | * @param {array} optResults (Optional) Number of results to return (defaults to 10) 38 | * @param {string} optTld (Optional) Top level domain (eg: ".co.uk". Defaults to ".com") 39 | * @param {string} optStart (Optional) Sets the starting offset for results (defaults to 0) 40 | * filter=0 Force all results from Google. Important when using large offset 41 | */ 42 | function fetch(kw, optResults, optTld, optStart) { 43 | errorOccurred = false; 44 | optResults = optResults || 10; 45 | optStart = optStart || 0; 46 | optTld = optTld || '.com'; 47 | try { 48 | var url = 'http://www.google' + optTld + '/search?q=' + kw + '&start=' + optStart + '&num=' + optResults + '&filter=0'; 49 | return UrlFetchApp.fetch(url).getContentText() 50 | } catch(e) { 51 | errorOccurred = true; 52 | return e; 53 | } 54 | } 55 | 56 | /** 57 | * Extracts the URL from an organic result. Returns false if nothing is found. 58 | * @param {string} result XML string of the result 59 | */ 60 | function extractUrl(result) { 61 | var url; 62 | if (result.match(/\/url\?q=/)) { 63 | url = getInside(result, "?q=", "&"); 64 | return (url != '') ? url : false 65 | } 66 | return false; 67 | } 68 | 69 | /** 70 | * Extracts the organic results from the page and puts them into an array. 71 | * One per element. Each element is an XMLElement. 72 | */ 73 | function extractOrganic(html) { 74 | html = html.replace(/\n|\r/g, ''); 75 | var allOrganic = html.match(/
  • (.*)<\/li>/gi).toString(), 76 | results = allOrganic.split("
  • "), 77 | organicData = [], 78 | i = 0, 79 | len = results.length, 80 | url; 81 | while(i < len) { 82 | url = extractUrl(results[i]); 83 | if (url && url.indexOf('http') == 0) { 84 | organicData.push(url); 85 | } 86 | i++; 87 | } 88 | return organicData; 89 | } 90 | 91 | /** 92 | * Transpose an array from row to cols 93 | */ 94 | function transpose(ary) { 95 | var i = 0, len = ary.length, ret = []; 96 | while(i < len) { 97 | ret.push([ary[i]]); 98 | i++; 99 | } 100 | return ret; 101 | } 102 | 103 | //-------------------------------------------------------------------------- 104 | 105 | return { 106 | /** 107 | * Returns Google SERPs for a given keyword 108 | * @param {string} kw Keyword 109 | */ 110 | get: function(kw, optResults, tld, optStart) { 111 | var result = fetch(kw, optResults, tld, optStart); 112 | if (errorOccurred) { return result; } 113 | return transpose(extractOrganic(result)); 114 | } 115 | } 116 | 117 | })(); 118 | 119 | function googleScraper(keyword, optResults, optTld, optStart) { 120 | return SeerJs_GoogleScraper.get(keyword, optResults, optTld, optStart); 121 | } 122 | 123 | function test() { 124 | var withArg = googleScraper("seer interactive", 20); 125 | var noArg = googleScraper("seer interactive"); 126 | return 0; 127 | } 128 | -------------------------------------------------------------------------------- /has_google_results.js: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------- 2 | * has_google_results.js 3 | * 4 | * @desc Determine if there are any results for a given a keyword / site. 5 | * @author Chris Le - @djchrisle - chrisl at seerinteractive.com 6 | * @license MIT (see: http://www.opensource.org/licenses/mit-license.php) 7 | * @version 1.0 8 | * -------------------------------------------------------------------------*/ 9 | 10 | /** 11 | * Returns true if results in Google are found. Returns false if not. 12 | * @param {string} site What site you want to look into 13 | * @param {string} kw The keyword you want to look for 14 | * @param {integer} optResults (optional) Number of results (default is 20) 15 | * @param {string} optParams (optional) Extra query string parameters 16 | * @return {Boolean} True / false. 17 | * @example 18 | * =hasGoogleResults('arnold.com', 'guest post'); // => false 19 | * =hasGoogleResults('cnn.com', 'guest post'); // => true 20 | */ 21 | function hasGoogleResults(site, kw, optResults, optParams) { 22 | var url = generateUrl_(site, kw, optResults, optParams); 23 | var fetch = Xml.parse(UrlFetchApp.fetch(url).getContentText(), true); 24 | var html = fetch.toXmlString(); 25 | var hasResults = html.match("not match any documents"); 26 | return (hasResults != null) ? false : true; 27 | } 28 | 29 | /** 30 | * Returns a Google search URL 31 | * @param {string} site What site you want to look into 32 | * @param {string} kw The keyword you want to look for 33 | * @param {integer} optResults (optional) Number of results (default is 20) 34 | * @param {string} optParams (optional) Extra query string parameters 35 | * @return {string} Full Google search URL 36 | * @private 37 | */ 38 | function generateUrl_(site, kw, optResults, optParams) { 39 | optResults = optResults || 20; 40 | optParams = optParams || "&hl=en&lr=&ft=i&cr=&safe=images&pws=0"; // jdoherty's stuff... 41 | kw.replace(/\s/, '+'); // replace spaces with +'s' 42 | site = 'site:' + site; // site: search 43 | var query = site + '%20' + kw; 44 | var url = 'http://www.google.com/search?q=' + query + "&num=" + optResults + optParams; 45 | return url; 46 | } 47 | -------------------------------------------------------------------------------- /html_files/fish_oil.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 9 | 10 | 11 | fish oil - Google Search 12 | 15 | 18 | 21 | 24 | 25 | 26 | 28 |
    29 | 32 | 33 |
    34 |
    35 |
      36 |
    1. 39 | Search
    2. 40 | 41 |
    3. 45 | Images
    4. 46 | 47 |
    5. 51 | Videos
    6. 52 | 53 |
    7. 57 | Maps
    8. 58 | 59 |
    9. 62 | News
    10. 63 | 64 |
    11. 68 | Shopping
    12. 69 | 70 |
    13. Gmail
    14. 74 | 75 |
    15. 76 | More 81 | 82 |
      83 |
      84 |
        85 |
      1. 89 | Translate
      2. 90 | 91 |
      3. 95 | Books
      4. 96 | 97 |
      5. 101 | Finance
      6. 102 | 103 |
      7. 107 | Scholar
      8. 108 | 109 |
      9. 113 | Blogs
      10. 114 | 115 |
      11. 116 |
        117 |
      12. 118 | 119 |
      13. 123 | YouTube
      14. 124 | 125 |
      15. 129 | Calendar
      16. 130 | 131 |
      17. 135 | Photos
      18. 136 | 137 |
      19. Documents
      20. 141 | 142 |
      21. Sites
      22. 146 | 147 |
      23. 151 | Groups
      24. 152 | 153 |
      25. 157 | Reader
      26. 158 | 159 |
      27. 160 |
        161 |
      28. 162 | 163 |
      29. Even more »
      30. 167 |
      168 |
      169 |
      170 |
    16. 171 |
    172 |
    173 | 174 |
    175 |

    Account Options

    176 | 177 |
      178 |
    1. Sign in
    2. 183 | 184 |
    3. 185 | 190 | 191 |
      192 |
      193 |
        194 |
      1. Search settings
      2. 196 | 197 |
      3. 198 |
        199 |
      4. 200 | 201 |
      5. Web 203 | History
      6. 204 |
      205 |
      206 |
      207 |
    4. 208 |
    209 |
    210 |
    211 | 212 |
    213 | 214 |
    217 |
    218 | 219 |
    221 |
    222 | We\'re 224 | changing our privacy policy and terms. This stuff 225 | matters.
    226 | Learn 229 | more | Dismiss 232 |
    233 |

    235 | 236 | 238 | 239 | 244 | 245 | 273 | 274 | 275 | 276 | 277 | 278 | 291 | 292 | 293 | 294 | 295 | 296 | 363 | 364 | 983 | 984 | 1070 | 1071 |
    240 |

    243 |
    246 |
    248 | 251 | 252 | 260 | 261 | 269 | 270 |
    254 |
    255 | 258 |
    259 |
    262 |
    263 |
    264 | 266 |
    267 |
    268 |
    271 |
    272 |
    279 |
    280 |
    281 | Advanced 283 | search 284 |
    285 | 286 |
    287 | About 127,000,000 results 288 |
    289 |
    290 |
    297 |
    298 | More 318 |
    319 | 320 |
    321 |

    Search Options

    322 | 323 | More 360 | search tools 361 |
    362 |
    365 |
    366 |
    368 |

    370 | Ads

    371 | 372 |
      373 |
    1. 374 |

      Is Your Fish Oil Safe? | 377 | EDF.org

      Find out which fish oil 378 | supplements have removed the contaminants.
      379 | www.edf.org/ 380 |
    2. 381 | 382 |
    3. 383 |

      Fish Oil - 50% Off - Promotes 386 | Overall Health & Wellness.

      The Best 387 | Fish Oil from Top Brands!
      388 | 390 | Free Shipping over $22 - Today Only - 392 | $2.22 Standard Shipping. Today Only
      393 | www.vitacost.com/FishOil 394 | 395 |
      396 |
      397 | vitacost.com is rated (5,736 400 | reviews) 401 |
      402 |
      403 | 404 |
      405 |
    4. 406 | 407 |
    5. 408 |

      Buy Fish Oil Supplements | 411 | drugstore.com

      Fish Oil Supplements. 412 | Shop at Home. User Reviews. Free Shipping On 413 | $25+
      414 | 416 | Free Shipping With $25 Purchase - 418 | Instant Coupons - 420 | Free Gift With Purchase
      421 | www.drugstore.com/Supplements 422 | 423 |
      424 |
      425 | drugstore.com is rated (28,467 428 | reviews) 429 |
      430 |
      431 | 432 |
      433 |
    6. 434 |
    435 |
    436 | 437 |
    438 |
    439 |
      440 |
    1. 441 |

      443 | Fish oil - Wikipedia, the free 444 | encyclopedia

      445 | 446 |
      447 | Fish oil is oil derived from the tissues 448 | of oily fish. Fish oils contain the 449 | omega-3
      450 | fatty acids eicosapentaenoic acid (EPA), and 451 | docosahexaenoic acid (DHA), ...
      452 | 453 |
      454 | en.wikipedia.org/wiki/Fish_oil 455 | - 457 | Cached - 459 | Similar 460 |
      461 |
      462 |
    2. 463 | 464 |
    3. 465 |

      467 | Fish Oil & Omega-3 Benefits 468 | âÄ¢ Free Fish Oil 469 | Supplement Guide.

      470 | 471 |
      472 | Learn Everything You Need to Know About Fish 473 | Oil and Omega-3. Find the
      474 | Highest Quality Fish Oil 475 | Supplements.
      476 | 477 |
      478 | www.fishoilblog.com/ 479 | - 481 | Cached - 483 | Similar 484 |
      485 |
      486 |
    4. 487 | 488 |
    5. 489 |

      491 | FISH OIL: Uses, Side Effects, Interactions 492 | and Warnings - WebMD

      493 | 494 |
      495 | Find patient medical information for FISH 496 | OIL on WebMD including its uses,
      497 | effectiveness, side effects and safety, 498 | interactions, user ratings and products that 499 | ...
      500 | 501 |
      502 | www.webmd.com/.../ingredientmono-993-FISH%20OIL.aspx?...FISH%20OIL 503 | - 505 | Cached - 507 | Similar 508 |
      509 |
      510 |
    6. 511 | 512 |
    7. 513 |

      Shopping results 515 | for fish oil

      516 | 517 |
      518 |
      519 |
      521 | 523 | 526 |
      527 | 528 | 535 | 536 |
      538 |
      539 | . 540 |
      541 |
      (1) 542 | 543 |
      544 | $27 545 |
      43 stores 546 |
      547 | 548 |
      549 |
      551 | 554 |
      555 | 556 | 561 | 562 |
      564 |
      565 | . 566 |
      567 |
      () 568 | 569 |
      570 | $31 571 |
      41 stores 572 |
      573 | 574 |
      575 |
      577 | 580 |
      581 | 582 | 588 | 589 |
      591 |
      592 | . 593 |
      594 |
      () 595 | 596 |
      597 | $14 598 |
      39 stores 599 |
      600 | 601 |
      602 |
      604 | 607 |
      608 | 609 | 614 | 615 |
      617 |
      618 | . 619 |
      620 |
      () 621 | 622 |
      623 | $13 624 |
      13 stores 625 |
      626 | 627 |
      628 |
      630 | 633 |
      634 | 635 | 640 | 641 |
      643 |
      644 | . 645 |
      646 |
      () 647 | 648 |
      649 | $14 650 |
      14 stores 651 |

      652 |
      653 |
    8. 654 | 655 |
    9. 656 |

      658 | Confused? Omega 3 vs Fish Oil & Omega 659 | 3 Fish Oil Benefits

      660 | 661 |
      662 | Discover the Benefits of Fish Oil vs 663 | Omega 3 Benefits, Get Your FREE Omega 3
      664 | Fish Oil Expert Audio Guide & Learn 665 | How to Choose the Best Fish Oil 666 | ...
      667 | 668 |
      669 | www.omega-3.us/ - 672 | Cached - 674 | Similar 675 |
      676 |
      677 |
    10. 678 | 679 |
    11. 680 |

      682 | GNC Triple Strength Fish Oil - GNC - 683 | GNC

      684 | 685 |
      686 |
      688 |
      689 | . 690 |
      691 |
       167 reviews 692 |
      693 | 694 |
      695 | The GNC Auto Delivery program allows you to set 696 | up shipping and delivery for
      697 | products (just one or many!) that you use on a 698 | recurring basis. To begin receiving 699 | ...
      700 | 701 |
      702 | www.gnc.com/product/index.jsp?productId=3685052 703 | - 705 | Cached - 707 | Similar 708 |
      709 |
      710 |
    12. 711 | 712 |
    13. 713 |

      715 | Omega-3 fatty acids, fish oil, 716 | alpha-linolenic acid - MayoClinic.com

      717 | 718 |
      719 | Oct 1, 2011 ... Dietary sources of 720 | omega-3 fatty acids include fish oil and 721 | certain plant/nut oils.
      722 | Fish oil contains both docosahexaenoic 723 | acid (DHA) and ...
      724 | 725 |
      726 | www.mayoclinic.com/health/fish-oil/NS_patient-fishoil 727 | - 729 | Cached - 731 | Similar 732 |
      733 |
      734 |
    14. 735 | 736 |
    15. 737 |

      739 | Amazon.com: Fish Oil Vitamins & 740 | Supplements

      741 | 742 |
      743 | Results 1 - 24 of 694 ... Online 744 | shopping for Fish Oil Vitamins & 745 | Supplements from a great selection of
      746 | Health & Personal Care; & more at 747 | everyday low prices.
      748 | 749 |
      750 | www.amazon.com/s?ie=UTF8&rh...n_feature_six...page... 751 | - 753 | Cached - 755 | Similar 756 |
      757 |
      758 |
    16. 759 | 760 |
    17. 761 |

      763 | Fish Oil Supplements | at Puritan's 764 | Pride

      765 | 766 |
      767 | Our Fish Oils naturally contain EPA and 768 | DHA, the important fatty acids
      769 | responsible for fish oil's healthy 770 | benefits.** Buy high quality.
      771 | 772 |
      773 | www.puritan.com/fish-oils-056 774 | - 776 | Cached - 778 | Similar 779 |
      780 |
      781 |
    18. 782 | 783 |
    19. 784 |

      786 | What is the difference between fish oil 787 | and - Nordic Naturals

      788 | 789 |
      790 | Since 1995, Nordic Naturals has been the 791 | industry leader in omega-3 fish oil 792 | ...
      793 | GROUP 2 What's the difference between fish 794 | oil and other sources of omega-3s?
      795 | 796 |
      797 | www.nordicnaturals.com/en/FAQ's/FAQs/385 798 | - 800 | Cached - 802 | Similar 803 |
      804 |
      805 |
    20. 806 | 807 |
    21. 808 |

      810 | Omega 3 Fish Oil 300 Epa/ 200 Dha by 811 | Vitamin Shoppe - VS-1044 ...

      812 | 813 |
      814 | Product Label. *Essential Oils & 815 | Fatty Acids *Molecularly Distilled 816 | *Supports
      817 | Cardiovascular and Joint Health** *NO yeast, 818 | corn, wheat, sugar, salt, starch,
      819 | dairy, ...
      820 | 821 |
      822 | www.vitaminshoppe.com/store/en/browse/sku_detail.jsp?id=VS... 823 | - 825 | Cached - 827 | Similar 828 |
      829 |
      830 |
    22. 831 |
    832 |
    833 |
    834 | *** 835 | 836 |
    837 |
    838 | Searches related to: fish oil 839 |
    840 | 841 | 842 | 843 | 860 | 861 | 878 | 879 |
    844 |

    846 | fish oil side effects

    847 | 848 |

    850 | benefits of fish oil

    851 | 852 |

    854 | fish oil dosage

    855 | 856 |

    858 | fish oil weight loss

    859 |
    862 |

    864 | fish oil supplements

    865 | 866 |

    868 | fish oil pills

    869 | 870 |

    872 | flax seed oil

    873 | 874 |

    876 | fish oil wow

    877 |
    880 |
    881 |
    882 | 883 | 982 |
    985 |
    987 | 989 | 990 | 1066 | 1067 |
    992 |

    994 | Ads

    995 | 996 |
      998 |
    1. 999 |

      Premium Omega-3 Fish 1002 | Oil

      Highest Quality Omega-3 Fish 1003 | Oil,
      1004 | Buy Direct & Save, Buy Online Today
      1005 | www.lahananaturals.com/
      1006 |
    2. 1007 | 1008 |
    3. 1009 |

      New ProNutrientsâÑ¢ 1012 | Omega-3

      Cutting edge, Ultra 1013 | Concentrated
      1014 | MiniGels, Smaller than Most Brands
      1015 | www.pronutrients.com/
      1016 |
    4. 1017 | 1018 |
    5. 1019 |

      Natural Fish Oil 1022 | Sale

      Clean Salmon Oil 1023 | Supplements -
      1024 | Get Omega 3 Fish Oil for Health!
      1025 | www.martindalesnutrition.com/fish
      1026 |
    6. 1027 | 1028 |
    7. 1029 |

      The Best Fish Oil

      Learn 1032 | why so many people around
      1033 | the world prefer our pure fish oil
      1034 | www.xtend-life.com/
      1035 |
    8. 1036 | 1037 |
    9. 1038 |

      Zone Diet Omega Fish 1041 | Oils

      Find Out The Health Benefits 1042 | of
      1043 | Omega Fish Oil From Dr. Sears!
      1044 | www.zonediet.com/Zone_Fish_Oil
      1045 |
    10. 1046 | 1047 |
    11. 1048 |

      Nature Made At Target

      Safely 1051 | Made, Purely Made Vitamins.
      1052 | Save On Nature Made Vitamins Today!
      1053 | www.target.com/
      1054 |
    12. 1055 | 1056 |
    13. 1057 |

      What Is Fish Oil

      Find 1060 | What Is Fish Oil
      1061 | Q&A What Is Fish Oil
      1062 | www.ask.com/What+Is+Fish+Oil
      1063 |
    14. 1064 |
    1065 |
    1068 |
    1069 |
    1077 | 1078 | 1079 | -------------------------------------------------------------------------------- /seomoz.js: -------------------------------------------------------------------------------- 1 | /* --------------------------------------------------------------------------- 2 | * seomoz.js 3 | * 4 | * @desc Wrapper for SEOmoz API 5 | * @author Chris Le - @djchrisle - chrisl at seerinteractive.com 6 | * @license MIT (see: http://www.opensource.org/licenses/mit-license.php) 7 | * @version 1.0 8 | * -------------------------------------------------------------------------*/ 9 | 10 | /** 11 | * Credits and history: 12 | * Chris Le: http://bit.ly/xLop1a 13 | * Tom Anthony: http://bit.ly/lARZ9u 14 | * Ian Lurie: http://bit.ly/mBpRXC 15 | * Tom Critchlow: http://bit.ly/xmWlEP 16 | */ 17 | 18 | //---------------------------------------------------------------------------- 19 | // Expose functions to Google Docs 20 | 21 | function getLinkscape(urlRange, optIncludeHeader) { 22 | try { 23 | return SeerJs.SeomozApi.urlMetrics(urlRange, optIncludeHeader); 24 | } catch(e) { 25 | return e.message; 26 | } 27 | } 28 | 29 | function filterColumns(input, filterCols) { 30 | return SeerJs.ArrayTransform.filterColumns(input, filterCols); 31 | } 32 | 33 | function SeerJs_() { VERSION = 'v1.5'; return VERSION; } 34 | var SeerJs = new SeerJs_(); 35 | 36 | //---------------------------------------------------------------------------- 37 | // Settings tab in the spreadsheet 38 | 39 | SeerJs_.prototype.Settings = (function() { 40 | 41 | function getSettingsSheet_() { 42 | SETTINGS_SHEET = "Settings"; 43 | var thisDoc = SpreadsheetApp.getActiveSpreadsheet(); 44 | var settingsSheet = thisDoc.getSheetByName(SETTINGS_SHEET); 45 | if (settingsSheet == null) { 46 | thisDoc.insertSheet(SETTINGS_SHEET); 47 | } 48 | return settingsSheet; 49 | } 50 | 51 | return { 52 | /** 53 | * Returns setting names need to be in column A and the setting value needs to 54 | * be in column B. 55 | * 56 | * @param {string} settingName Name of the setting you want to return 57 | * @return The setting or false if not found. 58 | */ 59 | get: function(settingName) { 60 | var settings = getSettingsSheet_().getRange("A:B").getValues(); 61 | for (var i = 0; i < settings.length; i++) { 62 | var setting = settings[i][0]; 63 | if (settings[i][0].toUpperCase() == settingName.toUpperCase()) { 64 | return settings[i][1]; 65 | } 66 | } 67 | return false; 68 | } 69 | 70 | }; 71 | })(); 72 | 73 | //---------------------------------------------------------------------------- 74 | // Http module 75 | 76 | SeerJs_.prototype.Http = (function() { 77 | 78 | function toParam(params) { 79 | var tuples = []; 80 | for (key in params) { 81 | tuples.push(key + "=" + params[key]); 82 | } 83 | return tuples.join("&"); 84 | } 85 | 86 | return { 87 | /** 88 | * Builds a URI with parameters from an array, fetches it and returns a JSON 89 | * parse object. 90 | * @param {string} uri The URI you want to get 91 | * @param {hash} optParams (optional) Hash of parameters to pass 92 | * @return A Utilities.jsonParse object 93 | * @example 94 | * var result = fetchJson("http://www.some-api.com/param", { 95 | * "key" : "12345", 96 | * "param" : "value" 97 | * }); 98 | */ 99 | fetchJson: function(uri, optParams, optArgs) { 100 | var response, fetch; 101 | if (optParams != undefined) uri = uri + "?" + toParam(optParams); 102 | if (optArgs == undefined) { 103 | response = UrlFetchApp.fetch(uri) 104 | fetch = response.getContentText(); 105 | } else { 106 | response = UrlFetchApp.fetch(uri, optArgs); 107 | fetch = response.getContentText(); 108 | } 109 | return Utilities.jsonParse(fetch); 110 | } 111 | 112 | }; 113 | })(); 114 | 115 | //---------------------------------------------------------------------------- 116 | // Utils module 117 | 118 | SeerJs_.prototype.Utils = (function() { 119 | return { 120 | 121 | /** 122 | * Groups the array into chucks of size. 123 | * @example 124 | * SeerJs.Utils.groupBy([1,2,3,4,5,6], 3); // => [[1,2,3], [4,5,6]] 125 | */ 126 | groupBy: function(array, size) { 127 | if (array.length < size) return [array]; 128 | var temp = array; 129 | var first = true; 130 | var retval = []; 131 | while (temp.length > size) { 132 | var temp2 = temp.splice(size); 133 | if (first) { 134 | retval.push(temp); 135 | first = false; 136 | } 137 | retval.push(temp2); 138 | temp = temp2; 139 | } 140 | return retval; 141 | }, 142 | 143 | /** 144 | * Converts a string to an array (only if it's actually a string) 145 | * @example 146 | * SeerJs.Utils.strToArray('hello world'); // => ['hello world'] 147 | * SeerJs.Utils.strToArray(['hello world']); // => ['hello world'] 148 | */ 149 | strToArray: function(obj) { 150 | if (typeof obj == "string") { 151 | return [obj]; 152 | } else { 153 | return obj; 154 | } 155 | }, 156 | 157 | /** 158 | * Checks if the object is an array 159 | * @example 160 | * SeerJs.Utils.isArray([1,2,3]); // => true 161 | * SeerJs.Utils.isArray('not an array'); // => false 162 | */ 163 | isArray: function(obj) { 164 | if (obj.constructor.toString().indexOf("Array") == -1) { 165 | return false; 166 | } else { 167 | return true; 168 | } 169 | }, 170 | 171 | /** 172 | * Returns true if the array is actually in columns 173 | * @example 174 | * // myArray is A1:A3 175 | * isColumn(myArray); // => true 176 | * // myArray is A1:C1 177 | * isColumn(myArray); // => false 178 | * // myArray is A1 179 | * isColumn(myArray); // => false 180 | */ 181 | isColumn: function(array) { 182 | if (SeerJs.Utils.isArray(array)) { 183 | if (SeerJs.Utils.isArray(array[0])) { 184 | return true; 185 | } 186 | } 187 | return false; 188 | } 189 | }; 190 | })(); 191 | 192 | //---------------------------------------------------------------------------- 193 | // Array Transformer! (... more than meets the eye!) 194 | 195 | SeerJs_.prototype.ArrayTransform = (function() { 196 | 197 | // Transposes hash tables 198 | function hashTableToCols_ (input, filter) { 199 | var retval = new Array, colIndex = new Array, newRow = new Array; 200 | retval.push(filter); 201 | for (row in input) { 202 | newRow = new Array; 203 | for (col in filter) { 204 | newRow.push(input[row][filter[col]]); 205 | } 206 | retval.push(newRow); 207 | } 208 | return retval; 209 | } 210 | 211 | // Transposes arrays that have created an array in rows 212 | function arrayToCols_ (input, filter) { 213 | var retval = new Array, colIndex = new Array, newRow = new Array; 214 | for (col in filter) { colIndex.push(input[0].indexOf(filter[col])); } 215 | for (row in input) { 216 | newRow = new Array; 217 | for (col in colIndex) { newRow.push(input[row][colIndex[col]]); } 218 | retval.push(newRow); 219 | } 220 | return retval; 221 | } 222 | 223 | return { 224 | /** 225 | * Filters data by columns using the header. 226 | * 227 | * @param {array|range} input Input array or range of cells. 228 | * @param {array|range} filterCols Columns you want to filter by 229 | * @example 230 | * 231 | * Headers in the first row 232 | * ------------------------ 233 | * A1: url 234 | * B1: title 235 | * C1: mozrank 236 | * D1: page authority 237 | * 238 | * URLs down column A 239 | * ------------------ 240 | * A2: www.seerinteractive.com 241 | * A3: www.seomoz.org 242 | * A4: www.distilled.net 243 | * 244 | * filterColumns + getLinkscape + Magic 245 | * -------------------------------------- 246 | * B2: =filterColumns( getLinkscape(A2:A4, false), B1:D1 ) 247 | */ 248 | filterColumns: function(input, filterCols) { 249 | var retval, filter; 250 | filter = (filterCols.length == 1) ? filterCols[0] : filterCols; // transpose 251 | if ((input[0].length != undefined) && (input[0].length > 0)){ 252 | retval = arrayToCols_(input, filter); 253 | } else { 254 | retval = hashTableToCols_(input, filter); 255 | } 256 | return (filterCols.length == 1) ? 257 | SeerJs.ArrayTransform.removeFirstRow(retval) : retval; 258 | }, 259 | 260 | /** 261 | * Removes the header of an array or range 262 | * @param {array|range} input Array to shift 263 | * @return The array without a header 264 | */ 265 | removeFirstRow: function(input) { 266 | var temp = input; 267 | temp.shift(); 268 | return temp; 269 | } 270 | }; 271 | 272 | })(); 273 | 274 | //---------------------------------------------------------------------------- 275 | // Seomoz module 276 | 277 | /** 278 | * SEOmoz enclosure 279 | * @author Chris Le - @djchrisle - chrisl at seerinteractive.com 280 | * @author Tom Anthony (original: http://bit.ly/lARZ9u) 281 | */ 282 | SeerJs_.prototype.SeomozApi = (function() { 283 | 284 | var SEOMOZ_MEMBER_ID = SeerJs.Settings.get("SEOmoz Member ID"); 285 | var SEOMOZ_SECRET_KEY = SeerJs.Settings.get("SEOmoz Secret Key"); 286 | var SEOMOZ_BITFLAGS = { 287 | "ut" : "title" , // 1 288 | "uu" : "url" , // 4 289 | "ueid" : "external links" , // 32 290 | "uid" : "links" , // 2048 291 | "umrp" : "mozrank" , // 16384 292 | "fmrp" : "subdomain mozrank" , // 32768 293 | "us" : "http status code" , // 536870912 294 | "upa" : "page authority" , // 34359738368 295 | "pda" : "domain authority" // 6871947673 296 | }; 297 | var SEOMOZ_ALL_METRICS = 103616137253; // All the free metrics 298 | var SEOMOZ_BATCH_SIZE = 10; // Size of batch (Maximum 10) 299 | var OBEY_FREE_RATE_LIMIT = true; // Zzz for 5 sec. after every request 300 | 301 | /** 302 | * Extracts URLs from columns and removes the protocol from them 303 | */ 304 | function linkscapePrepUrls_ (urls) { 305 | var retval = []; 306 | if (SeerJs.Utils.isArray(urls)) { 307 | // remove outer array if we get columns 308 | if (SeerJs.Utils.isColumn(urls)) { 309 | var i = 0; var len = urls.length; 310 | while (i < len) { 311 | retval.push(urls[i]); 312 | i++; 313 | } 314 | } else { 315 | retval = urls; 316 | } 317 | } else { 318 | // if we get a string, convert it to an array. 319 | retval = SeerJs.Utils.strToArray(urls); 320 | } 321 | // remove protocol or seomoz doesn't work right. 322 | for (var i = 0; i < retval.length; i++) { 323 | retval[i] = retval[i].toString().replace(/http(s)?:\/\//gi, ""); 324 | } 325 | return retval; 326 | } 327 | 328 | /** 329 | * Transposes results from linkscape api to a 2d array 330 | */ 331 | function linkscapeTranspose_ (response) { 332 | var retval = []; 333 | var row = []; 334 | // push headers 335 | for (key in SEOMOZ_BITFLAGS) { row.push(SEOMOZ_BITFLAGS[key]); } 336 | retval.push(row); 337 | // push rows 338 | for (var i = 0; i < response.length; i++) { 339 | row = []; 340 | for (key in SEOMOZ_BITFLAGS) { 341 | row.push(response[i][key]); 342 | } 343 | retval.push(row); 344 | } 345 | return retval; 346 | } 347 | 348 | /** 349 | * Creates a XOR bit flag based on the array of columns you want. 350 | */ 351 | function linkscapeBitFlag_(cols) { 352 | for (flag in SEOMOZ_BITFLAGS) { 353 | var hash = SEOMOZ_BITFLAGS[flag]; 354 | } 355 | } 356 | 357 | /** 358 | * Creates a expiration time stamp 359 | */ 360 | function linkscapeExp_() { 361 | var uDate = new Date().getTime(); 362 | return Math.round(uDate/1000) + 1200; 363 | } 364 | 365 | /** 366 | * Calculates 64bit hash signature 367 | */ 368 | function linkscapeSig_(expire) { 369 | var signature = Utilities.computeHmacSignature( 370 | Utilities.MacAlgorithm.HMAC_SHA_1, SEOMOZ_MEMBER_ID + "\n" + 371 | expire, SEOMOZ_SECRET_KEY); 372 | return encodeURIComponent(Utilities.base64Encode(signature)); 373 | } 374 | 375 | return { 376 | 377 | /** 378 | * Returns all metrics from SEOmoz's Linkscape.

    379 | * 380 | * Original by {@link http://www.tomanthony.co.uk/blog/seomoz-linkscape-api-with-google-docs/} 381 | * Modified so that you can select a large range of URLs and it will get the 382 | * metrics in batches of 10.

    383 | * 384 | * @param {string[]} urlRange One or more URLs to send to Linkscape 385 | * @param {boolean} optIncludeHeader Include the header? (Default is true) 386 | * @function getLinkscape 387 | * 388 | * @example 389 | * Cells: 390 | * A1: www.seerinteractive.com 391 | * A2: http://www.domain.com/blog 392 | * A3: http://www.anotherdomain.com/page.html 393 | * 394 | * // => Gets current data on www.seerinteractive.com 395 | * =getLinkscape("www.seerinteractive.com") 396 | * // => Gets current data on www.seerinteractive.com 397 | * =getLinkscape(A1) 398 | * // => Gets data for three URLS in a batch 399 | * =getLInkscape(A1:A3) 400 | * // => Gets data for three URLS in a batch and reomves the header row 401 | * =getLInkscape(A1:A3, false) 402 | * 403 | */ 404 | urlMetrics: function(urlRange, optIncludeHeader) { 405 | if (optIncludeHeader == undefined) optIncludeHeader = true; 406 | var expire = linkscapeExp_(); 407 | var retval = new Array; 408 | var first = true; 409 | var response; 410 | 411 | // POST in batches of 10 and merge results 412 | urlRange = SeerJs.Utils.strToArray(urlRange); 413 | var urlGroups = SeerJs.Utils.groupBy(linkscapePrepUrls_(urlRange), 414 | SEOMOZ_BATCH_SIZE); 415 | for (var g = 0; g < urlGroups.length; g++) { 416 | var payload = Utilities.jsonStringify(urlGroups[g]) 417 | response = linkscapeTranspose_(SeerJs.Http.fetchJson( 418 | "http://lsapi.seomoz.com/linkscape/url-metrics/", 419 | { 420 | "AccessID" : SEOMOZ_MEMBER_ID, 421 | "Expires" : expire, 422 | "Signature" : linkscapeSig_(expire), 423 | "Cols" : SEOMOZ_ALL_METRICS 424 | }, 425 | { 426 | "method" : "post", 427 | "payload" : payload 428 | } 429 | )); 430 | // merge results from batches together 431 | if (first == false) response.shift(); 432 | retval.push.apply(retval, response); 433 | first = false; 434 | if (OBEY_FREE_RATE_LIMIT) { Utilities.sleep(5000); } 435 | } 436 | // remove header if user requests. 437 | if (!optIncludeHeader) retval.shift(); 438 | return retval; 439 | } 440 | }; 441 | })(); 442 | 443 | //---------------------------------------------------------------------------- 444 | // test 445 | 446 | function _cheap_test() { 447 | /*var response = getLinkscape("www.seerinteractive.com"); 448 | (response[1][1] == 'www.seerinteractive.com/') 449 | ? (Logger.log('passed.')) 450 | : (Logger.log('failed.'));*/ 451 | 452 | var response = getLinkscape([ ["www.seerinteractive.com"], 453 | ['www.seomoz.org'], 454 | ['www.google.com'] ]); 455 | return response; 456 | } 457 | --------------------------------------------------------------------------------