I just finished creating my first news web scraping script and I am quite content with it even though the code does not look nice at all. I was wondering how I should go about sending the output of the script to myself via email (gmail address) when I run it. I tried to run smtplib, but it's not working for me.
Here is my current code:
from urllib.request import urlopen as uReq
from bs4 import BeautifulSoup as soup
from datetime import date
from dateutil import parser
import smtplib
from email.mime.text import MIMEText
my_url1 = "https://www.coindesk.com/category/business-news/legal"
my_url2 = "https://cointelegraph.com/tags/bitcoin-regulation"
my_url3 = "https://techcrunch.com/tag/blockchain/"
# Opening up the website, grabbing the page
uFeedOne = uReq(my_url1, timeout=5)
page_one = uFeedOne.read()
uFeedOne.close()
# html parser
page_soup1 = soup(page_one, "html.parser")
# grabs each publication block
containers_one = page_soup1.findAll("a", {"class": "stream-article"} )
for container_one in containers_one:
## get todays date.
## I have taken an offset as the site has older articles than today.
today = date.today().strftime("%Y, %m, %d")
## The actual datetime string is in the datetime attribute of the time tag.
date_time1 = container_one.time['datetime']
## The dateutil package parses the ISO-formatted date and returns a condensed version.
date1 = parser.parse(date_time1)
dt1 = date1.strftime("%Y, %m, %d")
## Simple comparison
if dt1 == today:
link1 = container_one.attrs['href']
publication_date1 = "published on " + container_one.time.text
title1 = container_one.h3.text
description1 = "(CoinDesk)-- " + container_one.p.text
print("link: " + link1)
print("publication_date: " + publication_date1)
print("title: ", title1)
print("description: " + description1 + " \n")
uFeedTwo = uReq(my_url2, timeout=5)
page_two = uFeedTwo.read()
uFeedTwo.close()
page_soup2 = soup(page_two, "html.parser")
containers_two = page_soup2.findAll("div",{"class": "post-preview-item-inline__content"})
for container_two in containers_two:
today = date.today().strftime("%Y, %m, %d")
date_time2 = container_two.time['datetime']
date2 = parser.parse(date_time2)
dt2 = date2.strftime("%Y, %m, %d")
title_container2 = container_two.find("span",{"class": "post-preview-item-inline__title"})
description_container2 = container_two.find("p",{"class": "post-preview-item-inline__text"}).text
if dt2 == today:
link2 = container_two.div.a.attrs['href']
publication_date2 = "published on " + date2.strftime("%b %d, %Y")
title2 = title_container2.text
description2 = "(CoinTelegraph)-- " + description_container2
print("link: " + link2)
print("publication_date: " + publication_date2)
print("title: ", title1)
print("description: " + description2 + " \n")
uFeedThree = uReq(my_url3, timeout=5)
page_three = uFeedThree.read()
uFeedThree.close()
# html parser
page_soup3 = soup(page_three, "html.parser")
# grabs each publication block
containers_three = page_soup3.findAll("div",{"class": "post-block post-block--image post-block--unread"})
for container_three in containers_three:
today = date.today().strftime("%Y, %m, %d")
date_time3 = container_three.time['datetime']
date3 = parser.parse(date_time3)
dt3 = date3.strftime("%Y, %m, %d")
keyword1 = "law"
keyword2 = "legal"
description_container3 = container_three.find("div", {"class": "post-block__content"}).text.strip()
if dt3 == today and (keyword2 in description_container3) or (keyword1 in description_container3):
link3 = container_three.header.h2.a.attrs['href']
publication_date3 = "published on " + date3.strftime("%b %d, %Y")
title3 = container_three.header.h2.a.text.strip()
description3 = "(TechCrunch)-- " + description_container3
print("link: " + link3)
print("publication_date: " + publication_date3)
print("title: ", title3)
print("description: " + description3 + " \n")
I understand that I am suppose to do a variation of this:
# Open a plain text file for reading. For this example, assume that
# the text file contains only ASCII characters.
with open(textfile) as fp:
# Create a text/plain message
msg = MIMEText(fp.read())
# me == the sender's email address
# you == the recipient's email address
msg['Subject'] = 'The contents of %s' % textfile
msg['From'] = me
msg['To'] = you
# Send the message via our own SMTP server.
s = smtplib.SMTP('localhost')
s.send_message(msg)
s.quit()
This is the code snippet to send a mail to anyone using SMTP.
Below code is configured for gmail SMT P.If you have any other it can
be configred.
import smtplib
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
msg = MIMEMultipart()
msg['From'] = 'me#gmail.com'
msg['To'] = 'you#gmail.com'
msg['Subject'] = 'Enter subjecy of msg here'
message = 'here is the email'
msg.attach(MIMEText(message))
# GMAIL_SMTP_HOST = 'smtp.gmail.com'
# GMAIL_SMTP_PORT = '587'
mailserver = smtplib.SMTP('smtp.gmail.com',587)
# secure our email with tls encryption
mailserver.starttls()
mailserver.sendmail('me#gmail.com','you#gmail.com',msg.as_string())
mailserver.quit()
Related
I have a script that will run periodically to send email invitations to all receivers to inform them about upcoming maintenance.
Here is the code example
import os
import uuid
import smtplib
import icalendar
import datetime
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email import encoders
import pytz
from jinja2 import FileSystemLoader, Environment
class EmailWriter:
SMTP = 'smtp.test.com'
def __init__(self, receivers, cluster_name, dtstart=None, dtend=None, available="", tasks=""):
self.sender = 'sender#test.com'
self.smtp = smtplib.SMTP(EmailWriter.SMTP)
self.receivers = receivers
self.cluster_name = cluster_name
self.dtstart = dtstart
self.dtend = dtend
self.available = available
self.tasks = tasks
def __get_email_subject_and_content(self):
path = os.path.join(os.getcwd(), 'email_templates')
loader = FileSystemLoader(path)
env = Environment(loader=loader)
template_minor = env.get_template('minor_maintenance_email.html')
template_major = env.get_template('major_maintenance_email.html')
if 'unavailability' in self.available.lower():
html_content = template_major.render(
availability=self.available,
maintenance_date=self.dtstart,
start_time=self.dtstart,
expected_end_time=self.dtend,
tasks=self.tasks
)
subject = '{} | Maintenance | {}'.format(self.cluster_name, self.available)
else:
html_content = template_minor.render()
subject = '{} | Maintenance | 100% Availability'.format(self.cluster_name)
print('subject : "{}", receivers : "{}", maintenance_date : "{}", start_time : "{}", expected_end_time : "{}", '
'"task : "{}"'.format(subject, self.receivers, self.dtstart, self.dtstart, self.dtend, self.tasks))
return subject, html_content
def __prepare_event(self, subject, content, start, end):
event = icalendar.Event()
organizer = icalendar.vCalAddress('MAILTO:' + self.sender)
event.add('organizer', organizer)
event.add('status', 'confirmed')
event.add('category', 'Event')
event.add('summary', subject)
event.add('description', content)
event.add('dtstart', start)
event.add('dtend', end)
event.add('dtstamp', datetime.datetime.now())
event['uid'] = uuid.uuid4()
# Set the busy status of the appointment to free
event.add('X-MICROSOFT-CDO-BUSYSTATUS', icalendar.vText('FREE'))
event.add('priority', 5)
event.add('sequence', 0)
event.add('created', datetime.datetime.now())
for participant in self.receivers:
attendee = icalendar.vCalAddress('MAILTO:' + participant)
attendee.params['ROLE'] = icalendar.vText('REQ-PARTICIPANT')
attendee.params['cn'] = icalendar.vText(' '.join(participant.split('#')[0].split('.')))
event.add('attendee', attendee, encode=0)
return event
def __prepare_alarm(self):
alarm = icalendar.Alarm()
alarm.add('action', 'DISPLAY')
alarm.add('description', 'Reminder')
# The only way to convince Outlook to do it correctly
alarm.add('TRIGGER;RELATED=START', '-PT{0}H'.format(1))
return alarm
def __prepare_icalendar(self):
# Build the event itself
cal = icalendar.Calendar()
cal.add('prodid', icalendar.vText('-//Calendar Application//'))
cal.add('version', icalendar.vInt(2.0))
cal.add('method', icalendar.vText('REQUEST'))
# creates one instance of the event
cal.add('X-MS-OLK-FORCEINSPECTOROPEN', icalendar.vBoolean(True))
return cal
def __prepare_email_message(self, subject, content):
# Build the email message
# msg = MIMEMultipart('alternative')
msg = MIMEMultipart('mixed')
msg['Subject'] = subject
msg['From'] = self.sender
msg['To'] = ';'.join(self.receivers)
msg['Content-class'] = 'urn:content-classes:calendarmessage'
msg.attach(MIMEText(content, 'html', 'utf-8'))
return msg
def __prepare_invite_blocker(self, cal):
filename = 'invite.ics'
part = MIMEBase('text', 'calendar', method='REQUEST', name=filename)
part.set_payload(cal.to_ical())
encoders.encode_base64(part)
part.add_header('Content-Description', filename)
part.add_header('Filename', filename)
part.add_header('Path', filename)
return part
def send_appointment(self):
subject, html_content = self.__get_email_subject_and_content()
start = datetime.datetime.combine(self.dtstart, datetime.time(0, 0, 0)).astimezone(pytz.UTC)
end = datetime.datetime.combine(self.dtend, datetime.time(0, 0, 0)).astimezone(pytz.UTC)
cal = self.__prepare_icalendar()
event = self.__prepare_event(subject, html_content, start, end)
alarm = self.__prepare_alarm()
# Add a reminder
event.add_component(alarm)
cal.add_component(event)
part = self.__prepare_invite_blocker(cal)
msg = self.__prepare_email_message(subject, html_content)
msg.attach(part)
# Send the email out
self.smtp.sendmail(msg["From"], [msg["To"]], msg.as_string())
self.smtp.quit()
print('Invitation sent out')
def main():
receivers = ['test1#test.com', 'test2#test.com', 'test3#test.com']
cluster_name = 'TEST NOW (test_now)' # test cluster name
email_writer = EmailWriter(
receivers,
cluster_name,
datetime.datetime.strptime('2023-02-16', '%Y-%m-%d').date(),
datetime.datetime.strptime('2023-02-16', '%Y-%m-%d').date() + datetime.timedelta(days=1),
'100% Availability',
tasks='Minor test'
)
print('Sending email')
email_writer.send_appointment()
if __name__ == '__main__':
main()
However, when I tested the code, I could see only the first recipient in the receivers list can get the outlook invitation.
How to fix the code to let all email account in the list can get the invitation?
Looking at some other examples, it looks like the msg['To'] object needs to be in a string format with a delimiter of ',' I believe you are using ';' try changing that in your code and see if that resolves the issue.
current_code: msg['To'] = ';'.join(self.receivers)
new_code: msg['To'] = ', '.join(self.receivers)
Thank you for your help!
Finally, I got a solution here.
First, as the above answer said, I need to use , as the delimiter.
msg['To'] = ', '.join(self.receivers)
Second, in the function of smtp.sendmail(), the receiver parameter type is a list, so here I directly give the function a list
self.smtp.sendmail(msg['From'], self.receivers, msg.as_string())
Source code of sendmail()
def sendmail(self, from_addr, to_addrs, msg, mail_options=(),
rcpt_options=()):
"""This command performs an entire mail transaction.
The arguments are:
- to_addrs : A list of addresses to send this mail to. A bare
string will be treated as a list with 1 address.
Example:
>>> import smtplib
>>> s=smtplib.SMTP("localhost")
>>> tolist=["one#one.org","two#two.org","three#three.org","four#four.org"]
>>> msg = '''\\
... From: Me#my.org
... Subject: testin'...
...
... This is a test '''
>>> s.sendmail("me#my.org",tolist,msg)
I am trying to save email from sub-folder using the below python script, I am trying to restrict with days=1 means I only need to save emails which are 1 day old.
from win32com.client import Dispatch
from datetime import date, timedelta
import datetime as dt
msg_location = r'C:\Users\rahul\Desktop\msg_files'
outlook = Dispatch("outlook.Application").GetNamespace("MAPI")
inbox = outlook.GetDefaultFolder(6).Folders['Email_snapper']
messages = inbox.Items
message = messages.GetFirst()
Body_content = message.Body
print(Body_content)
for msg in messages:
lastWeekDateTime = dt.datetime.now() - dt.timedelta(days=1)
lastWeekDateTime = lastWeekDateTime.strftime('%m/%d/%Y %H:%M %p')
message = messages.Ryestrict("[ReceivedTime] >= '" + lastWeekDateTime + "'")
#name = str(message)
message.SaveAs(msg+".msg")
Try setting your filter like the following
Example
import re
import win32com.client
import datetime as dt
import os
Outlook = win32com.client.Dispatch("Outlook.Application")
olNs = Outlook.GetNamespace("MAPI")
Inbox = olNs.GetDefaultFolder(6)
lastWeekDateTime = dt.datetime.now() - dt.timedelta(days=1)
lastWeekDateTime = lastWeekDateTime.strftime('%m/%d/%Y %H:%M %p')
print(lastWeekDateTime)
Filter = ("#SQL=" + chr(34) + "urn:schemas:httpmail:datereceived" +
chr(34) + " >= '" + lastWeekDateTime + "'")
Items = Inbox.Items.Restrict(Filter)
Items.Sort('[ReceivedTime]', False)
for Item in Items:
print(Item.Subject)
print(Item.ReceivedTime)
save_name = re.sub('[^A-Za-z0-9]+', '', str(Item.Subject)) + '.msg'
Item.SaveAs(os.getcwd() + '//' + save_name)
else:
print("No Item")
Firstly, it is Restrict, not Ryestrict.
Secondly, Restrict returns Items collection, not a single item. You need to iterate over the items in that collection. If you only expect a single item, use Find instead of Restrict.
I'm trying to parse emails from Outlook.
I would like the following printed:
subject
body (excluding sender's signature)
Ignore all previous emails from conversion (reply & forward)
Is there any way I can print out the body text before multi-space between lines (usually this is how signature being separated from the main text)?
Any help would be appreciated!
import win32com.client
#other libraries to be used in this script
import os
from datetime import datetime, timedelta
outlook = win32com.client.Dispatch('outlook.application')
mapi = outlook.GetNamespace("MAPI")
for account in mapi.Accounts:
print(account.DeliveryStore.DisplayName)
inbox = mapi.GetDefaultFolder(6)
messages = inbox.Items
messages.Sort('[ReceivedTime]', True)
received_dt = datetime.now() - timedelta(days=1)
received_dt = received_dt.strftime('%m/%d/%Y %H:%M %p')
messages = messages.Restrict("[ReceivedTime] >= '" + received_dt + "'")
messages = messages.Restrict("[SenderEmailAddress] = 'firstname.lastname#gmail.com'")
message = messages.GetFirst()
print ("Current date/time: "+ received_dt)
while message:
print(message.Subject)
print(message.body)
message = messages.GetNext ()
You can use a regex to ignore everything after three newlines (there are normally one or two newlines between paragraphs):
import re
r = re.compile(r"(.*)\n\n\n", re.MULTILINE + re.DOTALL)
# ...
while message:
# ...
match = r.match(message.body)
if match:
body_without_signature = r.match(message.body).groups(0)
else:
# No signature found
body_without_signature = message.body
print(body_without_signature)
I'm trying to send and email based on High temperature or humidity but i cant figure out how to add this. Its my first time using DHT22 with Raspberry Pi so not sure how to structure my code.
I've tried a variety of codes others have suggested but they either no longer work on Python 3 (originally Python 2 - depreciated), or the code I've written just doesn't do anything except monitor and log with no email on high temp.
My original coding so far is this:
import os
import time
from time import sleep
from datetime import datetime
import Adafruit_DHT
file = open("/home/pi/TempHumLog.csv", "a")
if os.stat("/home/pi/TempHumLog.csv").st_size == 0:
file.write("Date,Time,Temperature,Humidity\n")
while True:
DHT_SENSOR = Adafruit_DHT.DHT22
DHT_PIN = 4
temperature, humidity = Adafruit_DHT.read_retry(DHT_SENSOR, DHT_PIN)
file.write("{0},{1},{3:0.2f}*C,{2:0.2f}%rh\n".format(time.strftime("%d/%m/%y"), time.strftime("%H:%M:%S"), temperature, humidity))
file.flush()
print("{0},{1},{3:0.2f}*C,{2:0.2f}%rh\n".format(time.strftime("%d/%m/%y"), time.strftime("%H:%M:%S"), temperature, humidity))
time.sleep(5)
import smtplib
#Email Variables
SMTP_SERVER = 'smtp.gmail.com' #Email Server (don't change!)
SMTP_PORT = 587 #Server Port (don't change!)
GMAIL_USERNAME = 'example#gmail.com' #change this to match your gmail account
GMAIL_PASSWORD = 'example pw' #change this to match your gmail password
class Emailer:
def sendmail(self, recipient, subject, content):
#Create Headers
headers = ["From: " + GMAIL_USERNAME, "Subject: " + subject, "To: " + recipient,
"MIME-Version: 1.0", "Content-Type: text/html"]
headers = "\r\n".join(headers)
#Connect to Gmail Server
session = smtplib.SMTP(SMTP_SERVER, SMTP_PORT)
session.ehlo()
session.starttls()
session.ehlo()
#Login to Gmail
session.login(GMAIL_USERNAME, GMAIL_PASSWORD)
#Send Email & Exit
session.sendmail(GMAIL_USERNAME, recipient, headers + "\r\n\r\n" + content)
session.quit
sender = Emailer()
while True:
if temperature > 24:
sendTo = 'example#gmail.com'
emailSubject = "High Temp!"
emailContent = "<br>A High Temp has Activated At: " + time.ctime() + "<br><br>Check The Room Temperature Levels"
sender.sendmail(sendTo, emailSubject, emailContent)
print("Email Sent")
elif temperature < 23:
sendTo = 'example#gmail.com'
emailSubject = "Room Temp Healthy"
emailContent = "High Room Temp Alarm Has Cleared At: " + time.ctime()
sender.sendmail(sendTo, emailSubject, emailContent)
print("Email Sent")
time.sleep(5)
At this time, no errors come through the terminal but it doesn't send any email. I've tried adding something like this:
instance = dht22.DHT22(pin=4)
while True:
result = instance.read()
tempHI = 26
tempLOW = 19
if (result.temperature) > tempHI:
**Send Email Script**
But no luck!
Any ideas how i can get the high temperature to trigger the email?
I am currently trying to make a program that send multiple emails to my self in a loop. I have already written 2 patches of code but i can not seem to get them to work. (I am running this on a raspberry pi so exsuse any weird directorys).
This is my first patch of while loop code
import os
i = 0
while i < 2:
os.pause(4)
os.system("home/Tyler/desktop/test.py")
i += 1
This opens the email "sending" part /\ .
This down here is the "sending" part /
import smtplib
smtpUser = 'smilingexample#gmail.com'
smtpPass = 'password'
toAdd = 'Example#aim.com'
fromAdd = smtpUser
subject = 'yep'
header = 'to: ' + toAdd + '\n' + 'From: ' + fromAdd + '\n' + 'Subject: ' + subject
body = 'hi'
print header + '\n' + body
s = smtplib.SMTP('smtp.gmail.com',587)
s.ehlo()
s.starttls()
s.ehlo()
s.login(smtpUser, smtpPass)
s.sendmail(fromAdd, toAdd, header + '\n\n' + body)
s.quit ()
import datetime
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.header import Header
from email import Charset
Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8')
maillist = []
def send_email(messages_list, smtpUser=None, smtpPass=None, tls=False):
failed = []
try:
s = smtplib.SMTP('smtp.gmail.com',587)
s.ehlo()
if tls:
s.starttls()
s.ehlo()
if smtpUser and smtpPass:
s.login(smtpUser, smtpPass)
except:
print "ehlo failed"
failed = [x[0] for x in messages_list]
else:
for to_address,from_address,subject,encoding,mesg in messages_list:
try:
if len(mesg) == 2:
msg = MIMEMultipart('alternative')
else:
msg = MIMEText(mesg[0],'plain','utf-8')
msg['Subject'] = "%s" % Header(subject, 'utf-8')
if len(from_address) == 2:
msg['From'] = "\"%s\" <%s>" % (Header(from_address[0], 'utf-8'), from_address[-1])
else:
msg['From'] = from_address[-1]
if len(to_address) == 2:
msg['From'] = "\"%s\" <%s>" % (Header(to_address[0], 'utf-8'), to_address[-1])
else:
msg['To'] = to_address[-1]
msg.set_charset("utf-8")
if len(mesg) == 2:
part1 = MIMEText(mesg[0], 'plain','utf-8')
part2 = MIMEText(mesg[1], 'html','utf-8')
msg.attach(part1)
msg.attach(part2)
s.sendmail(from_address[-1], to_address[-1], msg.as_string())
except:
traceback.print_exc()
failed.append(to_address[-1])
try:
s.quit()
except:
pass
return failed
maillist.append(( ['someone#gmail.com'],["Me","noreply#example.com"],'Subject','utf-8',['text_message','html but you can delete this list element'] ))
for k in send_email(maillist, smtpUser='you#gmail.com', smtpPass='pwd', tls=True):
print k, 'not delivered'
Here's what we use to send alternative Mime messages with alternative body. It is not necessary though so you can send simple text messages as well.
It's prepared to send from localhost but you can easily modify it to use it properly.