the problem
The firm collects delinquent property taxes. Before filing suit on a batch of accounts, somebody has to check each one against the county's public tax site: is it exempt, how much is due, which years are due, has anything been paid since the judgment. Doing that by hand means loading four pages per account and reading tables. A real batch from my input files: 43 accounts — 172 page loads. That's someone's whole day.
lgbs
So i wrote a python scraper. Feed it a list of account numbers and each account's judgment-year ranges, and for every account it pulls the four pages from the county site, parses the soup with BeautifulSoup and some regex, and spits out a CSV plus a color-coded console report: exemptions, total due, years due collapsed into ranges ("2010-2014, 2019"), last payment date, and a suit-type flag. The point is triage — it tallies the accounts that aren't worth pursuing (business personal property, mobile homes, judgment years already paid, invalid account numbers) so a human only looks at the rest.
Some of the domain logic is delightfully cursed: whether an account is BPP or a mobile home is encoded in specific digits of the account number itself, so the classifier is literally account_number[5] == '9'. That's not a hack, that's how the county numbers accounts. You just have to know.

fighting the county server
The county's server speaks TLS so old that modern python refuses to shake hands with it. The fix lives in web_scraper.py: drop to a raw http client and set ssl.OP_LEGACY_SERVER_CONNECT. Government infrastructure is forever.
There's also a progress bar module whose per-tick sleep is a random delay with the comment # how long u wanna stall. It looks cosmetic. It's also the politeness throttle between requests. Dual-purpose engineering.
PID-to-TSC
The second tool is a manifest v3 chrome extension. Appraisal-district property pages show a bare "State Code" like A1, and nobody memorizes all seventy of them. One click injects a script into the page, finds the code, and translates it — "A1 → Real, Residential, Single-Family" — from an embedded lookup table. Nine commits over four days in january 2025. Sometimes a tool is just a lookup table with a button, and that's enough.
milestones
The first commit of lgbs, october 2024, is message-for-message my first git commit ever: "uploading all files to github repo first commit ever in my life". The commit messages that follow chart the learning curve — "hell yeah cimmot", "f it", "final perchance?" — though there was, to my credit, a real branch-and-merge workflow attempt on day one.
Full disclosure: the public repo is the sanitized show copy — real account data was scrubbed before publishing, and a later cleanup pass renamed a bunch of functions in the modules without updating main.py, so the github version doesn't actually run as-is. A working checkout from before the rename pass still lives on my laptop — complete with a graveyard/ folder of abandoned first attempts. The lesson about running your code after refactoring it has since been learned.
There was also an OCR side of the internship — the "cool OCR stuff" — that never made it to github.
stack
- lgbs
- Python, BeautifulSoup4, colorama, raw http.client with the legacy-TLS workaround. Output to CSV.
- pid-to-tsc
- Vanilla javascript chrome extension, manifest v3, chrome.scripting injection.
- status
- Retired — internship tooling, last touched early 2025. Both repos public.