HW 04: Follow the money
Looking at the upcoming elections
Purpose
Let’s examine data for this upcoming election next week. We’ll use the same Ballot Measure Contribution Totals page from the Secretary of State, but for 2026. You will scrape it yourself, save a dated copy, and use it to see who is lobbying for and against this year’s measures.
You may use an LLM when necessary to complete tasks like help debug, find selectors or identify string patterns, but annotate any LLM-generated code line by line with a comment that says what the line does.
Getting started
Load the tidyverse and rvest packages.
Part I. Before you scrape
1. The checklist
We ran the checklist on the 2024 pages as part of AE08. Double check the site to confirm that nothing has changed for the 2026 pages, and that it’s still okay to scrape.
Part II. Get all the data!
2. Write the scraper function
Write a function, scrape_prop(url), that takes the address of one proposition’s page and returns one tidy table of its committees, with the columns position, committee_id, committee_name, total and prop (the proposition number, as a number). Don’t reinvent the wheel - we did this in AE08. Copy from there and modify as needed.
Some pages may not have committees yet, so there is no table to read. Add this line to your function, right after you find the tables, to return a blank table for those pages:
if (length(tables) == 0) return(tibble())3. Scrape, date and save
Get the links to the 14 propositions from the index page and map your function to run across each one and list_rbind them into one data frame. Note: this could take a few minutes!
These pages are live, so your numbers may be different from a classmates (or yourself) depending on when you scraped the tables. Saving the data to a CSV file is like a timestamp. The date in the file name says which version of the pages your results come from.
Save the result in your data folder, with the file name props-2026_asof-2026-mm-dd.csv - where mm-dd is the date YOU scraped the data.
Run the chunk once by hand. Then add #| eval: false to the top of the chunk, so that rendering your document does not scrape the site again.
4. Keep the data out of Git
A file named .gitignore lists files that Git should leave alone and not push to github. GitHub does not like large data files, and will actively block pushes containing large data files. This file may not be large, but let’s keep it out of github anyhow.
- Open the
.gitignorefile in RStudio from the Files pane. - Add a new line with the path to your
data/folder. The trailing/tells git this is a folder. - Save the file. Look at the Git pane: your data folder should no longer be listed.
- Add and commit the changes to this hw file and your
.gitignorenow. Use the commit message “added data folder to gitignore”.
Part III. Follow the money
Write code to import your saved file back in. This is necessary for rendering because you turned off the code chunk that did the scraping (right?!?), so the data is not regenerated on a render.
5. Money by proposition
Remember: Each row is one committee. total is the contributions the committee reported receiving, not what it spent. A committee that works on more than one proposition reports a single total, and the same number appears on each of its propositions’ pages.
Calculate the number of committees and the total money reported for each proposition. Which proposition has raised the most money overall?
6. Support and opposition
Make a side-by-side barchart of the money in support and in opposition for each proposition. You’ll need to summarize the data to calculate the totals by position within prop group_by(prop, position) before you calculate the total sum.
To make the y-axis read in the millions of dollars, you can add this layer: scale_y_continuous(labels = scales::label_dollar(scale = 1e-6, suffix = "M")).
7. Who raised the most?
Which committee has raised the most money overall? Be careful: a committee that is on more than one proposition has the same total on each page. You can use the distinct(committee_id, .keep_all = TRUE) function to count each committee once.
8. Your proposition
Choose one proposition that appears in your data and that you care about. Look at its committees. In three or four sentences, say who is paying for and against it, and what these data cannot tell you.
Submit
Render your code to PDF, do one last push to github and upload the PDF to Gradescope (via Canvas)
Data citation: California Secretary of State. “2026 Ballot Measure Contribution Totals.” https://www.sos.ca.gov/campaign-lobbying/helpful-resources/measure-contributions/2026-ballot-measure-contribution-totals. Accessed on 10-25-2026.