If you need to extract metadata such as title, description, keywords, Open Graph tags, canonical URL, and Twitter Card information from an HTML page, Python makes this easy using requests and BeautifulSoup.
Install Required Packages
Install the required Python packages:
pip install requests beautifulsoup4
Basic HTML Metadata Scraper
Create a Python file named metadata.py:
import requests
from bs4 import BeautifulSoup
url = "https://example.com"
response = requests.get(
url,
headers={
"User-Agent": "Mozilla/5.0"
},
timeout=10
)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
title = soup.title.string.strip() if soup.title and soup.title.string else None
description = soup.find(
"meta",
attrs={"name": "description"}
)
keywords = soup.find(
"meta",
attrs={"name": "keywords"}
)
print("Title:", title)
print("Description:", description.get("content") if description else None)
print("Keywords:", keywords.get("content") if keywords else None)
Run it:
python metadata.py
Example output:
Title: Example Domain
Description: This domain is for use in illustrative examples.
Keywords: example, domain
Extract Open Graph Metadata
Many modern websites use Open Graph metadata for sharing pages on Facebook, LinkedIn, WhatsApp, and other platforms.
A typical HTML page contains:
<meta property="og:title" content="My Website" />
<meta property="og:description" content="My website description" />
<meta property="og:image" content="https://example.com/image.jpg" />
<meta property="og:url" content="https://example.com/" />
You can extract these tags with:
og_title = soup.find(
"meta",
attrs={"property": "og:title"}
)
og_description = soup.find(
"meta",
attrs={"property": "og:description"}
)
og_image = soup.find(
"meta",
attrs={"property": "og:image"}
)
og_url = soup.find(
"meta",
attrs={"property": "og:url"}
)
print("OG Title:", og_title.get("content") if og_title else None)
print("OG Description:", og_description.get("content") if og_description else None)
print("OG Image:", og_image.get("content") if og_image else None)
print("OG URL:", og_url.get("content") if og_url else None)
Extract Twitter Card Metadata
Twitter/X uses Twitter Card metadata to control how a URL appears when shared.
For example:
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="My Website" />
<meta name="twitter:description" content="My website description" />
<meta name="twitter:image" content="https://example.com/image.jpg" />
Extract them using:
twitter_card = soup.find(
"meta",
attrs={"name": "twitter:card"}
)
twitter_title = soup.find(
"meta",
attrs={"name": "twitter:title"}
)
twitter_description = soup.find(
"meta",
attrs={"name": "twitter:description"}
)
twitter_image = soup.find(
"meta",
attrs={"name": "twitter:image"}
)
print("Twitter Card:", twitter_card.get("content") if twitter_card else None)
print("Twitter Title:", twitter_title.get("content") if twitter_title else None)
print("Twitter Description:", twitter_description.get("content") if twitter_description else None)
print("Twitter Image:", twitter_image.get("content") if twitter_image else None)
Extract Canonical URL
The canonical URL is usually defined as:
<link rel="canonical" href="https://example.com/page" />
Extract it with:
canonical = soup.find(
"link",
attrs={"rel": "canonical"}
)
print(
"Canonical:",
canonical.get("href") if canonical else None
)
Create a Reusable Metadata Function
Instead of extracting each tag separately, you can create a reusable function:
import requests
from bs4 import BeautifulSoup
def get_meta(soup, *, name=None, property=None):
if name:
tag = soup.find("meta", attrs={"name": name})
else:
tag = soup.find("meta", attrs={"property": property})
return tag.get("content") if tag else None
def scrape_metadata(url):
response = requests.get(
url,
headers={
"User-Agent": "Mozilla/5.0"
},
timeout=10
)
response.raise_for_status()
soup = BeautifulSoup(response.text, "html.parser")
return {
"title": soup.title.string.strip()
if soup.title and soup.title.string
else None,
"description": get_meta(
soup,
name="description"
),
"keywords": get_meta(
soup,
name="keywords"
),
"canonical": (
soup.find("link", rel="canonical").get("href")
if soup.find("link", rel="canonical")
else None
),
"og_title": get_meta(
soup,
property="og:title"
),
"og_description": get_meta(
soup,
property="og:description"
),
"og_image": get_meta(
soup,
property="og:image"
),
"og_url": get_meta(
soup,
property="og:url"
),
"twitter_card": get_meta(
soup,
name="twitter:card"
),
"twitter_title": get_meta(
soup,
name="twitter:title"
),
"twitter_description": get_meta(
soup,
name="twitter:description"
),
"twitter_image": get_meta(
soup,
name="twitter:image"
),
}
url = "https://example.com"
metadata = scrape_metadata(url)
for key, value in metadata.items():
print(f"{key}: {value}")
Return Metadata as JSON
If you want to use the scraper as part of an API or another application, returning JSON is more useful.
import json
metadata = scrape_metadata("https://example.com")
print(
json.dumps(
metadata,
indent=4,
ensure_ascii=False
)
)
Example:
{
"title": "Example Domain",
"description": "This domain is for use in illustrative examples.",
"keywords": null,
"canonical": "https://example.com/",
"og_title": "Example Domain",
"og_description": "Example website",
"og_image": "https://example.com/image.jpg",
"og_url": "https://example.com/",
"twitter_card": "summary_large_image",
"twitter_title": "Example Domain",
"twitter_description": "Example website",
"twitter_image": "https://example.com/image.jpg"
}
Scrape All Meta Tags
Sometimes you don’t know in advance which metadata tags a website uses. In that case, extract all <meta> tags:
for tag in soup.find_all("meta"):
name = tag.get("name")
property_name = tag.get("property")
content = tag.get("content")
if content:
print(
name or property_name,
":",
content
)
This is particularly useful when building a generic SEO metadata scraper because different websites may use different metadata conventions.
Handle HTTP Errors
Production code should handle network errors and invalid URLs.
import requests
try:
response = requests.get(
url,
headers={
"User-Agent": "Mozilla/5.0"
},
timeout=10
)
response.raise_for_status()
except requests.exceptions.Timeout:
print("Request timed out")
except requests.exceptions.RequestException as error:
print("Request failed:", error)
Important: JavaScript-Rendered Websites
requests downloads the HTML returned by the server. It does not execute JavaScript.
Therefore, if a website generates its metadata dynamically using JavaScript, BeautifulSoup may not see the final metadata.
For example:
Browser
│
├── Download HTML
├── Execute JavaScript
└── Render final page
Whereas:
Python requests
│
└── Download HTML only
For JavaScript-heavy websites, tools such as Playwright or Selenium can be used to load the page in a real browser before extracting the metadata.
Conclusion
For most server-rendered websites, the combination of:
requests
+
BeautifulSoup
is sufficient for scraping SEO and social metadata.
You can extract important fields such as:
- Page title
- Meta description
- Meta keywords
- Canonical URL
- Open Graph title
- Open Graph description
- Open Graph image
- Open Graph URL
- Twitter Card
- Twitter title
- Twitter description
- Twitter image
This approach is also a good foundation for building an SEO metadata checker, URL preview generator, content crawler, or website auditing tool.
