Jul-17-2018, 07:20 AM
Hi all. I just finished writing a web crawler which sends emails through gmail. It's one of my first GUI based programs that almost resembles a finished product, so I thought it would be worth posting on here to get some feedback. It is quite buggy at the moment, but it's functional and that's good enough for me.
Here's the code:
This program also requires a text file which is formatted like this:
You will need to change line 407 to the path of this file on your computer for it to run properly.
Other than that, everything should work as intended and you'll be able to send bulk emails with ease.
Thanks for your time if you decide to try this program out.
Note: The annoy function waits until the minute value of current time changes to send each repeat, therefore if you have a repeat value of 5 the program will become unresponsive for 5 minutes.
Here's the code:
#Import necessary modules from selenium import webdriver from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions from tkinter import * import time #Centers tkinter window (https://stackoverflow.com/a/10018670) def center(win): win.update_idletasks() width = win.winfo_width() height = win.winfo_height() x = (win.winfo_screenwidth() // 2) - (width // 2) y = (win.winfo_screenheight() // 2) - (height // 2) win.geometry('{}x{}+{}+{}'.format(width, height, x, y)) #Create GUI def createGUILogin(oldRoot=False): global sendingEmail, sendingPassword, GUIText if oldRoot != False: oldRoot.destroy() readFile() root = Tk() root.title('Emailer GUI') root.geometry('320x250') center(root) #Login frame entry = Frame(root) entry.pack(anchor='center', padx=10, pady=10) emailText = Label(entry, text='Email:') emailText.grid(row=0, column=0) emailEntry = Entry(entry) emailEntry.grid(row=0, column=1) passwordText = Label(entry, text='Password:') passwordText.grid(row=1, column=0) passwordEntry = Entry(entry) passwordEntry.grid(row=1, column=1) defaultButton = Button(entry, text='Use default account') defaultButton['command'] = lambda:login(sendingEmail, sendingPassword) defaultButton.grid(row=0, column=2) loginButton = Button(entry, text='Login') loginButton['command'] = lambda:login(emailEntry.get(), passwordEntry.get()) loginButton.grid(row=1, column=2) #Update frame update = Frame(root) update.pack(anchor='center', padx=10, pady=10) GUILabel = Label(update, text='Update box:') GUILabel.grid(row=0, column=0) GUIText = Text(update, width=35, height=5, wrap=WORD) GUIText.grid(row=1, column=0, columnspan=3) #Buttons frame buttons = Frame(root) buttons.pack(anchor='center', padx=10, pady=10) logoutButton = Button(buttons, text='Logout') logoutButton['command'] = logout logoutButton.grid(row=2, column=0) exitButton = Button(buttons, text='Exit program') exitButton['command'] = root.destroy exitButton.grid(row=2, column=1) mainMenu = Button(buttons, text='Main menu') mainMenu['command'] = lambda:createGUIMenu(root) mainMenu.grid(row=2, column=2) root.mainloop() def createGUIMenu(oldRoot=False): global GUIText if oldRoot != False: oldRoot.destroy() readFile() root = Tk() root.title('Emailer GUI') root.geometry('200x130') center(root) #Options frame options = Frame(root) options.pack(anchor='center', padx=10, pady=10) annoyButton = Button(options, text='Annoy') annoyButton['command'] = lambda:createGUIAnnoy(root) annoyButton.pack() annoyButton = Button(options, text='Send') annoyButton['command'] = lambda:createGUISend(root) annoyButton.pack() #Buttons frame buttons = Frame(root) buttons.pack(anchor='center', padx=10, pady=10) exitButton = Button(buttons, text='Exit program') exitButton['command'] = root.destroy exitButton.grid(row=0, column=0) loginMenu = Button(buttons, text='Login screen') loginMenu['command'] = lambda:createGUILogin(root) loginMenu.grid(row=0, column=1) root.mainloop() def createGUIAnnoy(oldRoot=False): global GUIText, emailsList, message if oldRoot != False: oldRoot.destroy() readFile() root = Tk() root.title('Emailer GUI') root.geometry('350x400') center(root) #Emails frame emails = Frame(root) emails.pack(anchor='center', padx=10, pady=10) emailsString = '' for x in emailsList: emailsString += x + '\n' GUILabel = Label(emails, text='Emails list:') GUILabel.grid(row=0, column=0) emailsText = Text(emails, width=35, height=5, wrap=WORD) emailsText.grid(row=1, column=0, columnspan=3) emailsText.insert(0.0, emailsString) #Entry frame entry = Frame(root) entry.pack(anchor='center', padx=10, pady=10) messageLabel = Label(entry, text='Message:') messageLabel.grid(row=0, column=0) messageEntry = Entry(entry) messageEntry.grid(row=0, column=1) defaultMessageButton = Button(entry, text='Use default message') defaultMessageButton['command'] = lambda:annoyingRepeats(emailsText.get(0.0, END), subjectEntry.get(), message, repeatsEntry.get()) defaultMessageButton.grid(row=0, column=2) subjectLabel = Label(entry, text='Subject:') subjectLabel.grid(row=1, column=0) subjectEntry = Entry(entry) subjectEntry.grid(row=1, column=1) repeatsLabel = Label(entry, text='Repeats:') repeatsLabel.grid(row=2, column=0) repeatsEntry = Entry(entry) repeatsEntry.grid(row=2, column=1) annoyButton = Button(entry, text='Annoy') annoyButton['command'] = lambda:annoyingRepeats(emailsText.get(0.0, END), subjectEntry.get(), messageEntry.get(), repeatsEntry.get()) annoyButton.grid(row=2, column=2) #Buttons frame buttons = Frame(root) buttons.pack(anchor='center', padx=10, pady=10) mainMenu = Button(buttons, text='Main menu') mainMenu['command'] = lambda:createGUIMenu(root) mainMenu.grid(row=2, column=0) onTopButton = Button(buttons, text='Always on top') onTopButton['command'] = lambda:root.wm_attributes("-topmost", 1) onTopButton.grid(row=2, column=1) #Update frame update = Frame(root) update.pack(anchor='center', padx=10, pady=10) GUILabel = Label(update, text='Update box:') GUILabel.grid(row=0, column=0) GUIText = Text(update, width=35, height=5, wrap=WORD) GUIText.grid(row=1, column=0, columnspan=3) root.mainloop() def createGUISend(oldRoot=False): global GUIText, emailsList, message if oldRoot != False: oldRoot.destroy() readFile() root = Tk() root.title('Emailer GUI') root.geometry('320x290') center(root) #Entry frame entry = Frame(root) entry.pack(anchor='center', padx=10, pady=10) emailLabel = Label(entry, text='Email:') emailLabel.grid(row=0, column=0) emailEntry = Entry(entry) emailEntry.grid(row=0, column=1) subjectLabel = Label(entry, text='Subject:') subjectLabel.grid(row=1, column=0) subjectEntry = Entry(entry) subjectEntry.grid(row=1, column=1) messageLabel = Label(entry, text='Message:') messageLabel.grid(row=2, column=0) messageEntry = Entry(entry) messageEntry.grid(row=2, column=1) repeatsLabel = Label(entry, text='Repeats:') repeatsLabel.grid(row=3, column=0) repeatsEntry = Entry(entry) repeatsEntry.grid(row=3, column=1) sendButton = Button(entry, text='Send') sendButton['command'] = lambda:send(emailEntry.get(), subjectEntry.get(), messageEntry.get(), repeatsEntry.get()) sendButton.grid(row=3, column=2) #Buttons frame buttons = Frame(root) buttons.pack(anchor='center', padx=10, pady=10) mainMenu = Button(buttons, text='Main menu') mainMenu['command'] = lambda:createGUIMenu(root) mainMenu.grid(row=0, column=1) onTopButton = Button(buttons, text='Always on top') onTopButton['command'] = lambda:root.wm_attributes("-topmost", 1) onTopButton.grid(row=0, column=2) #Update frame update = Frame(root) update.pack(anchor='center', padx=10, pady=10) GUILabel = Label(update, text='Update box:') GUILabel.grid(row=0, column=0) GUIText = Text(update, width=35, height=5, wrap=WORD) GUIText.grid(row=1, column=0, columnspan=3) root.mainloop() #Find element function | http://isaacviel.name/make-web-driver-wait-element-become-visiable/ def findElement(xpath): global browser var = WebDriverWait(browser, 20).until( expected_conditions.visibility_of_element_located((By.XPATH, xpath))) return var #Login function def login(sendingEmail, sendingPassword): global browser, loggedIn if loggedIn == False: browser.get('https://mail.google.com') try: emailEntry = findElement('//input[@id="identifierId"]') emailEntry.send_keys(sendingEmail) nextButton = findElement('//div[@id="identifierNext"]') nextButton.click() passwordEntry = findElement('//input[@name="password"]') passwordEntry.send_keys(sendingPassword) nextButton = findElement('//span[text()="Next"]') nextButton.click() error('UPDATE: Logged in as ' + sendingEmail) loggedIn = True except: error('ERROR: Could not login as ' + sendingEmail) else: error('ERROR: Already logged in, please logout first') #Logout function def logout(): global browser, loggedIn if loggedIn == True: try: userPicture = findElement('//a[@href="https://accounts.google.com/SignOutOptions?hl=en&continue=https://mail.google.com/mail&service=mail"]') userPicture.click() signOutButton = findElement('//a[text()="Sign out"]') signOutButton.click() switchAccountsButton = findElement('//div[@aria-label="Switch account"]') switchAccountsButton.click() removeAccountButton = findElement('//button[text()="Remove an account"]') removeAccountButton.click() currentAccountButton = findElement('//div[@data-profileindex="0"]') currentAccountButton.click() removeConfirmButton = findElement('//div[text()="Yes, remove"]') removeConfirmButton.click() error('UPDATE: Logged out') loggedIn = False except: error('ERROR: Could not logout') else: error('ERROR: Already logged out, please login first') #Send mail function def send(email, subject, message, repeats): global browser, loggedIn if loggedIn == True: try: repeats = int(repeats) if repeats <= 0: error('ERROR: Please enter a positive integer as repeats') passed = False else: passed = True except: error('ERROR: Please enter an integer as repeats') passed = False if passed == True: try: for x in range(0, repeats): composeButton = findElement('//div[@gh="cm"]') composeButton.click() recipientEntry = findElement('//textarea[@aria-label="To"]') recipientEntry.send_keys(email) subjectEntry = findElement('//input[@name="subjectbox"]') subjectEntry.send_keys(subject) textEntry = findElement('//div[@aria-label="Message Body"]') textEntry.send_keys(message) sendButton = findElement('//div[@data-tooltip-delay="800"]') sendButton.click() error('UPDATE: ' + subject + ' sent to: ' + email) except: error('ERROR: Could not send ' + subject + ' to ' + email) else: error('ERROR: You must be logged in to send mail') #Sends emails in list a message every time clock changes def annoyingRepeats(emailsList, subject, message, repeats): global browser, finishedEmail, loggedIn if loggedIn == True: #Print emails and message to be sent if type(emailsList).__name__ != 'list': emailsList = [emailsList] #Check that repeats is valid try: repeats = int(repeats) if repeats <= 0: error('ERROR: Please enter a positive integer as repeats') passed = False else: error('UPDATE: You will be notified at {} when the annoying is finished'.format(finishedEmail)) passed = True except: error('ERROR: Please enter an integer as repeats') passed = False if passed == True: #Send all emails in list the message every time the clock changes prevMin = time.strftime("%M") for x in range(0, repeats): while True: minute = time.strftime("%M") if minute != prevMin: prevMin = minute for y in emailsList: try: send(y, subject + ' ' + str(x + 1), message, 1) except: break break #Send finishing email finishedText = 'UPDATE: You annoyed the following emails for {:0.2f} hours:'.format(repeats / 60) for x in emailsList: finishedText += '\n' + x send(finishedEmail, 'Annoying is finished', finishedText, 1) error(finishedText) else: error('ERROR: You must be logged in to annoy mail') #Read Email list.txt and get a list of emails and variables def readFile(): global emailsList, finishedEmail, sendingEmail, sendingPassword, message emailsList = [] file = open('Your file location') for x in file.readlines(): if len(x.strip()) == 0: continue elif len(x.split(' = ')) == 1: emailsList.append(list(x.split('\n'))[0]) elif len(x.split(' = ')) == 2: var, data = x.split(' = ') if var == 'finishedEmail': finishedEmail = data.strip() elif var == 'sendingEmail': sendingEmail = data.strip() elif var == 'sendingPassword': sendingPassword = data.strip() elif var == 'message': message = data.strip() else: error('ERROR: Unknown variable in file') else: error('ERROR: Unknown error in file') def error(text): global GUIText print(text) try: GUIText.delete(0.0, END) GUIText.insert(0.0, text) except: pass #Run functions loggedIn = False readFile() browser = webdriver.Chrome('Your driver location') createGUILogin() #Quit browser if GUI is closed try: browser.quit() except: passFor selenium to work you must first install the module, then change the code on line 442 to the location of your browser driver. The web driver for google chrome can be found at: https://sites.google.com/a/chromium.org/.../downloads
This program also requires a text file which is formatted like this:
Quote:finishedEmail = Email to send updates to
sendingEmail = Default login email
sendingPassword = Default login password
message = Default message
[email protected]
[email protected]
You will need to change line 407 to the path of this file on your computer for it to run properly.
Other than that, everything should work as intended and you'll be able to send bulk emails with ease.
Thanks for your time if you decide to try this program out.

Note: The annoy function waits until the minute value of current time changes to send each repeat, therefore if you have a repeat value of 5 the program will become unresponsive for 5 minutes.