# Project export: ScraPy

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 11.0
- Tagline: Automate research email outreach and professor email collection using Python.
- Devpost: https://devpost.com/software/scrapy-xe473b
- GitHub: https://github.com/shadielfares/ScraPy
- Team: 0 GitHub contributor(s) — 

## Devpost submission (written by the team)

### Inspiration

The inspiration for this project came from the need to efficiently send out research emails and collect professor email addresses. Instead of sending specific emails to each person, the idea was to streamline the process by asking for coffee chats, which seemed more effective.

### What it does

ScraPy automates the process of sending out research emails and collecting professor emails. It uses web scraping techniques to gather email addresses and automates the email-sending process.

### How we built it

Technology Stack: The project is built using Python. Web Scraping: Utilizes BeautifulSoup and requests libraries. Email Automation: Uses smtplib to automate email sending. Data Management: Stores collected emails in CSV files. Setup: Requires setting up a virtual environment, installing necessary packages, and configuring email settings.

### Challenges we ran into

Dynamic Content: Handling websites that use JavaScript to load content dynamically. Anti-Scraping Measures: Overcoming measures put in place by websites to prevent scraping. Email Deliverability: Ensuring emails are not marked as spam.

### Accomplishments we're proud of

Successfully automating the email-sending process. Efficiently scraping professor email addresses. Implementing error handling to manage various issues.

### What we learned

Advanced web scraping techniques and ethical scraping practices. Automating tasks using Python. Handling and processing large datasets efficiently.

### What's next

Implementing an AI agent to tailor emails specifically to each professor. Enhancing the scraping techniques to handle more complex websites. Adding more features to improve the overall functionality and efficiency of the tool. For more details, you can visit the README file.

## README (from the GitHub repository)

# ScraPy ☕📧

ScraPy is a Python-based tool designed to send out research emails efficiently. This repository contains scripts that automate the process of scraping data and sending emails. Instead of sending specific emails to each person, I've opted for a more streamlined approach by asking for coffee chats, believing it would be more effective. In the future, I intend to implement an AI agent that will scrape each professor's entry and tailor the emails specifically to them. 🤖✨

NOTE: Clicking on the "Wiki" section at the top of this repo will show you some of the results this project yielded.

## Prerequisites ✅

Before you can run any scripts in this project, make sure you have the latest version of [Python 3](https://www.python.org/downloads/) installed.

1. **Install Python 3**: Follow the links above to download and install the latest versions.
  
2. **Create a Virtual Environment**:
   - Navigate to your project directory:
     ```bash
     cd path/to/your/project
     ```
   - Create a virtual environment:
     ```bash
     python -m venv .venv
     ```
   - Activate the virtual environment:
     - **On Windows**: 
       - Open PowerShell as Administrator and run:
         ```powershell
         Set-ExecutionPolicy Unrestricted
         ```
       - Then activate the virtual environment:
         ```powershell
         .\.venv\Scripts\activate
         ```

     - **On macOS/Linux**:
       - Activate the virtual environment using:
         ```bash
         source .venv/bin/activate
         ```

3. **Clone the Repository Inside the `.venv` Directory**:
   - After activating your virtual environment, clone the ScraPy repository:
   - Navigate into the cloned repository:
     ```bash
     cd ScraPy
     ```

4. **Install Required Packages**: You can install the required packages using pip:
   ```bash
   pip install -r requirements.txt
   ```

Here are the required packages listed in `requirements.txt`:

```
beautifulsoup4==4.12.3
certifi==2024.6.2
charset-normalizer==3.3.2
et-xmlfile==1.1.0
idna==3.7
openpyxl==3.1.3
python-dotenv==1.0.1
requests==2.32.3
soupsieve==2.5
urllib3==2.2.1
```

## Important File Setup 📋

- **Replace `DUMMYDOC.xlsx`**: Ensure you replace `DUMMYDOC.xlsx` with an empty Excel workbook, replacing its current existence in the project folder.

## Demo Run-through 🚀

Here's a step-by-step guide to running a script from the `scraperMagic` folder:

1. **Navigate to the Project Directory**:
   ```bash
   cd scraperMagic
   ```

2. **Run a Script**: Assuming you have a script named `example_script.py` in the `scraperMagic` folder, you can run it as follows:
   ```bash
   python example_script.py
   ```

### Example: Running `emailer.py` ✉️

If you have a script named `send_emails.py` in the `scraperMagic` folder, you can run it to send out research emails. Make sure to configure any necessary environment variables or input files as required by the script.

- **Configure Gmail for `emailer.py`**:
  To send emails from a personal Gmail address, you'll need to create an app password. Please read the instructions in the `emailer.py` script to set this up correctly.

- **Attaching Files**:
  If you want to attach any files to the email, read through the corresponding code block in `emailer.py` and follow the comments for instructions on how to do this.

```bash
python emailer.py
```

## Disclaimer ⚠️

I am not liable for any damage, spam, or liabilities this project may cause. This includes but is not limited to any unintended consequences of using this tool, such as your email being flagged as spam or any impact on your reputation. 

I have chosen not to include extensive documentation exploring all features and scripts, as the level of abstraction was necessary to limit its usability to those with a specific purpose who have the capabilities to modify the script and use it for good. By using this tool, you acknowledge that you understand the risks involved and agree to take full responsibility for any actions taken as a result of using this project.

## License 📄

This project is licensed under the CCC License - see the LICENSE file for details.

## Contributing 🤝

Feel free to fork this repository and submit pull requests. For major changes, please open an issue first to discuss what you would like to change.


## Detected evidence (automated analysis)

Indexed codebase: 10 recognized source files, 23 KB.
- Python (language) — detected in the code

## Codebase structure (from repository index)

### Files (12 of 12)

```
LICENSE
README.md
requirements.txt
scraperMagic/emailer.py
scraperMagic/eng-scraper.py
scraperMagic/fac-bizscraper.py
scraperMagic/fac-hum-scraper.py
scraperMagic/fac-sci-scraper.py
scraperMagic/fac-soc-sci-scraper.py
tools/add-doctor.py
tools/check-duplicates.py
tools/reformat-names.py
```

### Dependencies

- requirements.txt: beautifulsoup4@==4.12.3, certifi@==2024.6.2, charset-normalizer@==3.3.2, et-xmlfile@==1.1.0, idna@==3.7, openpyxl@==3.1.3, python-dotenv@==1.0.1, requests@==2.32.3, soupsieve@==2.5, urllib3@==2.2.1

### Recent commits (newest first)

- Update README.md
- Merge branch 'polished' of https://github.com/shadielfares/ScraPy into polished
- Update README.md
- Update README.md
- Update ReadME
- Create README.md
- Final modify .gitignore
- Modify .gitignore
- major changes to tracked file
- Changing .gitignore
- Redone folder presentation

## Key source files (fetched from GitHub, selected and truncated for size)

### requirements.txt

```
beautifulsoup4==4.12.3
certifi==2024.6.2
charset-normalizer==3.3.2
et-xmlfile==1.1.0
idna==3.7
openpyxl==3.1.3
python-dotenv==1.0.1
requests==2.32.3
soupsieve==2.5
urllib3==2.2.1
```

### tools/add-doctor.py

```python
import openpyxl

# Load the existing workbook
workbook_path = './excel-sheets/DUMMYDOC.xlsx' #Replace this with the correct file name located in the excel-sheets folder
workbook = openpyxl.load_workbook(workbook_path)

# Access the specific sheet
sheet_name = 'dummyPageName'
if sheet_name in workbook.sheetnames:
    sheet = workbook[sheet_name]
else:
    print(f"Sheet '{sheet_name}' does not exist in the workbook.")
    exit()

# Loop through rows 2 to 274 in column A and prepend "Dr." to the existing names
for row in range(2, 275):  # Note: range end is exclusive, so use 275 to include row 274
    cell = sheet[f'A{row}']
    if cell.value:
        cell.value = f"Dr. {cell.value}"

# Save the workbook
workbook.save(workbook_path)
print("Entries have been updated with 'Dr.' prefix.")

```

### tools/reformat-names.py

```python
import openpyxl

def reformat_names(file_path, sheet_name):
    # Load the workbook and select the sheet
    workbook = openpyxl.load_workbook(file_path)
    sheet = workbook[sheet_name]

    # Iterate over the cells from A2 to A176
    for row in range(2, 177):  # Row numbers are 2 to 176
        cell = sheet[f'A{row}']
        name = cell.value
        if name:
            # Split the name by comma and trim any whitespace
            first_name, last_name = [part.strip() for part in name.split(',', 1)]
            # Reformat the name
            reformatted_name = f'{first_name} {last_name}'
            # Update the cell value
            cell.value = reformatted_name

    # Save the changes to the workbook
    workbook.save(file_path)

# Example usage
file_path = './FEEDERDOC.xlsx'  # Path to your Excel file
sheet_name = 'DUMMYNAME'  # Name of the sheet
reformat_names(file_path, sheet_name)

```

### tools/check-duplicates.py

```python
import pandas as pd

# Load the Excel workbook
file_path = 'FEEDERDOC.xlsx' #Replace this with the correct file name located in the excel-sheets folder
sheets = ['health-sci', 'fac-hum', 'fac-eng', 'fac-biz', 'fac-sci', 'fac-soc-sci'] #Example page names to feed from, CHANGE THESE ACCORDINGLY
df_dict = pd.read_excel(file_path, sheet_name=sheets)

# Combine all sheets into one DataFrame for checking duplicates
combined_df = pd.concat(df_dict.values(), keys=df_dict.keys()).reset_index(level=0).rename(columns={'level_0': 'Sheet'})

# Identify duplicates in names and emails
duplicate_names = combined_df[combined_df.duplicated(['Name'], keep=False)]
duplicate_emails = combined_df[combined_df.duplicated(['Email'], keep=False)]

def remove_duplicates(df, column_name):
    while True:
        print(f"\nDuplicates in {column_name}:")
        print(df[df.duplicated([column_name], keep=False)])
        choice = input(f"\nWould you like to remove duplicates in {column_name}? (yes/no): ").strip().lower()
        if choice == 'yes':
            # Drop only the second occurrence of each duplicate
            duplicates = df[df.duplicated([column_name], keep='first')]
            df = df.drop(duplicates.index)
            print(f"\nDuplicates in {column_name} removed.")
            break
        elif choice == 'no':
            print(f"\nNo changes made to {column_name}.")
            break
        else:
            print("Invalid input. Please type 'yes' or 'no'.")
    return df

# Check for duplicates in names and prompt user
if not duplicate_names.empty:
    combined_df = remove_duplicates(combined_df, 'Name')
else:
    print("\nNo duplicate names found.")

# Check for duplicates in emails and prompt user
if not duplicate_emails.empty:
    combined_df = remove_duplicates(combined_df, 'Email')
else:
    print("\nNo duplicate emails found.")

# Split the combined DataFrame back into individual sheets
updated_df_dict = {sheet: combined_df[combined_df['Sheet'] == sheet].drop(columns='Sheet') for sheet in sheets}

# Save the updated DataFrames to a new Excel file
output_file_path = 'UPDATEDDOC.xlsx' #Replace this with the correct file name located in the excel-sheets folder
with pd.ExcelWriter(output_file_path) as writer:
    for sheet_name, df in updated_df_dict.items():
        df.to_excel(writer, sheet_name=sheet_name, index=False)

print(f"\nUpdated file saved as {output_file_path}")

```

### scraperMagic/fac-hum-scraper.py

```python
import requests
from bs4 import BeautifulSoup
import openpyxl
import time

# Load the existing workbook
workbook_path = './excel-sheets/DUMMYDOC.xlsx'
workbook = openpyxl.load_workbook(workbook_path)

# Create a new sheet for the scraped data
if 'dummyPageName' not in workbook.sheetnames:
    sheet = workbook.create_sheet('dummyPageName')
else:
    sheet = workbook['dummyPageName']

# Set up headers in the new sheet
sheet['A1'] = 'Name'
sheet['B1'] = 'Email'

# Function to scrape initial data from a specific page
def scrape_mcmaster_faculty(page_num):
    url = f'https://www.humanities.mcmaster.ca/contact-us/directory/?pg={page_num}'
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'html.parser')

    faculty_members = soup.find_all('div', class_='profile-item')
    data = []

    print(len(faculty_members))

    for member in faculty_members:
        name = member.find('div', class_='profile-item--name').get_text(strip=True)

        email_div = member.find('div', class_='profile-item-col profile-item-contact')
        email_tag = email_div.find('a', href=lambda href: href and 'mailto:' in href) if email_div else None
        email = email_tag['href'].replace('mailto:', '') if email_tag else 'N/A'
        data.append([name, email])
    return data

start_idx = 2

# Loop to scrape multiple pages
for page_num in range(1, 33):  # Adjust the range as needed to scrape more pages
    faculty_data = scrape_mcmaster_faculty(page_num)

    # Add data to the Excel sheet and scrape emails from profile pages
    for (name,email) in faculty_data:
        sheet[f'A{start_idx}'] = name
        sheet[f'B{start_idx}'] = email
        start_idx += 1

    # Delay between page requests
    time.sleep(5)

# Save the workbook and check to see if it changed

# Get the content of cell A1 before saving the workbook
pre_save_content = sheet['A2'].value

# Save the workbook
workbook.save(workbook_path)

# Load the workbook again to compare the content
workbook_post_save = openpyxl.load_workbook(workbook_path)
sheet_post_save = workbook_post_save['dummyPageName']

# Get the content of cell A1 after saving the workbook
post_save_content = sheet_post_save['A1'].value

# Compare the contents
if pre_save_content == post_save_content:
    print("The contents of cell A1 have not changed after saving the workbook.")
else:
    print("The contents of cell A1 have changed after saving the workbook.")
```

### scraperMagic/eng-scraper.py

```python
import requests
from bs4 import BeautifulSoup
import openpyxl
import time

# Load the existing workbook
workbook_path = './excel-sheets/DUMMYDOC.xlsx' #Replace this with the correct file name located in the excel-sheets folder
workbook = openpyxl.load_workbook(workbook_path)

# Create a new sheet for the scraped data
if 'dummyPageName' not in workbook.sheetnames:
    sheet = workbook.create_sheet('dummyPageName')
else:
    sheet = workbook['dummyPageName']

# Set up headers in the new sheet
sheet['A1'] = 'Name'
sheet['B1'] = 'Email'
#sheet['C1'] = 'Profile URL'

# Function to scrape initial data from a specific page
def scrape_mcmaster_faculty(page_num):
    url = f'https://www.eng.mcmaster.ca/faculty-staff/faculty-directory/?pg={page_num}'
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'html.parser')

    faculty_members = soup.find_all('li', class_='faculty-listing__faculty')
    data = []

    for member in faculty_members:
        name = member.find('h2', class_='faculty-card__name').get_text(strip=True)
        profile_link = member.find('a', class_='faculty-card__link')['href'] if member.find('a', class_='faculty-card__link') else None
        data.append((name, profile_link))
    return data

# Function to scrape email from profile page
def scrape_email_from_profile(profile_url):
    response = requests.get(profile_url)
    soup = BeautifulSoup(response.text, 'html.parser')
    email_div = soup.find('div', class_='single-faculty__contact__option-content')
    email_tag = email_div.find('p').find('a', href=lambda href: href and 'mailto:' in href) if email_div else None
    email = email_tag['href'].replace('mailto:', '') if email_tag else 'N/A'
    return email

# Initialize the starting row index
start_idx = 1

# Loop to scrape multiple pages
for page_num in range(1, 18):  # Adjust the range as needed to scrape more pages
    faculty_data = scrape_mcmaster_faculty(page_num)

    # Add data to the Excel sheet and scrape emails from profile pages
    for name, profile_url in faculty_data:
        email = scrape_email_from_profile(profile_url)
        sheet[f'A{start_idx}'] = name
        sheet[f'B{start_idx}'] = email
        #sheet[f'C{start_idx}'] = profile_url
        start_idx += 1

    # Delay between page requests
    time.sleep(10)

# Save the workbook
workbook.save(workbook_path)
print("Data has been scraped and added to the Excel file successfully.")

```

### scraperMagic/fac-soc-sci-scraper.py

```python
import requests
from bs4 import BeautifulSoup
import openpyxl
import time

# Load the existing workbook
workbook_path = './excel-sheets/DUMMYDOC.xlsx'
workbook = openpyxl.load_workbook(workbook_path)

# Create a new sheet for the scraped data
if 'dummyPageName' not in workbook.sheetnames:
    sheet = workbook.create_sheet('dummyPageName')
else:
    sheet = workbook['dummyPageName']

# Set up headers in the new sheet
sheet['A1'] = 'Name'
sheet['B1'] = 'Email'

# Function to scrape initial data from a specific page
def scrape_mcmaster_faculty(page_num):
    url = f'https://socialsciences.mcmaster.ca/contact-us/directory/?pg={page_num}'
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'html.parser')

    faculty_members = soup.find_all('div', class_='profile-item')
    data = []

    print(len(faculty_members))

    for member in faculty_members:
        name = member.find('div', class_='profile-item--name').get_text(strip=True)

        email_div = member.find('div', class_='profile-item-col profile-item-contact')
        email_tag = email_div.find('a', href=lambda href: href and 'mailto:' in href) if email_div else None
        email = email_tag['href'].replace('mailto:', '') if email_tag else 'N/A'
        data.append([name, email])
    return data

start_idx = 322

# Loop to scrape multiple pages
for page_num in range(32, 59):  # Adjust the range as needed to scrape more pages
    faculty_data = scrape_mcmaster_faculty(page_num)

    # Add data to the Excel sheet and scrape emails from profile pages
    for (name,email) in faculty_data:
        sheet[f'A{start_idx}'] = name
        sheet[f'B{start_idx}'] = email
        start_idx += 1

    # Delay between page requests
    time.sleep(5)

# Save the workbook and check to see if it changed

# Get the content of cell A1 before saving the workbook
pre_save_content = sheet['A2'].value

# Save the workbook
workbook.save(workbook_path)

# Load the workbook again to compare the content
workbook_post_save = openpyxl.load_workbook(workbook_path)
sheet_post_save = workbook_post_save['dummyPageName']

# Get the content of cell A1 after saving the workbook
post_save_content = sheet_post_save['A1'].value

# Compare the contents
if pre_save_content == post_save_content:
    print("The contents of cell A1 have not changed after saving the workbook.")
else:
    print("The contents of cell A1 have changed after saving the workbook.")
```

### scraperMagic/fac-sci-scraper.py

```python
import requests
from bs4 import BeautifulSoup
import openpyxl

# Load the existing workbook
workbook_path = './excel-sheets/DUMMYDOC.xlsx'
workbook = openpyxl.load_workbook(workbook_path)

# Create a new sheet for the scraped data
if 'dummyPageName' not in workbook.sheetnames:
    sheet = workbook.create_sheet('dummyPageName')
else:
    sheet = workbook['dummyPageName']

# Set up headers in the new sheet
sheet['A1'] = 'Name'
sheet['B1'] = 'Email'

url = 'https://biology.mcmaster.ca/people/faculty/'

# Function to scrape initial data
def scrape_mcmaster_faculty(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'html.parser')
    
    faculty_members = soup.find_all('div', class_='col-md-6 col-xl-3') #Changing the name of this class but same structure
    data = []
    print(len(faculty_members))

    for member in faculty_members:
        name = member.find('h3', class_='card-title no-line p-0 pb-2').get_text(strip=True)
        data.append(name)
    return data

# Function to scrape email from profile page
def scrape_email_from_profile(url):
    response = requests.get(url)
    soup = BeautifulSoup(response.text, 'html.parser')

    email_divs = soup.find_all('div', class_='list-group list-group-flush small mt-2')
    emails_array = []
    for emails in email_divs:
        email_tag = emails.find('a', href=lambda href: href and 'mailto:' in href) if emails else None
        email = email_tag['href'].replace('mailto:', '') if email_tag else 'N/A'
        emails_array.append(email)
    return emails_array

# Scrape the initial data
faculty_data = scrape_mcmaster_faculty(url)
faculty_emails = scrape_email_from_profile(url)

# Add data to the Excel sheet
for idx, (name, email) in enumerate(zip(faculty_data, faculty_emails), start=124):
    sheet[f'A{idx}'] = name
    sheet[f'B{idx}'] = email

# Save the workbook and check to see if it changed

# Get the content of cell A1 before saving the workbook
pre_save_content = sheet['A1'].value

# Save the workbook
workbook.save(workbook_path)

# Load the workbook again to compare the content
workbook_post_save = openpyxl.load_workbook(workbook_path)
sheet_post_save = workbook_post_save['dummyPageName']

# Get the content of cell A1 after saving the workbook
post_save_content = sheet_post_save['A1'].value

# Compare the contents
if pre_save_content == post_save_content:
    print("The contents of cell A1 have not changed after saving the workbook.")
else:
    print("The contents of cell A1 have changed after saving the workbook.")

```

### scraperMagic/fac-bizscraper.py

```python
import time
import openpyxl
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.chrome.service import Service

# Load the existing workbook
workbook_path = './excel-sheets/DUMMYDOC.xlsx'
workbook = openpyxl.load_workbook(workbook_path)

# Create a new sheet for the scraped data
if 'dummyPageName' not in workbook.sheetnames:
    sheet = workbook.create_sheet('dummyPageName')
else:
    sheet = workbook['dummyPageName']

# Set up headers in the new sheet
sheet['A1'] = 'Name'
sheet['B1'] = 'Email'
#sheet['C1'] = 'Profile URL'

# Initialize the starting row index
start_idx = 2

chromedriver_path = './LOCATIONOFCHROMEDRIVER'  # Update this with the actual path

# Setup WebDriver
driver = webdriver.Chrome(service=Service(chromedriver_path))
# Function to scrape initial data from a specific page
def scrape_mcmaster_faculty():
    faculty_members = driver.find_elements(By.XPATH, '//tr[contains(@class, "odd") or contains(@class, "even")]')
    data = []
    for member in faculty_members:
        name = member.find_element(By.CLASS_NAME, 'sorting_1').find_element(By.TAG_NAME, 'a').text.strip()
        try:
            email_element = member.find_element(By.XPATH, './/td[@class="text-center"]/a[contains(@href, "mailto:")]')
            email = email_element.get_attribute('href').replace('mailto:', '')
        except:
            email = 'N/A'
        #profile_link = member.find_element(By.CLASS_NAME, 'sorting_1').find_element(By.TAG_NAME, 'a').get_attribute('href')
        data.append((name, email))
    return data

# Open the initial page
driver.get('https://www.degroote.mcmaster.ca/contact/directory/')

# Loop to scrape multiple pages
while True:
    # Scrape faculty data
    faculty_data = scrape_mcmaster_faculty()
    
    # Add data to the Excel sheet and scrape emails from profile pages
    for (name,email) in faculty_data:
        sheet[f'A{start_idx}'] = name
        sheet[f'B{start_idx}'] = email
        #sheet[f'C{start_idx}'] = url
        start_idx += 1

    # Find the next page button
    try:
        next_button = driver.find_element(By.XPATH, '//a[@class="paginate_button next"]')
        if 'disabled' in next_button.get_attribute('class'):
            break  # Exit the loop if the "next" button is disabled (no more pages)
        next_button.click()
        time.sleep(5)  # Adjust the sleep time if necessary to allow page load
    except:
        break  # Exit the loop if no more pages are found

# Save the workbook
workbook.save(workbook_path)
print("Data has been scraped and added to the Excel file successfully.")

# Close the WebDriver
driver.quit()

```

### scraperMagic/emailer.py

```python
import openpyxl
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.base import MIMEBase
from email import encoders
import time

from dotenv import load_dotenv
import os

# Load the environment variables from the .env file
load_dotenv()

def send_email(sender_email, sender_password, receiver_email, subject, body,  image_path):
    msg = MIMEMultipart()
    msg['From'] = sender_email
    msg['To'] = receiver_email
    msg['Subject'] = subject
    msg.attach(MIMEText(body, 'html'))


    # Attach the image
    with open(image_path, 'rb') as img:
        mime = MIMEBase('image', 'png', filename="AppendedFileLocation") #Change application to image, and pdf to jpg or png
        mime.add_header('Content-Disposition', 'attachment', filename="Brochure")
        # The below code is for attaching an image
        mime.add_header('X-Attachment-Id', 'structure')
        mime.add_header('Content-ID', '<image1>')
        mime.set_payload(img.read())
        # mime.set_payload(pdf.read()) #Comment this line out to keep the avaliability to attach pdf
        encoders.encode_base64(mime)
        msg.attach(mime)
    try:
        server = smtplib.SMTP('smtp.gmail.com', 587)
        server.starttls()
        server.login(sender_email, sender_password)
        text = msg.as_string()
        server.sendmail(sender_email, receiver_email, text)
        print(f"Email sent to {receiver_email} successfully!")
        time.sleep(10)  # Add a delay between emails
    except Exception as e:
        print(f"Error: {e}")
    finally:
        server.quit()

def send_emails_from_excel(file_path, sender_email, sender_password):
    workbook = openpyxl.load_workbook(file_path)
    sheet = workbook["dummyPageName"] #Adjust reading workbook sheet.
    #Change the range to the rows you want to send emails to
    for row in sheet.iter_rows(min_row=1, max_row=2, values_only=True):  # assuming data starts from row 1 and ends at row 2
        receiver_name, receiver_email = row
        subject = "This is a Test E-Mail"
        body = f""" 
        <html>
        <body>
        <p>Hi {receiver_name},</p>
        <p>This is the first email</p>
        </body>
        </html>
        """
        send_email(sender_email, sender_password, receiver_email, subject, body, 'AppendedFileLocation')

if __name__ == "__main__":
    file_path = "./excel-sheets/DUMMYDOC.xlsx"  # Replace with the path to your Excel file
    sender_email = "emailForScript"  # Replace with your Gmail address    
    sender_password = "senderPassword"  # Replace with your Gmail password / app password
    send_emails_from_excel(file_path, sender_email, sender_password)

```