Extract eMail addresses from eMail messages in Apple Mail

Today I needed a reliable way to extract eMail addresses from the content of eMail messages in Apple Mail. After fiddling around with AppleScript’s text item delimiters first, I finally came up with a robust Python helper script utilizing regular expressions.

To share this solution with you, I have created a sample script. The procedure is as follows:

The AppleScript iterates over selected eMail messages in Apple Mail and writes each message content to a temporary file. Then the Python script searches the temporary file for contained eMail addresses and prints them back to AppleScript. Fairly simple :smiley:

If you want to take a look at the sample script, please feel free to download it right here:

Extract eMail addresses (ca. 36.7 KB)

It was successfully tested on Intel and PowerPC based Macs running Mac OS X 10.5.3 & Apple Mail 3.3.

The Python script is based on a recipe found on the excellent ASPN: Python Cookbook website.

Important: Opening and saving the below script code in Script Editor won’t result in a usable AppleScript! That is because the AppleScript internally relies on a Python script, which is located inside its Script bundle. Therefor please download the complete script here.


-- created: 17.06.2008

-- This AppleScript iterates over selected eMail messages in Apple Mail
-- and tries to extract eMail addresses from the message content.

-- The AppleScript writes the message content to a temporary file, then a Python
-- script parses the temporary file for eMail addresses and prints them back
-- to the calling AppleScript. Finally the temporary file is removed.

property mytitle : "Extract eMail addresses"

-- I am called when the user opens the script with a double-click
on run
	try
		-- maybe the script was started without Apple Mail being running?
		if not my appisrunning("Mail") then
			set infomsg to "Sorry, Apple Mail is currently not running." & return & return & "Please open Apple Mail, select appropriate eMail messages and try again."
			my dspinfomsg(infomsg)
			return
		end if
		-- getting the selected eMail messages in Apple Mail
		tell application "Mail"
			set msgs to selection as list
		end tell
		-- did the user select any eMail messages?
		if msgs is {} then
			set errmsg to "You did not select any eMail messages in Apple Mail."
			my dsperrmsg(errmsg, "--")
			return
		end if
		-- processing the selected eMail messages
		repeat with msg in msgs
			tell application "Mail"
				set msgsubject to subject of msg
			end tell
			-- extracting the eMail addresses from the message content
			set mailaddrs to my extrmailaddrs(msg)
			-- no eMail addresses found...
			if mailaddrs is {} then
				
				set errmsg to "We could not detect eMail addresses in the content of the eMail message with the subject:" & return & return & msgsubject & return
				my dsperrmsg(errmsg, "--")
			else
				-- displaying the found eMail addresses
				choose from list mailaddrs with prompt "We found these eMail addresses in the eMail message content:" & return & return & "(" & msgsubject & ")" with title mytitle with empty selection allowed
			end if
		end repeat
	on error errmsg number errnum
		my dsperrmsg(errmsg, errnum)
	end try
end run

-- I am returning a list of eMail addresses embedded in the message content
-- If no addresses are found, I return an empty list
on extrmailaddrs(msg)
	-- getting the content of the eMail message
	tell application "Mail"
		set msgcont to content of msg
	end tell
	-- writing the content of the eMail message to a temporary file
	set tmpfilepath to my TmpFile's newpath()
	my writetofile(msgcont, tmpfilepath)
	-- extracting the eMail addresses from the temporary file using a Python script
	set pyscriptpath to POSIX path of (((path to me) as Unicode text) & "Contents:Resources:findmails.py")
	set command to "/usr/bin/python/ " & quoted form of pyscriptpath & " " & quoted form of (POSIX path of tmpfilepath)
	set mailaddrs to paragraphs of (do shell script command)
	-- removing the temporary file
	my TmpFile's remove()
	return mailaddrs
end extrmailaddrs

-- I am indicating if a given application is currently running or not
-- only the (full) application name must be given, e.g. "Address Book"
on appisrunning(appname)
	tell application "System Events"
		set processnames to name of every process
	end tell
	if appname is in processnames then
		return true
	else
		return false
	end if
end appisrunning

-- I am displaying error messages
on dsperrmsg(errmsg, errnum)
	tell me
		activate
		display dialog "Sorry, an error occured:" & return & return & errmsg & " (" & errnum & ")" buttons {"Never mind"} default button 1 with icon stop with title mytitle
	end tell
end dsperrmsg

-- I am displaying info messages
on dspinfomsg(infomsg)
	tell me
		activate
		display dialog infomsg buttons {"OK"} default button 1 with icon note with title mytitle
	end tell
end dspinfomsg

-- I am writing given content to a given file using UTF-8 text encoding
on writetofile(cont, filepath)
	try
		set openfile to open for access filepath with write permission
		set eof of openfile to 0
		set BOM_UTF8 to ((ASCII character 239) & (ASCII character 187) & (ASCII character 191))
		write (cont as «class utf8») to openfile
		close access openfile
		return true
	on error
		try
			close access openfile
		end try
		return false
	end try
end writetofile

-- script object to manage a temporary file
script TmpFile
	property filepath : missing value
	
	-- I am creating a new, not yet existing file path in the temp folder
	on newpath()
		set tmpfolderpath to (path to temporary items folder from user domain) as Unicode text
		repeat
			set rndnum to random number from 1000 to 99999
			set tmpfilepath to (tmpfolderpath & (rndnum as Unicode text) & ".tmp")
			try
				set tmpfilepath to tmpfilepath as alias
			on error
				set filepath to tmpfilepath
				exit repeat
			end try
		end repeat
		return filepath
	end newpath
	
	-- I am returning the file path of the temporary file 
	on getpath()
		return filepath
	end getpath
	
	-- I am trying to delete the temporary file using the Finder
	on remove()
		try
			set command to "rm " & quoted form of (POSIX path of filepath)
			do shell script command
		end try
	end remove
end script

Hi!
Wow, thanks! This was just what I was looking for. I’m a colplete newbie at applescript, but I added some lines that creates an outgoing message with the addresses found in the mail as recipients. As you can tell by my un-stringent coding this is just a schmack-hack.
I use this for my forwarded mail from Exchange to gmail, where the from-address gets moved into the body of the mail. Now all I need is a button in applemail to activate this script… (Anyone knows how to do this?)
Regds,
Martin Bergman

PS. Eg I could probably have used count in the first repeat as well…

-- created: 17.06.2008

-- This AppleScript iterates over selected eMail messages in Apple Mail
-- and tries to extract eMail addresses from the message content.

-- The AppleScript writes the message content to a temporary file, then a Python
-- script parses the temporary file for eMail addresses and prints them back
-- to the calling AppleScript. Finally the temporary file is removed.

property mytitle : "Extract eMail addresses"



-- I am called when the user opens the script with a double-click
on run
	try
		-- maybe the script was started without Apple Mail being running?
		if not my appisrunning("Mail") then
			set infomsg to "Sorry, Apple Mail is currently not running." & return & return & "Please open Apple Mail, select appropriate eMail messages and try again."
			my dspinfomsg(infomsg)
			return
		end if
		-- getting the selected eMail messages in Apple Mail
		tell application "Mail"
			set msgs to selection as list
		end tell
		-- did the user select any eMail messages?
		if msgs is {} then
			set errmsg to "You did not select any eMail messages in Apple Mail."
			my dsperrmsg(errmsg, "--")
			return
		end if
		-- processing the selected eMail messages
		repeat with msg in msgs
			tell application "Mail"
				set msgsubject to subject of msg
			end tell
			-- extracting the eMail addresses from the message content
			set mailaddrs to my extrmailaddrs(msg)
			-- no eMail addresses found...
			if mailaddrs is {} then
				
				set errmsg to "We could not detect eMail addresses in the content of the eMail message with the subject:" & return & return & msgsubject & return
				my dsperrmsg(errmsg, "--")
			else
				-- displaying the found eMail addresses
				choose from list mailaddrs with prompt "We found these eMail addresses in the eMail message content:" & return & return & "(" & msgsubject & ")" with title mytitle with empty selection allowed
				
				-- this was added to the script, makes a new mail with adresses in the to-field
				
				set excludes to {}
				set exclu to ""
				tell application "Mail"
					repeat with i from 1 to the number of items in accounts
						set exclu to email addresses of item i of accounts as text
						if excludes does not contain exclu then
							set excludes to excludes & {exclu}
						end if
					end repeat
				end tell
				
				set sendlist to {}
				
				repeat with i from 1 to count mailaddrs
					if {mailaddrs's item i} is not in excludes then set sendlist's end to mailaddrs's item i
				end repeat
				
				if sendlist is {} then
					
					set errmsg to "Only your own addresses in mail" & return & return & msgsubject & return
					my dsperrmsg(errmsg, "--")
				else
					
					tell application "Mail"
						set AppleScript's text item delimiters to ", "
						set sendlisttext to sendlist as text
						set AppleScript's text item delimiters to ""
						set newMessage to make new outgoing message
						tell newMessage
							set visible to true
							make new to recipient with properties {address:sendlisttext}
						end tell
						activate
					end tell
				end if
			

--end of my addings...



end if
		end repeat
	on error errmsg number errnum
		my dsperrmsg(errmsg, errnum)
	end try
end run

-- I am returning a list of eMail addresses embedded in the message content
-- If no addresses are found, I return an empty list
on extrmailaddrs(msg)
	-- getting the content of the eMail message
	tell application "Mail"
		set msgcont to content of msg
	end tell
	-- writing the content of the eMail message to a temporary file
	set tmpfilepath to my TmpFile's newpath()
	my writetofile(msgcont, tmpfilepath)
	-- extracting the eMail addresses from the temporary file using a Python script
	set pyscriptpath to POSIX path of (((path to me) as Unicode text) & "Contents:Resources:findmails.py")
	set command to "/usr/bin/python " & quoted form of pyscriptpath & " " & quoted form of (POSIX path of tmpfilepath)
	set mailaddrs to paragraphs of (do shell script command)
	-- removing the temporary file
	my TmpFile's remove()
	return mailaddrs
end extrmailaddrs

-- I am indicating if a given application is currently running or not
-- only the (full) application name must be given, e.g. "Address Book"
on appisrunning(appname)
	tell application "System Events"
		set processnames to name of every process
	end tell
	if appname is in processnames then
		return true
	else
		return false
	end if
end appisrunning

-- I am displaying error messages
on dsperrmsg(errmsg, errnum)
	tell me
		activate
		display dialog "Sorry, an error occured:" & return & return & errmsg & " (" & errnum & ")" buttons {"Never mind"} default button 1 with icon stop with title mytitle
	end tell
end dsperrmsg

-- I am displaying info messages
on dspinfomsg(infomsg)
	tell me
		activate
		display dialog infomsg buttons {"OK"} default button 1 with icon note with title mytitle
	end tell
end dspinfomsg

-- I am writing given content to a given file using UTF-8 text encoding
on writetofile(cont, filepath)
	try
		set openfile to open for access filepath with write permission
		set eof of openfile to 0
		set BOM_UTF8 to ((ASCII character 239) & (ASCII character 187) & (ASCII character 191))
		write (cont as «class utf8») to openfile
		close access openfile
		return true
	on error
		try
			close access openfile
		end try
		return false
	end try
end writetofile

-- script object to manage a temporary file
script TmpFile
	property filepath : missing value
	
	-- I am creating a new, not yet existing file path in the temp folder
	on newpath()
		set tmpfolderpath to (path to temporary items folder from user domain) as Unicode text
		repeat
			set rndnum to random number from 1000 to 99999
			set tmpfilepath to (tmpfolderpath & (rndnum as Unicode text) & ".tmp")
			try
				set tmpfilepath to tmpfilepath as alias
			on error
				set filepath to tmpfilepath
				exit repeat
			end try
		end repeat
		return filepath
	end newpath
	
	-- I am returning the file path of the temporary file 
	on getpath()
		return filepath
	end getpath
	
	-- I am trying to delete the temporary file using the Finder
	on remove()
		try
			set command to "rm " & quoted form of (POSIX path of filepath)
			do shell script command
		end try
	end remove
end script

I can’t seem to get this script to work. I’m pretty ignorant, so could you walk me through the process step by step?

Currently, I highlight all of the emails from which I want to extract email addresses.
Then I click your link that says to open in script editor. I have tried two ways: Opening in script editor choosing no application, and doing so choosing Mail. In both cases the script window comes up with the script.
I double click run. It appears to run until stopping with an error:

Sorry, an error occured:
/usr/bin/python/: can’t open file ‘/
Applications/AppleScript/Script Editor.app/Contents/Resources/findmails.py’(2)
Never mind

Any ideas what I need to do?

Model: iMac Intel
AppleScript: 2.1.2
Browser: Firefox 3.0.10
Operating System: Mac OS X (10.4)

As already written above, you cannot just paste the script code into an open Script editor window and execute it, because the AppleScript internally relies on a Python script, which is located inside its Script bundle. Therefor please download the complete script here.

Once you downloaded the script, you only need to select one or more messages in Apple Mail and then run the script by double-clicking on its icon.

That’s it :wink:

Thanks for the link and instructions. I followed them as you suggested and this is the error message:

Sorry, an error occured

Traceback (most recent call last): File “/Users/…/Extract eMail addresses.app/Contents/Resources/findmails.py”,line 52, in ? main()
File “/Users/…/Extract eMail addresses.app/Contents/Resources/findmails.py”,line 47, in main mailaddres = extrmailaddrs ([filepath])
File “Users/…/Extract eMail addresses/Extract eMail addresses.app/Contents/Resources/findmails.py”, line 24, in extrmailaddrs found = set()
NameError: global name ‘set’ is not defined (1)

Your thoughts?

You are not running Leopard, are you? :slight_smile: I guess you are running Mac OS X 10.4, where the script currently does not work (due to the Python version included).

That is a real bummer.
Thanks for clarifying.

Than you very very much!! This is what I’m looking for. I’m a newbie to Apple script, is there a way to copy the email in a text file one after the other? We get a lot of newsletter subscription emails and it would be “life changing” if we could automate this process by automatically screening email messages, copying the subscriber’s emails and pasting it in a text file like this:

email1@email.com, email2@email.com, email3@email.com, etc…

Thanks!

create a file named file.txt on your desktop.
Then open Apple Mail and set up a mail rule in and then in apply action, select “Run Applescript” and give the location to this applescript.

using terms from application "Mail"
	on perform mail action with messages theMessages for rule theRule
		tell application "Mail"
			repeat with eachMessage in theMessages
				set thesender to sender of eachMessage
				
			end repeat
		end tell
		set thesender to thesender & ","
		set theshellscript to "echo " & quoted form of thesender & " >> ~/Desktop/file.txt"
		do shell script theshellscript
	end perform mail action with messages
	end using terms from