Archive

Archive for the ‘PYTHON’ Category

Sending Emails Via Gmail SMTP With Python

December 3rd, 2010 No comments

python
This is the third post in the article series “Playing With Python And Gmail”. This will be a tutorial on how to send mails using Python smtplib through Gmail SMTP.

The smtplib module defines an SMTP client session object that can be used to send mail to any Internet machine with an SMTP or ESMTP listener daemon.

[vinod@mercury ~]$ python
Python 2.5.2 (r252:60911, Jan 24 2010, 14:53:14)
[GCC 4.3.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import smtplib

The first step is to create a SMTP connection to the server. The smtplib.SMTP class encapsulates an SMTP connection. It has methods that support a full repertoire of SMTP and ESMTP operations. If the optional host and port parameters are given, the SMTP connect() method is called with those parameters during initialization. An SMTPConnectError is raised if the specified host doesn’t respond correctly. The optional timeout parameter specifies a timeout in seconds for blocking operations like the connection attempt (if not specified, the global default timeout setting will be used).

Remember Google’s SMTP server is ‘smtp.gmail.com’ and the port is 587.

>>> session = smtplib.SMTP('smtp.gmail.com', 587)

Next we will identify ourself to an ESMTP server using EHLO. The SMTP HELO clause is the stage of the SMTP protocol where a SMTP server introduce them selves to each other. EHLO is just like HELO except that the server’s response text provides computer-readable information about the server’s abilities.

>>> session.ehlo()
(250, 'mx.google.com at your service, [x.x.x.x]\nSIZE 35651584\n8BITMIME\nSTARTTLS\nENHANCEDSTATUSCODES')

Next we call SMTP.starttls function to put the SMTP connection in TLS (Transport Layer Security) mode. All SMTP commands that follow will be encrypted. You should then call ehlo() again.

If keyfile and certfile are provided, these are passed to the socket module’s ssl() function.If there has been no previous EHLO or HELO command this session, this method tries ESMTP EHLO first.

>>> session.starttls()
(220, '2.0.0 Ready to start TLS')
>>> session.ehlo
>
>>>

OK, now we are safe to login to the server using SMTP.login(user, password). After successful login we use SMTP.sendmail(from_addr, to_addrs, msg[, mail_options, rcpt_options]) to send mails via Gmail.

>>> session.login('username@gmail.com', 'topsecret')
(235, '2.7.0 Accepted')

We will cerate some SMTP headers before sending our mail.

headers = ["from: " + sender,
           "subject: " + subject,
           "to: " + recipient,
           "mime-version: 1.0",
           "content-type: text/html"]
headers = "\r\n".join(headers)

Finally send our mail

session.sendmail(sender, recipient, headers + "\r\n\r\n" + body)

Sample code

#!/usr/bin/python
import smtplib
 
SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 587
 
sender = 'you@gmail.com'
recipient = 'user@example.com'
subject = 'Gmail SMTP Test'
body = 'blah blah blah'
 
"Sends an e-mail to the specified recipient."
 
body = "" + body + ""
 
headers = ["From: " + sender,
           "Subject: " + subject,
           "To: " + recipient,
           "MIME-Version: 1.0",
           "Content-Type: text/html"]
headers = "\r\n".join(headers)
 
session = smtplib.SMTP(server, port)
 
session.ehlo()
session.starttls()
session.ehlo
session.login(sender, password)
 
session.sendmail(sender, recipient, headers + "\r\n\r\n" + body)
session.quit()

Sending Attachments

The email package is a library for managing email messages, including MIME and other RFC 2822-based message documents. The central class in the email package is the Message class, imported from the email.message module. It is the base class for the email object model. Message provides the core functionality for setting and querying header fields, and for accessing message bodies. This is the base class for all the MIME-specific subclasses of Message. Ordinarily you won’t create instances specifically of MIMEBase, although you could. MIMEBase is provided primarily as a convenient base class for more specific MIME-aware subclasses.

We import MIMEMultipart class, a sub class of MIMEBase to create the enclosing (outer) message.

from email.MIMEMultipart import MIMEMultipart
 
msg = MIMEMultipart()
msg['From'] = sender
msg['To'] = recipient
msg['Subject'] = subject

The MIMEImage class is used to create MIME message objects of major image types. We create a MIMEImage object containing out raw image data and attach it to our outer message.

from email.MIMEImage import MIMEImage
 
img = MIMEImage(open("test.gif","rb").read(), _subtype="gif")
 
img.add_header('Content-Disposition', 'attachment; filename="test.gif"')
msg.attach(img)

Finally we create MIMEText object to add some plain text to our mail.The MIMEText class is used to create MIME objects of type text.

from email.MIMEText import MIMEText
 
part = MIMEText('text', "plain")
part.set_payload("Image attached.")
msg.attach(part)

Now send the mail

session.sendmail(sender, recipient, msg.as_string())

You can create MIME objects for audio, application etc. Find the documentation here.

A Simple Example

Send all .gif files in a directory as attachment.

#!/usr/bin/python
import os, re
import sys
import smtplib
 
from email.mime.image import MIMEImage
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
 
SMTP_SERVER = 'smtp.gmail.com'
SMTP_PORT = 587
 
sender = 'you@gmail.com'
password = "secret"
recipient = 'user@example.com'
subject = 'Python emaillib Test'
message = 'Images attached.'
 
directory = "/tmp/images/"
 
def main():
    msg = MIMEMultipart()
    msg['Subject'] = 'Python emaillib Test'
    msg['To'] = recipient
    msg['From'] = sender
 
    files = os.listdir(directory)
    gifsearch = re.compile(".gif", re.IGNORECASE)
    files = filter(gifsearch.search, files)
    for filename in files:
        path = os.path.join(directory, filename)
        if not os.path.isfile(path):
            continue
 
        img = MIMEImage(open(path, 'rb').read(), _subtype="gif")
        img.add_header('Content-Disposition', 'attachment', filename=filename)
        msg.attach(img)
 
    part = MIMEText('text', "plain")
    part.set_payload(message)
    msg.attach(part)
 
    session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
 
    session.ehlo()
    session.starttls()
    session.ehlo
    session.login(sender, password)
 
    session.sendmail(sender, recipient, msg.as_string())
    session.quit()
 
if __name__ == '__main__':
    main()
Categories: PYTHON Tags: , , ,

Playing With Python And CouchDB

November 4th, 2010 No comments

couchdb
Apache CouchDB is a document-oriented database that can be queried and indexed in a MapReduce fashion using JavaScript. CouchDB also offers incremental replication with bi-directional conflict detection and resolution.

CouchDB provides a RESTful JSON API than can be accessed from any environment that allows HTTP requests. There are myriad third-party client libraries that make this even easier from your programming language of choice. CouchDB’s built in Web administration console speaks directly to the database using HTTP requests issued from your browser.

CouchDB is written in Erlang, a robust functional programming language ideal for building concurrent distributed systems. Erlang allows for a flexible design that is easily scalable and readily extensible.

See the introduction and the technical overview for more information.

Getting Started

The couchdb.client.Server represents a CouchDB server. New databases can be created using the create method:

from couchdb.client import Server
 
server = Server('http://localhost:5984')
print server
try:
        db = server.create('emails')
except Exception:
        db = server['emails']
 
print db

Output:

<database 'emails'>

This class behaves like a dictionary of databases. For example, to get a list of database names on the server, you can simply iterate over the server object.

for db in server:
    print db

The out put will be something like

test
segfault
emails

Creating a Document

To define a document mapping, you declare a Python class inherited from Document, and add any number of Field attributes:

from couchdb.client import Server, Document
from couchdb.mapping import TextField, DateTimeField, ListField

Now you create subclass of Document and fill in the values.

class Email(Document):
  frm  = TextField()
  to = ListField(TextField())
  sub = TextField()
  added = DateTimeField(default=datetime.now())
 
eml = Email()
eml['frm'] = "vinod@example.com"
eml['to'] = "user@domain.com"
eml['sub'] = "test"

To update a document, simply set the attributes, and then call the save() method:

doc_id, doc_rev = db.save(eml)
print doc_id, doc_rev

Output:

8be7ef2e5d711f11e859972ca9d38a52 455397369

Retrieving documents

for docid in db:
  eml = db.get(docid)
  print eml['frm'], eml['to'], eml['sub']

Output:

vinod@example.com user@domain.com test

Working With Views

Views are the primary tool used for querying and reporting on CouchDB documents. There are two different kinds of views: permanent and temporary views.

Temporary Views

The views you don’t want to save in the CouchDB database. NOTE: Temporary views are only good during development. Final code should not rely on them as they are very expensive to compute each time they get called and they get increasingly slower the more data you have in a database.

code = '''function(doc) { if(doc.frm == "vinod@example.com") emit(doc.frm, null); }'''
results = db.query(code)
 
for res in results:
   print res.key
Permenant Views

Permanent views are stored inside special documents called design documents, and can be accessed via an HTTP GET request to the URI /{dbname}/{docid}/{viewname}, where {docid} has the prefix _design/ so that CouchDB recognizes the document as a design document, and {viewname} has the prefix _view/ so that CouchDB recognizes it as a view.

You use ViewDefinition class to create a permanent view in the database.

from couchdb.design import ViewDefinition
 
rpt_view = ViewDefinition('reports', 'fromemail', '''function(doc) { if(doc.frm == "vinod@example.com") emit(doc.frm, null); }''')
rpt_view.sync(db)

You can see our view in the drop down list.
couchdb

Query permanent views
for res in db.view("_design/reports/_view/fromemail"):
        print res.id, res.key

Output

8be7ef2e5d711f11e859972ca9d38a52 vinod@example.com

Installing Python-CouchDB

If you are a Debian/Ubuntu user install couchdb and python-couchdb via apt:

sudo aptitude install couchdb python-couchdb

Or you can download from python-couchdb project home.

Categories: PYTHON Tags: , ,

Shorten URLs using Python and bit.ly

October 5th, 2010 No comments

pythonLast time we found how to shorten URLs using Python and Google’s goo.gl URL shortening service. This time we will see how to use bit.ly’s api to shorten URLs. Here is the Python way of shortening/expanding URLs using using bit.ly. You will require a bit.ly user name and apikey to use this service. The apikey can be found here http://bit.ly/a/your_api_key. Also look at the complete bit.ly API Documentation.

#!/usr/bin/python
# use bit.ly's URL shortener
# requires urllib, urllib2, re, simplejson
 
try:
  from re import match
  from urllib2 import urlopen, Request, HTTPError
  from urllib import urlencode
  from simplejson import loads
except ImportError, e:
  raise Exception('Required module missing: %s' % e.args[0])
 
user = "username"
apikey  = "yourapikey"
 
def expand(url):
  try:
    params = urlencode({'shortUrl': url, 'login': user, 'apiKey': apikey, 'format': 'json'})
    req = Request("http://api.bit.ly/v3/expand?%s" % params)
    response = urlopen(req)
    j = loads(response.read())
    if j['status_code'] == 200:
      return j['data']['expand'][0]['long_url']
    raise Exception('%s'%j['status_txt'])
  except HTTPError, e:
    raise('HTTP Error%s'%e.read())
 
def shorten(url):
  try:
    params = urlencode({'longUrl': url, 'login': user, 'apiKey': apikey, 'format': 'json'})
    req = Request("http://api.bit.ly/v3/shorten?%s" % params)
    response = urlopen(req)
    j = loads(response.read())
    if j['status_code'] == 200:
      return j['data']['url']
    raise Exception('%s'%j['status_txt'])
  except HTTPError, e:
    raise('HTTP error%s'%e.read())
 
if __name__ == '__main__':
  from sys import argv
  if not match('http://',argv[1]):
    raise Exception('URL must start with "http://"')
  print shorten(argv[1])
Categories: PYTHON Tags: , , ,