Python is the perfect language for automating repetitive tasks that steal hours from your week: renaming files, scraping websites, sending bulk emails, interacting with APIs, generating reports. In this practical guide, you'll learn to automate real tasks with scripts you can start using today.
Prerequisites
Install Python 3.10+ and set up a virtual environment:
python -m venv myenv
source myenv/bin/activate # macOS/Linux
myenv\Scripts\activate # Windows
pip install requests beautifulsoup4 openpyxl python-dotenv
1. File operations automation
Organize files automatically by type:
import os
import shutil
from pathlib import Path
def organize_downloads(folder_path):
"""Organize files into subfolders by type."""
categories = {
'Images': ['.jpg', '.jpeg', '.png', '.gif', '.svg', '.webp'],
'Documents': ['.pdf', '.doc', '.docx', '.txt', '.xlsx', '.csv'],
'Videos': ['.mp4', '.avi', '.mkv', '.mov'],
'Code': ['.py', '.js', '.html', '.css', '.json'],
'Archives': ['.zip', '.rar', '.7z', '.tar', '.gz'],
}
folder = Path(folder_path)
moved = 0
for file in folder.iterdir():
if file.is_dir():
continue
ext = file.suffix.lower()
dest = 'Other'
for cat, exts in categories.items():
if ext in exts:
dest = cat
break
dest_folder = folder / dest
dest_folder.mkdir(exist_ok=True)
shutil.move(str(file), str(dest_folder / file.name))
moved += 1
print(f" {file.name} -> {dest}/")
print(f"\n{moved} files organized")
organize_downloads(os.path.expanduser("~/Downloads"))
Bulk rename files
from pathlib import Path
def rename_photos(folder, prefix="photo"):
"""Rename files sequentially: photo_001.jpg, photo_002.jpg..."""
folder = Path(folder)
images = sorted(
[f for f in folder.iterdir() if f.suffix.lower() in ('.jpg', '.png')],
key=lambda x: x.stat().st_mtime
)
for i, img in enumerate(images, 1):
new_name = f"{prefix}_{i:03d}{img.suffix.lower()}"
img.rename(folder / new_name)
print(f" {img.name} -> {new_name}")
rename_photos("./my_photos", prefix="vacation_2026")
2. Web scraping with BeautifulSoup
import requests
from bs4 import BeautifulSoup
import csv
from datetime import datetime
def scrape_hackernews():
"""Extract titles and links from Hacker News."""
url = "https://news.ycombinator.com/"
headers = {'User-Agent': 'Mozilla/5.0 (compatible; MyBot/1.0)'}
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')
stories = []
for item in soup.select('.titleline > a'):
stories.append({
'title': item.text,
'url': item.get('href', ''),
'scraped_at': datetime.now().isoformat()
})
filename = f"hackernews_{datetime.now().strftime('%Y%m%d')}.csv"
with open(filename, 'w', newline='', encoding='utf-8') as f:
writer = csv.DictWriter(f, fieldnames=['title', 'url', 'scraped_at'])
writer.writeheader()
writer.writerows(stories)
print(f"Scraped {len(stories)} stories -> {filename}")
scrape_hackernews()
Important: Always respect robots.txt and add delays between requests with time.sleep(1). Read more about robots.txt and Python.
3. Working with APIs
import requests
import os
from dotenv import load_dotenv
load_dotenv()
def get_weather(city):
"""Get current weather using OpenWeatherMap API."""
api_key = os.getenv('OPENWEATHER_API_KEY')
if not api_key:
print("Error: Set OPENWEATHER_API_KEY in your .env file")
return
url = "https://api.openweathermap.org/data/2.5/weather"
params = {'q': city, 'appid': api_key, 'units': 'metric'}
try:
resp = requests.get(url, params=params, timeout=10)
resp.raise_for_status()
data = resp.json()
print(f"\nWeather in {data['name']}:")
print(f" Temperature: {data['main']['temp']}°C")
print(f" Humidity: {data['main']['humidity']}%")
print(f" Conditions: {data['weather'][0]['description']}")
except requests.exceptions.HTTPError as e:
print(f"HTTP Error: {e}")
except requests.exceptions.ConnectionError:
print("Error: No internet connection")
get_weather("London")
4. Excel report generation
from openpyxl import Workbook
from openpyxl.styles import Font, PatternFill, Alignment
from datetime import datetime
def generate_sales_report(data):
"""Generate a formatted Excel sales report."""
wb = Workbook()
ws = wb.active
ws.title = "Sales Report"
# Header styles
header_font = Font(bold=True, color="FFFFFF", size=12)
header_fill = PatternFill(start_color="1e3a5f", fill_type="solid")
# Title
ws.merge_cells('A1:E1')
ws['A1'] = f"Sales Report - {datetime.now().strftime('%B %Y')}"
ws['A1'].font = Font(bold=True, size=16)
# Headers
headers = ['Product', 'Quantity', 'Unit Price', 'Total', 'Date']
for col, header in enumerate(headers, 1):
cell = ws.cell(row=3, column=col, value=header)
cell.font = header_font
cell.fill = header_fill
# Data rows
grand_total = 0
for row, sale in enumerate(data, 4):
total = sale['qty'] * sale['price']
grand_total += total
ws.cell(row=row, column=1, value=sale['product'])
ws.cell(row=row, column=2, value=sale['qty'])
ws.cell(row=row, column=3, value=sale['price']).number_format = '$#,##0.00'
ws.cell(row=row, column=4, value=total).number_format = '$#,##0.00'
ws.cell(row=row, column=5, value=sale['date'])
# Grand total
total_row = len(data) + 4
ws.cell(row=total_row, column=3, value="TOTAL:").font = Font(bold=True)
ws.cell(row=total_row, column=4, value=grand_total).font = Font(bold=True)
for col in ['A', 'B', 'C', 'D', 'E']:
ws.column_dimensions[col].width = 18
filename = f"sales_{datetime.now().strftime('%Y%m%d')}.xlsx"
wb.save(filename)
print(f"Report generated: {filename}")
sales = [
{'product': 'Laptop', 'qty': 5, 'price': 899.99, 'date': '2026-02-01'},
{'product': 'Monitor 27"', 'qty': 12, 'price': 349.99, 'date': '2026-02-05'},
{'product': 'Keyboard', 'qty': 25, 'price': 79.99, 'date': '2026-02-10'},
]
generate_sales_report(sales)
5. Sending automated emails
import smtplib
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import os
from dotenv import load_dotenv
load_dotenv()
def send_email(to, subject, html_body):
sender = os.getenv('EMAIL_USER')
password = os.getenv('EMAIL_PASSWORD')
msg = MIMEMultipart()
msg['From'] = sender
msg['To'] = to
msg['Subject'] = subject
msg.attach(MIMEText(html_body, 'html'))
try:
with smtplib.SMTP('smtp.gmail.com', 587) as server:
server.starttls()
server.login(sender, password)
server.send_message(msg)
print(f"Email sent to {to}")
except Exception as e:
print(f"Error: {e}")
send_email("client@example.com", "Monthly Report", "February 2026
See attached.
")
For Gmail, generate an App Password instead of using your regular password.
Best practices
- Use environment variables for credentials (
.env+python-dotenv). Never hardcode passwords. - Always handle errors: Use
try/exceptfor network, file, and API operations. - Add logging: Use Python's logging module instead of
print()in production. - Use virtual environments: Each project with its own
venvavoids dependency conflicts. - Respect rate limits: In web scraping, respect
robots.txtand add delays between requests.
Next steps
- Selenium — Automate web browsers
- Pandas — Large-scale data processing
- asyncio + aiohttp — Concurrent HTTP requests
- GitHub Actions — Run Python scripts automatically in the cloud