google-shopping-list
Advanced tools
Comparing version 1.0.1 to 1.0.2
178
index.js
@@ -1,54 +0,142 @@ | ||
const puppeteer = require('puppeteer'); | ||
const puppeteer = require('puppeteer'), | ||
fs = require('fs-extra'), | ||
logger = require('./lib/logger'); | ||
to = require('./lib/to'); | ||
const ERROR_CHECK = 'body > div > div.main.content.clearfix > div', | ||
USERNAME_SELECTOR = '#Email', | ||
EMAIL_NEXT_BUTTON = '#next', | ||
PASSWORD_SELECTOR = '#Passwd', | ||
PASSWORD_NEXT_BUTTON = '#signIn', | ||
SHOPPINGLIST_ITEMS = '.listItemTitle'; | ||
let exp = module.exports; | ||
exp.getList = function(creds) { | ||
return new Promise((resolve,reject) => { | ||
exp.getList = async (creds, options = { cookies: false }) => { | ||
let err, list, browser, page; | ||
if(!creds) { | ||
reject(new Error('Credentials not supplied')); | ||
} else if(!('email' in creds)) { | ||
reject(new Error('Email not supplied')); | ||
} else if (!('password' in creds)) { | ||
reject(new Error('Password not supplied')); | ||
} else { | ||
get(); | ||
if(!creds) { | ||
return Error('Credentials not supplied'); | ||
} else if(!('email' in creds)) { | ||
return Error('Email not supplied'); | ||
} else if (!('password' in creds)) { | ||
return Error('Password not supplied'); | ||
} | ||
// Turn headless to false to debug | ||
[err, browser] = await to( puppeteer.launch({ headless: true }) ); | ||
if(err) return Error("Unable to launch the browser"); | ||
[err, page] = await to( browser.newPage() ); | ||
if(err) return Error("Unable to create a new page"); | ||
// Have to set this because some pages display differently for Headless Chrome | ||
[err] = await to( page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/65.0.3312.0 Safari/537.36')); | ||
if(err) return Error("Unable to set the user agent"); | ||
// If cookies option is enabled | ||
if(options.cookies) { | ||
let loadedCookies; | ||
[err, loadedCookies] = await to( loadCookies() ); | ||
if(err) logger.verbose("No cookie file.") | ||
if(loadedCookies) { | ||
if(creds.email in loadedCookies) { // Check cookies file contains cookies for the specified email if true, set cookies | ||
[err] = await to( page.setCookie(...loadedCookies[creds.email]) ); | ||
if(err) logger.Error("Unable to set cookies in browser"); | ||
} | ||
} | ||
} | ||
function get() { | ||
const ERROR_CHECK = 'body > div > div.main.content.clearfix > div' | ||
const USERNAME_SELECTOR = '#Email'; | ||
const EMAIL_NEXT_BUTTON = '#next'; | ||
const PASSWORD_SELECTOR = '#Passwd'; | ||
const PASSWORD_NEXT_BUTTON = '#signIn'; | ||
const SHOPPINGLIST_ITEMS = '.listItemTitle'; | ||
[err] = await to( page.goto('https://shoppinglist.google.com', {waitUntil: 'domcontentloaded'}) ); | ||
if(err) return Error("Unable to go to url https://shoppinglist.google.com"); | ||
(async () => { | ||
const browser = await puppeteer.launch({ | ||
headless: true // Turn this to false to debug | ||
}); | ||
const page = await browser.newPage(); | ||
await page.setUserAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/65.0.3312.0 Safari/537.36') // Have to set this because some pages display differently for Headless Chrome | ||
await page.goto('https://shoppinglist.google.com', {waitUntil: 'networkidle0'}); | ||
await page.click(USERNAME_SELECTOR); | ||
await page.keyboard.type(creds.email); | ||
await page.click(EMAIL_NEXT_BUTTON); | ||
await page.waitForNavigation(); | ||
await page.keyboard.type(creds.password); | ||
await page.click(PASSWORD_NEXT_BUTTON); | ||
await page.waitForNavigation({waitUntil: 'networkidle0'}); | ||
logger.verbose("Go to https://shoppinglist.google.com") | ||
let list = await page.evaluate(selector => { | ||
let items = []; | ||
let allItems = document.querySelectorAll(selector); | ||
allItems.forEach(res => items.push(res.innerHTML)); // Must use a forEach as it is a nodeList not an array, convert to array | ||
items.pop(); // Removes last element from array as it is not a list item | ||
return items; | ||
}, SHOPPINGLIST_ITEMS) | ||
// If you are not logged in google redirects you to a different url | ||
if(page.url() !== 'https://shoppinglist.google.com/') { | ||
logger.verbose("Not logged in") | ||
await logIn(page, creds); | ||
} | ||
await browser.close(); | ||
logger.verbose("Logged in") | ||
resolve(list); | ||
})(); | ||
} | ||
}) | ||
} | ||
[err] = await to( page.waitFor(SHOPPINGLIST_ITEMS) ); | ||
if(err) return Error("Unable to see the shopping list"); | ||
logger.verbose("Found the shopping list"); | ||
if(options.cookies) { // If cookies option is enabled | ||
const cookies = await page.cookies(); | ||
await saveCookies(creds.email, cookies); | ||
} | ||
[err, list] = await to( page.evaluate(selector => { | ||
let items = []; | ||
let allItems = document.querySelectorAll(selector); | ||
allItems.forEach(res => items.push(res.innerHTML.trim())); // Must use a forEach as it is a nodeList not an array, convert to array | ||
items.pop(); // Removes last element from array as it is not a list item | ||
return items; | ||
}, SHOPPINGLIST_ITEMS) ); | ||
if(err) return Error("Unable to get the shopping list items"); | ||
await browser.close(); | ||
return list; | ||
} | ||
async function logIn(page, creds) { | ||
[err] = await to( page.click(USERNAME_SELECTOR) ); | ||
if(err) return Error("Unable to find the username field"); | ||
logger.verbose('Clicked username'); | ||
[err] = await to( page.keyboard.type(creds.email) ); | ||
if(err) return Error("Unable to enter email"); | ||
logger.verbose('Entered email'); | ||
[err] = await to( page.click(EMAIL_NEXT_BUTTON) ); | ||
if(err) return Error(err, "Unable to click the next button"); | ||
logger.verbose('Click next button'); | ||
[err] = await to( page.waitFor(PASSWORD_NEXT_BUTTON) ); | ||
if(err) return Error(err, "Unable to see the password page"); | ||
logger.verbose("Sign in button seen"); | ||
[err] = await to( page.keyboard.type(creds.password) ); | ||
if(err) return Error(err, "Unable to enter the password"); | ||
logger.verbose("Entered password"); | ||
[err] = await to( page.click(PASSWORD_NEXT_BUTTON) ); | ||
if(err) return Error(err, "Unable to click the signin button"); | ||
logger.verbose("Clicked sign in"); | ||
} | ||
async function saveCookies(email, cookies) { | ||
cookies = {[email]: cookies} | ||
const [err] = await to ( fs.writeJson('./cookies.json', cookies) ) | ||
if(err) { | ||
logger.error(Error(err)); | ||
return Error(err); | ||
} | ||
logger.verbose('Cookies written to file'); | ||
return true; | ||
} | ||
async function loadCookies() { | ||
const [err, cookies] = await to ( fs.readJson('./cookies.json') ) | ||
if(err) { | ||
logger.error(Error(err)); | ||
return Error(err); | ||
} | ||
logger.verbose('Cookies read from file'); | ||
return cookies; | ||
} |
{ | ||
"name": "google-shopping-list", | ||
"version": "1.0.1", | ||
"version": "1.0.2", | ||
"description": "Scrapes the shopping list from shoppinglist.google.com as Google does not seem to have an API for this.", | ||
"main": "index.js", | ||
"scripts": { | ||
"test": "echo \"Error: no test specified\" && exit 1" | ||
"start": "node index.js", | ||
"test": "node test.js" | ||
}, | ||
@@ -20,4 +21,6 @@ "repository": { | ||
"dependencies": { | ||
"puppeteer": "^1.0.0" | ||
"fs-extra": "^5.0.0", | ||
"puppeteer": "^1.17.0", | ||
"winston": "^3.2.1" | ||
} | ||
} |
# Google Shopping List | ||
Scrapes the shopping list from shoppinglist.google.com as Google does not seem to have an API for this. | ||
@@ -25,9 +24,10 @@ | ||
**googleShoppingList.getList(credentials)** | ||
**googleshoppinglist.getList(credentials[, options])** | ||
* credentials <[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> | ||
* email: <[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type)> A Google email | ||
* password: <[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type)> The password for your google email | ||
* `credentials` <[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> | ||
* `email` <[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type)> A Google email | ||
* `password` <[string](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#String_type)> The password for your google email | ||
* `options` <[Object](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object)> | ||
* `cookies` <[boolean](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Data_structures#Boolean_type)> Will store the cookies from the first session in a json file and use them in consequent connections until they expire. This speeds up the return. Defaults to `false` | ||
* returns: <[Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)<[Array](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)>> Promise which resolves an array containing the list items | ||
@@ -37,2 +37,2 @@ | ||
[puppeteer](https://www.npmjs.com/package/puppeteer) | ||
[puppeteer](https://www.npmjs.com/package/puppeteer) |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
8133
6
143
0
37
3
2
+ Addedfs-extra@^5.0.0
+ Addedwinston@^3.2.1
+ Added@colors/colors@1.6.0(transitive)
+ Added@dabh/diagnostics@2.0.3(transitive)
+ Added@types/triple-beam@1.3.5(transitive)
+ Addedasync@3.2.5(transitive)
+ Addedcolor@3.2.1(transitive)
+ Addedcolor-convert@1.9.3(transitive)
+ Addedcolor-name@1.1.3(transitive)
+ Addedcolor-string@1.9.1(transitive)
+ Addedcolorspace@1.1.4(transitive)
+ Addedenabled@2.0.0(transitive)
+ Addedfecha@4.2.3(transitive)
+ Addedfn.name@1.1.0(transitive)
+ Addedfs-extra@5.0.0(transitive)
+ Addedgraceful-fs@4.2.11(transitive)
+ Addedis-arrayish@0.3.2(transitive)
+ Addedis-stream@2.0.1(transitive)
+ Addedjsonfile@4.0.0(transitive)
+ Addedkuler@2.0.0(transitive)
+ Addedlogform@2.6.0(transitive)
+ Addedone-time@1.0.0(transitive)
+ Addedreadable-stream@3.6.2(transitive)
+ Addedsafe-stable-stringify@2.4.3(transitive)
+ Addedsimple-swizzle@0.2.2(transitive)
+ Addedstack-trace@0.0.10(transitive)
+ Addedtext-hex@1.0.0(transitive)
+ Addedtriple-beam@1.4.1(transitive)
+ Addeduniversalify@0.1.2(transitive)
+ Addedwinston@3.13.0(transitive)
+ Addedwinston-transport@4.7.0(transitive)
Updatedpuppeteer@^1.17.0