Files
Python/diff-web.py
T

152 lines
5.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# Description: Monitor a website for changes.
# Usage: python3 diff-web.py --domain example.com
# Author: Justin Oros
# Source: https://github.com/JustinOros
import argparse
import hashlib
import os
import re
import sys
import time
from datetime import datetime
from getpass import getpass
from urllib.parse import urlparse
import requests
from requests.exceptions import ConnectionError, HTTPError, Timeout, RequestException
from bs4 import BeautifulSoup
def clean_text(text):
junk_phrases = [
r'\bMenu\b', r'\bClose\b', r'\bSign ?In\b', r'\bSign ?Out\b',
r'\bLogin\b', r'\bLogout\b', r'\bBack\b', r'\bNext\b', r'\bMore\b',
r'\bSearch\b', r'\bCart\b', r'\bSettings\b', r'\bHelp\b',
r'\bContact\b', r'\bLanguage\b', r'\bProfile\b', r'\bAccount\b',
r'\bSupport\b', r'\bUS\b', r'\bEN\b', r'\bFR\b', r'\bDE\b',
r'\bJP\b', r'\bES\b', r'\bIT\b', r'\bCN\b', r'\b≡\b', r'\b×\b'
]
text = re.sub(r'\b(?:US|EN|FR|DE|JP|ES|IT|CN)\b(\s*chevron_right\s*)+', '', text, flags=re.IGNORECASE)
text = re.sub(r'(chevron_right\s*){2,}', '', text, flags=re.IGNORECASE)
text = re.sub(r'\bchevron_right\b', '', text, flags=re.IGNORECASE)
for pattern in junk_phrases:
text = re.sub(pattern, '', text, flags=re.IGNORECASE)
text = re.sub(r'\s+', ' ', text)
return text.strip()
def fetch_content_with_fallback(domain):
"""
Try HTTPS first, if fails, fall back to HTTP.
Return tuple (url_used, content)
"""
parsed = urlparse(domain)
netloc = parsed.netloc if parsed.netloc else parsed.path # handles domain or full url input
https_url = f"https://{netloc}"
http_url = f"http://{netloc}"
for url in [https_url, http_url]:
try:
response = requests.get(url, timeout=10)
response.raise_for_status()
soup = BeautifulSoup(response.text, 'html.parser')
# Remove unwanted tags
for tag in soup(["script", "style", "noscript", "iframe", "svg", "canvas", "header", "footer", "nav", "form", "button"]):
tag.decompose()
keep_tags = ['h1', 'h2', 'h3', 'h4', 'p', 'li', 'article', 'section', 'span', 'div']
for tag in soup.find_all(True):
if tag.name not in keep_tags:
tag.decompose()
raw_text = soup.get_text(separator=' ', strip=True)
cleaned = clean_text(raw_text)
return url, cleaned
except (ConnectionError, HTTPError, Timeout, RequestException) as e:
print(f"[!] Failed to fetch {url}: {e}")
# Try next url in fallback loop
continue
raise Exception(f"Could not fetch content from HTTPS or HTTP for domain: {domain}")
def hash_content(content):
return hashlib.sha256(content.encode('utf-8')).hexdigest()
def save_log(domain, content):
now = datetime.now()
timestamp = now.strftime("%Y%m%d.%H%M%S")
domain_name = domain.replace("https://", "").replace("http://", "").split("/")[0]
filename = f"{domain_name}.{timestamp}.log"
with open(filename, "w", encoding="utf-8") as f:
f.write(content)
print(f"[+] Change detected. Log saved to: {os.path.abspath(filename)}")
def prompt_credentials(args):
try:
# Prompt email if -e passed without value or empty string
if args.email is not None and not args.email.strip():
args.email = input("Enter your email: ").strip()
# Prompt password only if email is set and password not provided
if args.email and not args.password:
args.password = getpass("Enter your password: ")
except KeyboardInterrupt:
print("\n[!] Input canceled by user. Exiting.")
sys.exit(0)
return args
def main():
parser = argparse.ArgumentParser(description="Monitor a webpage for changes.")
parser.add_argument("-d", "--domain", type=str, required=True, help="Domain to monitor (e.g., example.com)")
parser.add_argument("--time", type=int, default=60, help="Polling interval in seconds (default: 60)")
parser.add_argument("-e", "--email", nargs="?", const="", help="Your email address (optional prompt)")
parser.add_argument("-p", "--password", nargs="?", const="", help="Your password (optional; will prompt if not provided)")
args = parser.parse_args()
args = prompt_credentials(args)
domain = args.domain
print(f"[+] Monitoring: {domain} every {args.time} seconds")
if args.email:
print(f"[+] Email: {args.email}")
previous_hash = None
current_url = None # to remember actual URL used (https/http)
while True:
try:
url_used, content = fetch_content_with_fallback(domain)
current_url = url_used
current_hash = hash_content(content)
if previous_hash and current_hash != previous_hash:
save_log(current_url, content)
else:
print(f"[{datetime.now().strftime('%H:%M:%S')}] No change.")
previous_hash = current_hash
except Exception as e:
print(f"[!] Error: {e}")
try:
time.sleep(args.time)
except KeyboardInterrupt:
print("\n[!] Monitoring stopped by user. Goodbye!")
sys.exit(0)
if __name__ == "__main__":
try:
main()
except KeyboardInterrupt:
print("\n[!] Monitoring stopped by user. Goodbye!")
sys.exit(0)