#!/usr/bin/env python3 """Fetch archived 2009-2010 Twitter API docs from the Wayback Machine. For each apiwiki method page, pick the latest 200 snapshot, download the raw page (id_ flag), and extract
example blocks into testdata/wayback/.
Usage: python3 scripts/fetch_wayback.py
"""
import html, json, os, re, sys, time, urllib.parse, urllib.request
OUT = os.path.join(os.path.dirname(__file__), "..", "testdata", "wayback")
RAW = os.path.join(OUT, "raw")
# (pattern, latest timestamp to consider). apiwiki pages became "this page
# has moved" stubs in late 2010, so they are capped; dev.twitter.com/doc
# replaced them and carries the late-2010 response shapes (with id_str).
PATTERNS = [
("apiwiki.twitter.com/Twitter-REST-API-Method*", "20100731"),
("apiwiki.twitter.com/Twitter-Search-API-Method*", "20100731"),
("apiwiki.twitter.com/Return-Values*", "20100731"),
("apiwiki.twitter.com/HTTP-Response-Codes-and-Errors*", "20100731"),
("apiwiki.twitter.com/Rate-limiting*", "20100731"),
("apiwiki.twitter.com/Authentication*", "20100731"),
("apiwiki.twitter.com/OAuth-FAQ*", "20100731"),
("apiwiki.twitter.com/Things-Every-Developer-Should-Know*", "20100731"),
("dev.twitter.com/doc/get/*", "20110331"),
("dev.twitter.com/doc/post/*", "20110331"),
]
def get(url, tries=6):
for i in range(tries):
try:
req = urllib.request.Request(url, headers={"User-Agent": "mockingbird-research/1.0"})
with urllib.request.urlopen(req, timeout=60) as r:
b = r.read()
if b"Temporarily Offline" in b[:2000]:
raise IOError("archive offline")
return b
except Exception as e:
print(" retry", i, url[:100], e, file=sys.stderr)
time.sleep(3 + i * 4)
return None
def slug(u):
p = urllib.parse.unquote(urllib.parse.urlsplit(u).path).strip("/")
p = p.replace(" ", "-").replace(":", "").replace(" ", "-")
return re.sub(r"[^A-Za-z0-9._-]+", "-", p) or "index"
def main():
os.makedirs(RAW, exist_ok=True)
latest = {}
for pat, to in PATTERNS:
cdx = "https://web.archive.org/cdx/search/cdx?" + urllib.parse.urlencode(
{"url": pat, "output": "json", "filter": "statuscode:200", "fl": "original,timestamp", "to": to})
b = get(cdx)
if not b:
print("cdx failed", pat, file=sys.stderr); continue
rows = json.loads(b or b"[]")[1:]
for orig, ts in rows:
if "SearchFor=" in orig or "%E2%97%8F" in orig:
continue
if "dev.twitter.com" in orig:
# The dev.twitter.com index is polluted with URLs scraped
# from JavaScript; keep only real method pages.
path = urllib.parse.unquote(urllib.parse.urlsplit(orig).path)
if not re.match(r"^/doc/(get|post|delete)[/ ][a-z_]+(/[a-z_:]+)*/?$", path, re.I):
continue
key = slug(orig.split("?")[0].replace(":80/", "/"))
if "mode=print" in orig: # prefer print views, they have less chrome
ts = ts + "p"
if key not in latest or ts > latest[key][1]:
latest[key] = (orig, ts)
index = {}
for key, (orig, ts) in sorted(latest.items()):
dest = os.path.join(RAW, key + ".html")
if not os.path.exists(dest):
b = get("https://web.archive.org/web/%sid_/%s" % (ts.rstrip("p"), orig))
if not b:
continue
open(dest, "wb").write(b)
s = open(dest, encoding="utf-8", errors="replace").read()
if "This page has moved" in s:
os.remove(dest)
continue
pres = [html.unescape(re.sub(r"<[^>]+>", "", p)) for p in re.findall(r"]*>(.*?)
", s, re.S)]
text = html.unescape(re.sub(r"<[^>]+>", "", re.sub(r"|", "", s, flags=re.S)))
text = re.sub(r"\n\s*\n+", "\n", text)
open(os.path.join(OUT, key + ".txt"), "w").write(text)
if pres:
open(os.path.join(OUT, key + ".pre.txt"), "w").write("\n\n=====\n\n".join(pres))
index[key] = {"url": orig, "timestamp": ts.rstrip("p"), "examples": len(pres)}
print(key, ts, len(pres))
json.dump(index, open(os.path.join(OUT, "index.json"), "w"), indent=1, sort_keys=True)
if __name__ == "__main__":
main()