Python for SEO Data Analysis: Automate Logs & Keywords
Master Python for SEO data analysis. Learn how to write scripts for log-file parsing, clean massive crawl exports, and perform bulk keyword clustering.

Previously in this course, we examined how to track performance metrics in modern search environments, as detailed in Measuring Performance in the AI Era: Metrics for Modern SEO. While dashboards and commercial tools provide aggregate views, enterprise-scale search optimization inevitably hits a wall when dealing with raw, unaggregated data. Spreadsheets choke on multi-gigabyte exports, and manual analysis becomes impossible. This lesson adds programmatic muscle to your workflow by teaching you how to use Python, SEO Automation, Data Analysis, and Scripting to process hundreds of millions of data points locally or in distributed environments.
Modern technical audits require more than clicking around in commercial software. Whether you are extracting insights from server logs, sanitizing 500,000-row Screaming Frog exports, or clustering 100,000 unorganized search queries, Python gives you direct access to the metal.
Log-File Parsing at Scale with Python
When your site spans millions of URLs, third-party log analyzers often impose artificial limits or cost prohibitive subscription tiers. Writing a custom parser using Python allows you to ingest raw Apache or Nginx access logs, filter out noise, and pinpoint how search bots navigate your site architecture (building on the foundational concepts covered in Log-File Analysis for Search Engine Behavior: A Pro Guide).
Instead of loading a 10GB log file entirely into RAM—which will crash standard machines—we use Python's built-in iterator pattern to process the file line by line.
PYTHONimport re import pandas as pd from urllib.parse import urlparse # Define a regular expression pattern for standard NGINX/Apache combined log format LOG_PATTERN = re.compile( rCE9178">'(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) - - ' rCE9178">'\[(?P<timestamp>[^\]]+)\] ' rCE9178">'"(?P<method>[A-Z]+) (?P<url>[^\s]+) [^"]+" ' rCE9178">'(?P<status>\d{3}) (?P<bytes>\d+|-)' ) def parse_log_line(line): match = LOG_PATTERN.match(line) if match: return match.groupdict() return None def stream_and_filter_logs(filepath, bot_identifier="Googlebot"): parsed_rows = [] # Open file using a generator context to preserve system memory with open(filepath, CE9178">'r', encoding=CE9178">'utf-8', errors=CE9178">'ignore') as f: for line in f: data = parse_log_line(line) if data and bot_identifier in line: # Quick substring check before deep filtering parsed_rows.append(data) return pd.DataFrame(parsed_rows) # Execute streaming parse on an enterprise log dump df_logs = stream_and_filter_logs(CE9178">'access_log_enterprise.log') print(f"Total verified crawler hits loaded: {len(df_logs)}")
Once loaded into a Pandas DataFrame, you can instantly aggregate crawl frequency by directory, isolate orphan pages receiving bot hits but no internal links, or cross-reference bot status codes against your server-side health checks (aligning with Technical Health Monitoring via Server-Side Data for SEO).
Cleaning Large-Scale Crawl Exports

Enterprise crawls routinely export CSV files with millions of rows, frequently containing malformed characters, inconsistent URL casings, trailing slashes, and duplicate entries. Relying on spreadsheet formulas for this scale results in corrupted data or outright application crashes.
Here is a robust script using Pandas to ingest, normalize, and clean a massive crawl export:
PYTHONimport pandas as pd def clean_crawl_export(file_path): # Read CSV in chunks if the file exceeds available RAM (e.g., chunksize=100000) df = pd.read_csv(file_path, low_memory=False) initial_count = len(df) # 1. Normalize URLs: strip whitespace, force lowercase, remove trailing slashes df[CE9178">'Address'] = df[CE9178">'Address'].astype(str).str.strip().str.lower().str.rstrip(CE9178">'/') # 2. Drop exact duplicate URLs keeping the first instance df = df.drop_duplicates(subset=[CE9178">'Address']) # 3. Handle missing values in critical columns df[CE9178">'Title 1'] = df[CE9178">'Title 1'].fillna(CE9178">'MISSING_TITLE') df[CE9178">'Status Code'] = df[CE9178">'Status Code'].fillna(0).astype(int) # 4. Filter for indexable internal HTML pages indexable_df = df[ (df[CE9178">'Status Code'] == 200) & (df[CE9178">'Content'].str.contains(CE9178">'html', na=False)) & (~df[CE9178">'Indexability'].str.contains(CE9178">'Noindex', case=False, na=False)) ] print(f"Cleaned {initial_count} rows down to {len(indexable_df)} indexable assets.") return indexable_df cleaned_crawl = clean_crawl_export(CE9178">'enterprise_crawl_export.csv')
Bulk Keyword Clustering Using Machine Learning
Manual keyword grouping breaks down past a few thousand terms. To structure content architecture or map keyword intent at an enterprise scale, we leverage machine learning libraries like scikit-learn to cluster terms based on semantic similarity using TF-IDF vectorization and K-Means clustering.
PYTHONimport pandas as pd from sklearn.feature_extraction.text import TfidfVectorizer from sklearn.cluster import MiniBatchKMeans def cluster_keywords(csv_path, num_clusters=50): df = pd.read_csv(csv_path) keywords = df[CE9178">'Keyword'].astype(str).tolist() # Vectorize keywords using character and word n-grams for semantic capture vectorizer = TfidfVectorizer(analyzer=CE9178">'char_wb', ngram_range=(3, 5), max_features=10000) X = vectorizer.fit_transform(keywords) # Use MiniBatchKMeans for rapid clustering on large datasets kmeans = MiniBatchKMeans(n_clusters=num_clusters, random_state=42, batch_size=1024) df[CE9178">'Cluster'] = kmeans.fit_predict(X) # Sort and export clustered dataset df_sorted = df.sort_values(by=CE9178">'Cluster') df_sorted.to_csv(CE9178">'clustered_keywords_output.csv', index=False) print(f"Successfully clustered {len(df)} keywords into {num_clusters} thematic groups.") return df_sorted # Run the clustering pipeline # clustered_df = cluster_keywords(CE9178">'gsc_query_export.csv', num_clusters=100)
Hands-On Exercise
Write a Python script that reads a mock log file containing 10,000 lines, extracts all requests returning a 500 status code, and outputs them to a clean CSV report.
- Step 1: Create a sample text file named
test_logs.logwith mixed 200, 404, and 500 status code lines. - Step 2: Write a script using regular expressions to iterate through the file line by line.
- Step 3: Collect matching lines, convert them into a Pandas DataFrame, and save the result as
server_errors.csv.
Common Pitfalls
- Loading Entire Files into RAM: Never use
pd.read_csv()oropen().readlines()on multi-gigabyte log files without chunking or streaming via generators. Your OS will run out of memory and kill the process. - Ignoring URL Normalization Variants: Failing to lowercase URLs and strip trailing slashes before running duplicate checks will leave behind shadow duplicates that skew your technical audit metrics.
- Over-Clustering Keyword Data: Setting K-Means cluster counts too high creates fragmented single-keyword groups; setting them too low lumps disparate search intents together. Always test multiple cluster increments against manual spot-checks.
Recap

By harnessing Python for SEO data analysis, you eliminate the artificial boundaries imposed by spreadsheets and commercial UI limits. You can now stream multi-gigabyte log files, sanitize massive crawl exports cleanly, and group hundreds of thousands of keywords using machine learning algorithms.
Up next: BigQuery for Enterprise SEO — where we take massive data processing to the cloud using SQL and scalable data warehouses.
Work with me

AI Automation & Agentic Workflow Development
Automate the repetitive work eating your time — content pipelines, data workflows, and agentic AI tasks that run themselves.

Next.js Full-Stack Web App Development
A fast, SEO-ready full-stack web app built with Next.js 16 — from idea to deployed product, by an engineer who ships to production.

