#| read historical data for Consumer Price Index from ftp://ftp.bls.gov/pub/special.requests/cpi/cpiai.txt Since this changes slowly, I just cache the result in a file. This is meant to be read by http://ap5.com/loadurl.lisp so I use ap5::wget to read the url. I assume the user won't mind if I save an extra file... |# (ap5::wget "ftp://ftp.bls.gov/pub/special.requests/cpi/cpiai.txt" "ap5.com:cpiai.txt" "--passive-ftp") (defun read-cpi (file) (with-open-file (f file) ;; elementary page scraping ;; the data starts after a line containing YEAR ;; intentionally get an error if we don't find that line (loop until (search " YEAR" (read-line f) :test 'string-equal) do nil) ;; the line actually has an index for every month, but I settle ;; for just the Jan data (loop with line with pair while (setf line (read-line f nil nil)) when (setf pair (ignore-errors ;; in particular, one blank line (with-input-from-string (s line) (list (read s) (read s))))) collect pair))) ;; Now there's some choice of how to use this data. ;; I'd just as soon NOT read it on every access. ;; The easiest thing is to use an internal ap5 relation. (defrelation cpi :documentation "(cpi year index) - consumer price index data from BLS" :arity 2 :representation tree :types (integer number)) (loop for (x y) in (READ-CPI "ap5.com:cpiai.txt") do (ap5::++ cpi x y))