87 lines
2.7 KiB
Python
87 lines
2.7 KiB
Python
#!/usr/bin/env python3
|
|||
|
|
# Description: Monitor a website for changes.
|
||
|
|
# Usage: python3 diff-web.py
|
||
|
|
# Author: Justin Oros
|
||
|
|
# Source: https://github.com/JustinOros
|
||
|
|
|
||
|
|
import requests
|
||
|
|
import hashlib
|
||
|
|
import os
|
||
|
|
import time
|
||
|
|
import argparse
|
||
|
|
from urllib.parse import urlparse
|
||
|
|
from datetime import datetime
|
||
|
|
|
||
|
|
def normalize_url(url):
|
||
|
|
if not urlparse(url).scheme:
|
||
|
|
url = 'https://' + url
|
||
|
|
return url
|
||
|
|
|
||
|
|
def fetch_content(url):
|
||
|
|
response = requests.get(url, timeout=10)
|
||
|
|
response.raise_for_status()
|
||
|
|
return response.text
|
||
|
|
|
||
|
|
def get_hash(content):
|
||
|
|
return hashlib.sha256(content.encode('utf-8')).hexdigest()
|
||
|
|
|
||
|
|
def get_log_filename(url, override=None):
|
||
|
|
if override:
|
||
|
|
return override
|
||
|
|
domain = urlparse(url).netloc
|
||
|
|
return f"{domain}.log"
|
||
|
|
|
||
|
|
def get_cache_filename(url):
|
||
|
|
domain = urlparse(url).netloc
|
||
|
|
return f".{domain}.cache"
|
||
|
|
|
||
|
|
def log_message(message, log_file):
|
||
|
|
print(message)
|
||
|
|
with open(log_file, 'a') as f:
|
||
|
|
f.write(message + '\n')
|
||
|
|
|
||
|
|
def monitor_website(url, interval, log_file_override=None):
|
||
|
|
url = normalize_url(url)
|
||
|
|
domain = urlparse(url).netloc
|
||
|
|
log_file = get_log_filename(url, log_file_override)
|
||
|
|
cache_file = get_cache_filename(url)
|
||
|
|
|
||
|
|
log_message(f"[{datetime.now()}] Monitoring {domain}", log_file)
|
||
|
|
|
||
|
|
while True:
|
||
|
|
try:
|
||
|
|
current_content = fetch_content(url)
|
||
|
|
current_hash = get_hash(current_content)
|
||
|
|
except Exception as e:
|
||
|
|
log_message(f"[{datetime.now()}] [ERROR] Failed to fetch {url}: {e}", log_file)
|
||
|
|
time.sleep(interval)
|
||
|
|
continue
|
||
|
|
|
||
|
|
if os.path.exists(cache_file):
|
||
|
|
with open(cache_file, 'r') as f:
|
||
|
|
old_hash = f.read().strip()
|
||
|
|
if current_hash != old_hash:
|
||
|
|
msg = f"[{datetime.now()}] Change detected on {url}"
|
||
|
|
log_message(msg, log_file)
|
||
|
|
with open(cache_file, 'w') as f:
|
||
|
|
f.write(current_hash)
|
||
|
|
else:
|
||
|
|
msg = f"[{datetime.now()}] First-time check for {url}: storing baseline."
|
||
|
|
log_message(msg, log_file)
|
||
|
|
with open(cache_file, 'w') as f:
|
||
|
|
f.write(current_hash)
|
||
|
|
|
||
|
|
time.sleep(interval)
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
parser = argparse.ArgumentParser(description="Monitor a website for changes.")
|
||
|
|
parser.add_argument("url", help="The website URL or domain to monitor.")
|
||
|
|
parser.add_argument("-t", "--time", type=int, default=60,
|
||
|
|
help="Time interval between checks in seconds (default: 60).")
|
||
|
|
parser.add_argument("-l", "--log", type=str,
|
||
|
|
help="Optional log file name (default: domain.tld.log)")
|
||
|
|
|
||
|
|
args = parser.parse_args()
|
||
|
|
monitor_website(args.url, args.time, args.log)
|
||
|
|
|