Operations 10 min read

Automating Daily Extraction of Chrome Browsing History and Email Delivery on macOS

This guide demonstrates how to extract a macOS Chrome user's browsing URLs and timestamps from the History SQLite database, save them to a text file, and automatically email the file daily using Python scripts, a shell wrapper, and a crontab scheduled task.

Python Programming Learning Circle
Python Programming Learning Circle
Python Programming Learning Circle
Automating Daily Extraction of Chrome Browsing History and Email Delivery on macOS

Requirement : Retrieve all URLs and visit times from a target's Chrome browsing history for the previous day, store them in a txt file, and email the file to a specified address automatically each day.

Background : Chrome stores its history in a SQLite database located at /Users/USERNAME/Library/Application Support/Google/Chrome/Default/History . The urls table contains the needed fields url and last_visit_time . Python's built‑in sqlite3 module can query this database.

Preparation : macOS Sierra, Python 3.6+, Chrome, a QQ email address, its authorization code, and SMTP server smtp.qq.com . All paths must be absolute.

Script: get_history.py – extracts URLs and timestamps and writes them to result.txt :

# -*- coding: utf-8 -*-
from email import encoders
from email.header import Header
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.utils import parseaddr, formataddr
import smtplib
import argparse

parser = argparse.ArgumentParser()
parser.add_argument('affix_file', help='the path of the affix')
args = parser.parse_args()

def _format_addr(s):
    name, addr = parseaddr(s)
    return formataddr((Header(name, 'utf-8').encode(), addr))

from_addr = "[email protected]"  # sender
password = "xxxxxxxx"          # authorization code
to_addr = "[email protected]"  # receiver
smtp_server = "smtp.qq.com"

msg = MIMEMultipart()
msg['From'] = _format_addr('风一样的女子 <%s>' % from_addr)
msg['To'] = _format_addr('风一样的男子 <%s>' % to_addr)
msg['Subject'] = Header('chrome历史记录每日更新', 'utf-8').encode()
msg.attach(MIMEText('窥探隐私是犯法的啊!', 'plain', 'utf-8'))

with open(args.affix_file, 'r') as f:
    mime = MIMEBase('result', 'txt', filename='result.txt')
    mime.add_header('Content-Disposition', 'attachment', filename='result.txt')
    mime.add_header('Content-ID', '<0>')
    mime.add_header('X-Attachment-Id', '0')
    mime.set_payload(f.read())
    encoders.encode_base64(mime)
    msg.attach(mime)

server = smtplib.SMTP(smtp_server, 25)
server.set_debuglevel(1)
server.login(from_addr, password)
server.sendmail(from_addr, [to_addr], msg.as_string())
server.quit()

Script: send_email.py – queries the History DB and writes the result to result.txt :

# -*- coding: utf-8 -*-
import sqlite3

history_db = '/Users/Marcel/Desktop/tmp/code/chrome_history/History'

c = sqlite3.connect(history_db)
cursor = c.cursor()

try:
    select_statement = "SELECT url, datetime(last_visit_time/1000000-11644473600,'unixepoch','localtime') AS tm FROM urls WHERE julianday('now') - julianday(tm) < 1 ORDER BY tm;"
    cursor.execute(select_statement)
except sqlite3.OperationalError:
    print('[!] The database is locked! Please exit Chrome and run the script again.')
    quit()

results = cursor.fetchall()
with open('/Users/Marcel/Desktop/tmp/code/chrome_history/result.txt', 'w') as f:
    for i in range(len(results)):
        f.write(results[i][1] + '\n')
        f.write(results[i][0] + '\n')

Execution : Running the two scripts produces a result.txt file containing URLs and timestamps, which is then emailed as an attachment. Sample screenshots show the extracted data.

Shell wrapper (start.sh) combines the steps:

cp /Users/Marcel/Library/Application\ Support/Google/Chrome/Default/History /Users/Marcel/Desktop/tmp/code/chrome_history/
python /Users/Marcel/Desktop/tmp/code/chrome_history/get_history.py
python /Users/Marcel/Desktop/tmp/code/chrome_history/send_mail.py /Users/Marcel/Desktop/tmp/code/chrome_history/result.txt

Executing ./start.sh runs the three commands sequentially.

Cron setup : Add an absolute‑path entry to crontab -e such as:

20 14 * * * /Users/Marcel/Desktop/tmp/code/chrome_history/start.sh

which runs the task daily at 14:20. All paths must be absolute; otherwise the job fails.

Common issues and solutions:

QQ email requires an authorization code, not the login password.

The History DB may be locked if Chrome is running; close Chrome or copy the file first.

Timestamp conversion: divide last_visit_time by 1,000,000 and subtract 11644473600 to get Unix epoch.

Ensure absolute paths in both the shell script and the crontab entry.

Summary :

Use Python to connect to a SQLite database and execute SQL.

Use Python's email modules to send an email with an attachment.

Automate the workflow with a shell script and schedule it via crontab.

The method is for personal experimentation; monitoring others' privacy is unethical.

Outlook :

Package the whole process into a cross‑platform executable.

Current solution only works when the computer is on and networked; further work is needed for continuous monitoring.

shellSQLiteCronEmail Automation
Python Programming Learning Circle
Written by

Python Programming Learning Circle

A global community of Chinese Python developers offering technical articles, columns, original video tutorials, and problem sets. Topics include web full‑stack development, web scraping, data analysis, natural language processing, image processing, machine learning, automated testing, DevOps automation, and big data.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.