When you want to bulk unpublish old articles after migrating your Hatena Blog articles to another site.
Important Note: You Cannot Revert to Draft
With the Hatena Blog AtomPub API, you cannot revert published articles back to draft. Sending <app:draft>yes</app:draft> via a PUT request results in a 400 Cannot Change into Draft error.
Therefore, there are two approaches:
Method 1: Replace the Article Body with “This Article Has Moved”
You can rewrite the article’s <content> using the AtomPub API’s PUT method.
import requests
import xml.etree.ElementTree as ET
import time
HATENA_ID = "your_hatena_id"
BLOG_ID = "your_blog_id.hatenablog.com"
API_KEY = "your_api_key"
NEW_SITE_URL = "https://your-new-site.com"
ATOM_NS = "http://www.w3.org/2005/Atom"
def fetch_all_entries():
entries = []
url = f"https://blog.hatena.ne.jp/{HATENA_ID}/{BLOG_ID}/atom/entry"
while url:
resp = requests.get(url, auth=(HATENA_ID, API_KEY), timeout=30)
resp.raise_for_status()
root = ET.fromstring(resp.text)
for entry in root.findall(f"{{{ATOM_NS}}}entry"):
title_el = entry.find(f"{{{ATOM_NS}}}title")
title = title_el.text or "" if title_el is not None else ""
edit_link = entry.find(f"{{{ATOM_NS}}}link[@rel='edit']")
edit_url = edit_link.get("href") if edit_link is not None else None
if edit_url:
entries.append({"title": title, "edit_url": edit_url})
next_el = root.find(f"{{{ATOM_NS}}}link[@rel='next']")
url = next_el.get("href") if next_el is not None else None
return entries
def replace_content(entry):
title = entry["title"]
update_xml = f"""<?xml version="1.0" encoding="utf-8"?>
<entry xmlns="http://www.w3.org/2005/Atom"
xmlns:app="http://www.w3.org/2007/app">
<title>{title}</title>
<content type="text/plain">This article has moved to {NEW_SITE_URL}.</content>
</entry>"""
resp = requests.put(
entry["edit_url"],
auth=(HATENA_ID, API_KEY),
data=update_xml.encode("utf-8"),
headers={"Content-Type": "application/atom+xml; charset=utf-8"},
timeout=30,
)
return resp.status_code
entries = fetch_all_entries()
print(f"Found {len(entries)} entries")
for i, e in enumerate(entries):
status = replace_content(e)
print(f"[{i+1}/{len(entries)}] {status}: {e['title'][:50]}")
time.sleep(0.5)
Method 2: Delete Articles Manually from the Hatena Blog Admin Panel
If the number of articles is small, you can manually delete them from the “Manage Articles” section of the admin panel. However, since there is no bulk selection feature, this is not suitable for a large number of articles.
How to Obtain the API Key
- Log in to the Hatena Blog admin panel
- Go to “Settings” -> “Advanced Settings”
- The API key is displayed in the “AtomPub” section at the bottom of the page
Summary
| Method | Advantage | Disadvantage |
|---|---|---|
| Replace body text | URLs remain, allowing redirection from old links | Articles themselves remain |
| Delete articles | Completely removed | Old links return 404 |
| Close the blog itself | Simplest approach | Everything is deleted |
When migrating sites, it is recommended to process the old site after the new site has been indexed by Google.