Recipe 10.16. Reading Microsoft Outlook Contacts
Credit: Kevin Altis
Problem
Your Microsoft Outlook Contacts
house a wealth of useful information, and you need to extract some of
it in text form.
Solution
Like many other problems of system administration on Windows, this
one is best approached by using COM. The most popular way to
interface Python to COM is to use the win32com
package, which is part of Mark Hammond's
pywin32 extension
package:
from win32com.client import gencache, constants
DEBUG = False
class MSOutlook(object):
def _ _init_ _(self):
try:
self.oOutlookApp = gencache.EnsureDispatch("Outlook.Application")
self.outlookFound = True
except:
print "MSOutlook: unable to load Outlook"
self.outlookFound = False
self.records = [ ]
def loadContacts(self, keys=None):
if not self.outlookFound: return
onMAPI = self.oOutlookApp.GetNamespace("MAPI")
ofContacts = onMAPI.GetDefaultFolder(constants.olFolderContacts)
if DEBUG: print "number of contacts:", len(ofContacts.Items)
for oc in range(len(ofContacts.Items)):
contact = ofContacts.Items.Item(oc + 1)
if contact.Class == constants.olContact:
if keys is None:
# no keys were specified, so build up a list of all keys
# that belong to some types we know we can deal with
good_types = int, str, unicode
keys = [key for key in contact._prop_map_get_
if isinstance(getattr(contact, key), good_types) ]
if DEBUG:
print "Fields\n== == == == == == == == == == == =="
keys.sort( )
for key in keys: print key
record = { }
for key in keys:
record[key] = getattr(contact, key)
self.records.append(record)
if DEBUG:
print oc, contact.FullName
if _ _name_ _ == '_ _main_ _':
if '-d' in sys.argv:
DEBUG = True
if DEBUG:
print "attempting to load Outlook"
oOutlook = MSOutlook( )
if not oOutlook.outlookFound:
print "Outlook not found"
sys.exit(1)
fields = ['FullName', 'CompanyName',
'MailingAddressStreet', 'MailingAddressCity',
'MailingAddressState', 'MailingAddressPostalCode',
'HomeTelephoneNumber', 'BusinessTelephoneNumber',
'MobileTelephoneNumber', 'Email1Address', 'Body',
]
if DEBUG:
import time
print "loading records..."
startTime = time.time( )
# to get all fields just call oOutlook.loadContacts( )
# but getting a specific set of fields is much faster
oOutlook.loadContacts(fields)
if DEBUG:
print "loading took %f seconds" % (time.time( ) - startTime)
print "Number of contacts: %d" % len(oOutlook.records)
print "Contact: %s" % oOutlook.records[0]['FullName']
print "Body:\n%s" % oOutlook.records[0]['Body']
Discussion
This recipe's code could use more error-checking,
and you could get it by using nested
TRy/except blocks, but I
didn't want to obscure the code's
fundamental simplicity in this recipe. This recipe should work with
different versions of Outlook, but I've tested it
only with Outlook 2000. If you have applied the Outlook security
patches then you will be prompted with a dialog requesting access to
Outlook for 1-10 minutes from an external program, which in this case
is Python.The code has already been optimized in two important ways. First, by
ensuring that the Python COM wrappers for Outlook have been
generated, which is guaranteed by calling
gencache.EnsureDispatch. Second, in the loop that
reads the contacts, the Contact reference is
obtained only once and then kept in a local variable
contact to avoid repeated references. This simple
but crucial optimization is the role of the statement:
contact = ofContacts.Items.Item(oc + 1)Both of these optimizations have a dramatic impact on total import
time, and both are important enough to keep in mind. Specifically,
the EnsureDispatch idea is important for most uses
of COM in Python; the concept of getting an object reference, once,
into a local variable (rather than repeating indexing, calls, and
attribute accesses) is even more important and applies to
every use of Python.Simple variations of this script can be applied to other elements of
the Outlook object model such as the Calendar and Tasks.
You'll want to look at the Python wrappers generated
for Outlook in the
C:\Python23\Lib\site-packages\win32com\gen_py
directory. I also suggest that you look at the Outlook object model
documentation on MSDN and/or pick up a book on the subject.
See Also
PyWin32 docs at http://sourceforge.net/projects/pywin32/;
Microsoft's MSDN site, http://msdn.microsoft.com.