Repository: AmeliaMN/deleting_facebook
Branch: master
Commit: 4ae914b3d5dc
Files: 10
Total size: 27.0 KB
Directory structure:
gitextract_g6lawsdp/
├── .gitignore
├── FacebookFriends.Rmd
├── FacebookFriends.md
├── FacebookPhotosOfYou.Rmd
├── FacebookPhotosOfYou.md
├── LeavingFacebook.txt
├── README.md
├── deleting_facebook.Rproj
├── parsing_birthdays.Rmd
└── parsing_birthdays.md
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
.DS_Store
*.html
!FacebookFriends.html
!FacebookPhotosOfYou.html
!UselessScraps.html
*.csv
.Rproj.user
/img/*
*.jpeg
*.jpg
*.ics
================================================
FILE: FacebookFriends.Rmd
================================================
---
title: "Parsing Facebook friends HTML"
output: github_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, eval = FALSE)
library(rvest)
library(tibble)
library(readr)
library(here)
```
## Parsing Facebook friends from HTML (not recommended)
Since I downloaded my data in HTML format rather than the much easier to use JSON format, I had to parse it. Here's my code for that:
```{r}
here("../Documents/facebook-ameliamcnamara/friends")
facebookfriend_html <- read_html(here("../Documents/facebook-ameliamcnamara/friends", "friends.html"))
friends <- html_nodes(facebookfriend_html, "._2lel") %>%
html_text() %>%
as.tibble()
## write_csv(friends, "facebookfriends.csv")
```
If you are following along, you should download your data as JSON to avoid having to do this.
================================================
FILE: FacebookFriends.md
================================================
Parsing Facebook friends HTML
================
Parsing Facebook friends from HTML (not recommended)
----------------------------------------------------
Since I downloaded my data in HTML format rather than the much easier to use JSON format, I had to parse it. Here's my code for that:
``` r
here("../Documents/facebook-ameliamcnamara/friends")
facebookfriend_html <- read_html(here("../Documents/facebook-ameliamcnamara/friends", "friends.html"))
friends <- html_nodes(facebookfriend_html, "._2lel") %>%
html_text() %>%
as.tibble()
## write_csv(friends, "facebookfriends.csv")
```
If you are following along, you should download your data as JSON to avoid having to do this.
================================================
FILE: FacebookPhotosOfYou.Rmd
================================================
---
title: "Scraping tagged photos"
output: github_document
---
```{r setup, include=FALSE}
knitr::opts_chunk$set(echo = TRUE, eval = FALSE)
library(tidyverse)
```
## Tagged photos of me
Another goal was to get tagged "photos of you" pictures backed up. Facebook really doesn't want you to do this. I tried several approaches. (Some that failed are detailed in UselessScraps.Rmd)
### RSelenium
Selenium is good because it drives a real browser. This is the approach the Python scripts for scraping tagged photos take. Those scripts don't work because Facebook has changed the HTML, but I figured I could debug the code better in R.
Since I don't know about Docker, I just used the `rsDriver` function to navigate.
```{r}
library(RSelenium)
rD <- rsDriver(port = 4445L, browser = "firefox", version = "3.141.59")
remDr <- rD$client
```
I was able to get logged in pretty easily.
```{r}
remDr$navigate("https://www.facebook.com/")
YourLogIN <- ""
YourPassword <- "" # this is where your password goes
remDr$findElement("id", "email")$sendKeysToElement(list(YourLogIN))
remDr$findElement("id", "pass")$sendKeysToElement(list(YourPassword))
remDr$findElement("id", "loginbutton")$clickElement()
```
The first step is to navigate to photos of me. I am lazy, so I just hardcoded this to my facebook URL. There is probably a way to grab this automatically, but we're aiming low here.
```{r}
remDr$navigate("https://www.facebook.com/amelia.mcnamara/photos_of")
```
Then, I click on the first tagged photo of me to get the slideshow to start.
```{r}
webElem <- remDr$findElement(using = "class", "uiMediaThumb")
webElem$clickElement()
```
It would be handy if we could right-click with RSelenium to download the files. But, it doesn't work. So instead we're just going to grab the src links and do the downloading separately.
I tried a couple approaches with while statements that did not work. So, here is another hacky solution:
- make a character vector of length 5000 (seemed likely to be a much greater number than the number of tagged photos of me)
- run a for loop to iteratively grab the src and click on the next button
- checked to see if the current src was the same as the first (photos eventually loop back around) and if so, stop
```{r}
srcs <- character(5000)
## first two
for (i in 1:2) {
img1 <- remDr$findElement(using = "class", "spotlight")
srcs[i] <- img1$getElementAttribute("src")[[1]]
nextButton <- remDr$findElement(using = "class", "next")
nextButton$clickElement()
}
for (i in 3:5000) {
img1 <- remDr$findElement(using = "class", "spotlight")
srcs[i] <- img1$getElementAttribute("src")[[1]]
if (srcs[i] == srcs[1]) {
stop(paste("done, with", i-1, "photos"))
}
nextButton <- remDr$findElement(using = "class", "next")
nextButton$clickElement()
}
```
Then, I made a tibble with those urls, and made sure to just grab the distinct ones. Then I made up some names for the files I was about to download. I saved this dataframe to an external file so I wouldn't lose those valuable URLs!
```{r}
srcs_df <- tibble(url = srcs)
srcs_df <- srcs_df %>%
distinct(url)
srcs_df <- srcs_df %>%
mutate(filename = paste0("img/FB_download", row_number(), ".jpg"))
write_csv(srcs_df, "fb_images.csv")
```
To finish up with RSelenium, you need to close the session,
```{r}
remDr$close()
```
Somehow this never fully works, so I end up having to go into Activity Monitor and kill the Java process.
## Downloading images
The final step is to download the images. I ended up with 878 tagged photos on facebook, so again I did a for loop (perhaps this could have been vectorized?).
```{r}
library(curl)
srcs_df <- read_csv("fb_images.csv")
for (i in 1:878) {
curl_download(srcs_df$url[i], srcs_df$filename[i])
}
```
Success! I now have all the files on my local computer. And, if I want to grab them on a different computer I have the csv of URLs (which don't seem to have time-expirations in them, as some facebook URLs do) to run the `curl` stuff again.
================================================
FILE: FacebookPhotosOfYou.md
================================================
Scraping tagged photos
================
## Tagged photos of me
Another goal was to get tagged “photos of you” pictures backed up.
Facebook really doesn’t want you to do this. I tried several approaches.
(Some that failed are detailed in UselessScraps.Rmd)
### RSelenium
Selenium is good because it drives a real browser. This is the approach
the Python scripts for scraping tagged photos take. Those scripts don’t
work because Facebook has changed the HTML, but I figured I could debug
the code better in R.
Since I don’t know about Docker, I just used the `rsDriver` function to
navigate.
``` r
library(RSelenium)
rD <- rsDriver(port = 4445L, browser = "firefox", version = "3.141.59")
remDr <- rD$client
```
I was able to get logged in pretty easily.
``` r
remDr$navigate("https://www.facebook.com/")
YourLogIN <- ""
YourPassword <- "" # this is where your password goes
remDr$findElement("id", "email")$sendKeysToElement(list(YourLogIN))
remDr$findElement("id", "pass")$sendKeysToElement(list(YourPassword))
remDr$findElement("id", "loginbutton")$clickElement()
```
The first step is to navigate to photos of me. I am lazy, so I just
hardcoded this to my facebook URL. There is probably a way to grab this
automatically, but we’re aiming low here.
``` r
remDr$navigate("https://www.facebook.com/amelia.mcnamara/photos_of")
```
Then, I click on the first tagged photo of me to get the slideshow to
start.
``` r
webElem <- remDr$findElement(using = "class", "uiMediaThumb")
webElem$clickElement()
```
It would be handy if we could right-click with RSelenium to download the
files. But, it doesn’t work. So instead we’re just going to grab the src
links and do the downloading separately.
I tried a couple approaches with while statements that did not work. So,
here is another hacky solution:
- make a character vector of length 5000 (seemed likely to be a much
greater number than the number of tagged photos of me)
- run a for loop to iteratively grab the src and click on the next
button
- checked to see if the current src was the same as the first (photos
eventually loop back around) and if so, stop
<!-- end list -->
``` r
srcs <- character(5000)
## first two
for (i in 1:2) {
img1 <- remDr$findElement(using = "class", "spotlight")
srcs[i] <- img1$getElementAttribute("src")[[1]]
nextButton <- remDr$findElement(using = "class", "next")
nextButton$clickElement()
}
for (i in 3:5000) {
img1 <- remDr$findElement(using = "class", "spotlight")
srcs[i] <- img1$getElementAttribute("src")[[1]]
if (srcs[i] == srcs[1]) {
stop(paste("done, with", i-1, "photos"))
}
nextButton <- remDr$findElement(using = "class", "next")
nextButton$clickElement()
}
```
Then, I made a tibble with those urls, and made sure to just grab the
distinct ones. Then I made up some names for the files I was about to
download. I saved this dataframe to an external file so I wouldn’t lose
those valuable URLs\!
``` r
srcs_df <- tibble(url = srcs)
srcs_df <- srcs_df %>%
distinct(url)
srcs_df <- srcs_df %>%
mutate(filename = paste0("img/FB_download", row_number(), ".jpg"))
write_csv(srcs_df, "fb_images.csv")
```
To finish up with RSelenium, you need to close the session,
``` r
remDr$close()
```
Somehow this never fully works, so I end up having to go into Activity
Monitor and kill the Java process.
## Downloading images
The final step is to download the images. I ended up with 878 tagged
photos on facebook, so again I did a for loop (perhaps this could have
been vectorized?).
``` r
library(curl)
srcs_df <- read_csv("fb_images.csv")
for (i in 1:878) {
curl_download(srcs_df$url[i], srcs_df$filename[i])
}
```
Success\! I now have all the files on my local computer. And, if I want
to grab them on a different computer I have the csv of URLs (which don’t
seem to have time-expirations in them, as some facebook URLs do) to run
the `curl` stuff again.
================================================
FILE: LeavingFacebook.txt
================================================
In two weeks, I will be deleting my facebook account.
I’ve thought about this for years, and while I love facebook and have gotten so much out of it, as someone who teaches about data I can no longer justify having an account. For me, the final straw was facebook’s decision to exempt politicians from fact-checking.
Facebook has made it clear again and again that all they value is money. Yes, I know they are a corporation. But they could choose to do the right thing and sacrifice a little profit in order to be a force for good in the world. They have tremendous power to influence people’s lives, and as far as I can tell, they are abusing it.
[By the way, if you see this post in your feed I’d appreciate it if you’d “like” or “heart” it, because I’ve heard that will boost it in the algorithm.]
I’ll still be active on social media. While I’m deleting Facebook, Messenger, and WhatsApp, I’m keeping Instagram for the time being, I’m on LinkedIn (maybe I’ll figure out what that’s for) and I’m very active on Twitter.
I’m also starting a little newsletter about my life. My plan is to send an update maybe four times a year. Anyone who I’m friends with on here is welcome to subscribe. It doesn’t matter if we went to high school together and haven’t talked in years, if you’re a coworker from that one summer I tempted at Toyota Financial Services, or you’re one of my far-flung cousins. Somehow it feels like it’s more personal to subscribe to a newsletter than to be facebook friends, but in this transition I’m trying for more personal connection. Here’s the link: [redacted]
It’s strange that I feel something like grief as I leave facebook. I joined in 2006, and have logged in almost every day since. My relationships and friendships are documented here. Facebook has allowed me to watch the kids of Unitarian friends from Los Angeles grow up. It helped me meet up with a friend I know from the University of Cincinnati who was visiting Minneapolis from Italy. Facebook suggests events I might otherwise not have heard of. It connects me with family near and far.
But, I give talks about Algorithmic Inequality, and facebook perpetuates that.
The reason for the two-week delay is to allow me to gather my things. I’ve already downloaded my facebook data and am backing that up in a couple spots. I’m planning to go through photos I’m tagged in and save some of those. Once I have the digital files I’ll be getting favorite pictures printed out. I want to make sure I capture people’s birthdays on my calendar.
It’s also a chance for you to check in, say bye, give me your email address or cellphone number or weird newsletter subscription link. I am really going to miss the connections! So let’s stay connected.
================================================
FILE: README.md
================================================
# Documentation of what I did to prepare to delete my Facebook account.
If you want a more narrative view of this whole thing, check out [the blogpost I wrote about the whole process](https://www.amelia.mn/blog/2019-12-29-Deleting-Facebook/). Otherwise, see the bullets below.
Here are the steps I took:
- [Downloaded my facebook data](https://www.facebook.com/help/1701730696756992/?helpref=hc_fnav). This comes with a lot of data. Most importantly, all the photos I had uploaded to facebook over the years, organized by album, and a list of all my facebook friends. Initially, I unchecked some boxes in the facebook data, so I hadn't downloaded the list of Apps and Websites I used facebook to log in with. I eventually went back and grabbed those.
- [Posted on facebook](LeavingFacebook.txt) about my plan, announcing I'm starting a small (personal) newsletter, and giving a two-week countdown to get my affairs in order.
- [Parsed the HTML file of my friends list](FacebookFriends.md). This HTML file is probably the least-useful way they could have provided the list of my friends, but with the help of [rvest](https://rvest.tidyverse.org/) and [SelectorGadget](https://selectorgadget.com/) I was able to find the right elements to get a data frame. [Edit! I missed the option to dowload your data in JSON format, which is much easier to parse than the HTML. This would be my recommended method.]
- Made a google sheet of my entire friends list, as an accessible place for the data.
- Went through the entire list categorizing how I knew people and how much I want to stay in contact. (This took several days, as I had over 600 facebook friends!)
- Exported my friends' birthdays from facebook. Facebook has changed their settings, so it's no longer easy to do this. But, [mobeigi has a python script](https://github.com/mobeigi/fb2cal) that will do it for you. As he warns, using this script made facebook temporarily suspend my account because of suspected security issues. I was able to get back in.
- [Downloaded all the photos I am tagged in](FacebookPhotosOfYou.md). There were methods available on the internet using Python (see, [gnmerritt](https://gnmerritt.net/deletefacebook/2018/04/03/fb-photos-of-me/), [jcontini](https://github.com/jcontini/fb-photo-downloader), and [KatyaRuth](https://github.com/KatyaRuth/photos-of-you)), but none of them worked with the current version of facebook (I think Facebook likely changed their API to make it more difficult to scrape this stuff). I'm not a Python person, so it was hard for me to debug what was not working about them. I wrote [my own code using RSelenium](FacebookPhotosOfYou.md) to do the browser stuff. It is highly inefficient, but it works!
- Made sure the Apps and Websites I used facebook to log in to have alternate modes of access.
- Go through my google sheet again and make sure I have other modes of contact for the folks I want to stay in touch with. [Didn't do a great job of this, but it's time to let go!]
- **Actually deleted my account!!**
- [Parsed the birthday .ics file](parsing_birthdays.md) to save just the birthdays of people I want to stay in contact with. (I don't need 600+ birthday reminders on my calendar!)
- Ordered prints of favorite photos from Spoonflower, organized them into a photo album.
- Cleared cookies from my work computer, home computer, and phone
- Wrote [a blog post](https://www.amelia.mn/blog/2019-12-29-Deleting-Facebook/) about my entire process
## What am I going to use instead?
- I'm still going to be active on Twitter. I have a LinkedIn account. And, for right now I'm keeping Instagram. I know Instagram is owned by facebook, but it just feels like too much to delete IG right now. Baby steps!
- I'm starting a personal newsletter to send updates to my friends and family. I'm using [TinyLetter](https://tinyletter.com/) for this.
- To keep up on local events, I'm planning to pay closer attention to local arts publications, like the [City Pages](http://www.citypages.com/) in the Twin Cities.
- For events I'm hosting, I'll continue to use [Paperless Post](https://www.paperlesspost.com/) to invite folks (I haven't used FB for event invites in a couple of years).
- Mostly, I'm going to be emailing, texting, calling, and talking face-to-face with my social network!
================================================
FILE: deleting_facebook.Rproj
================================================
Version: 1.0
RestoreWorkspace: Default
SaveWorkspace: Default
AlwaysSaveHistory: Default
EnableCodeIndexing: Yes
UseSpacesForTab: Yes
NumSpacesForTab: 2
Encoding: UTF-8
RnwWeave: knitr
LaTeX: pdfLaTeX
================================================
FILE: parsing_birthdays.Rmd
================================================
---
title: "Parsing friend's birthdays"
output: github_document
---
```{r setup, include=TRUE, message=FALSE}
knitr::opts_chunk$set(echo = TRUE, eval = FALSE)
library(calendar)
library(here)
library(dplyr)
library(readr)
```
There is a package called `calendar`, which is supposed to parse .ics files. However, it doesn't work right out of the box on a facebook-generated .ics file.
```{r, eval=FALSE}
birthdays_calendar_pkg <- ic_read(here("../Dropbox/birthdays.ics"))
```
All of the birthdays get set to the same date (October 3), which was the first birthday in my file.
So, now we need to debug to figure out what's going wrong. I am not a master debugger, so what I do is look at the source code for a function, and then run it step-by-step on my data to see where the error is.
## ic_read
The top-level function is `ic_read`.
```{r, eval=FALSE}
## taken from the source code of ic_read
birthdays <- readLines("birthdays.ics")
y <- c()
for (line in birthdays) {
if (!grepl(pattern = "[A-Z]-?[A-Z]:|[A-Z];", x = line)) {
y[length(y)] <- paste0(y[length(y)], "\n", line)
}
else {
y[length(y) + 1] <- line
}
} ## to this point, the data is still fine
ical(y) ## this is where everything becomes October 3
```
## ical
```{r, eval=FALSE}
## taken from the source code of ical
x <- y
is_df <- is.data.frame(x)
if (methods::is(x, "character")) {
ical_df <- ic_dataframe(x) ## here's where the next problem happens
ical_tibble <- tibble::as_tibble(ical_df)
if (is.null(ic_attributes)) {
attr(ical_tibble, "ical") <- ic_attributes_vec(x)
}
else {
attr(ical_tibble, "ical") <- ic_attributes
}
}
else {
if (!is_df)
stop("x must be a data frame or charcter strings")
n <- names(x)
is_core = calendar::properties_core %in% n
if (!all(is_core)) {
stop(paste0("x must contain column names: ", paste0(calendar::properties_core,
collapse = ", ")))
}
ical_tibble <- tibble::as_tibble(x)
if (is.null(ic_attributes)) {
attr(ical_tibble, "ical") <- ic_attributes_vec()
}
else {
attr(ical_tibble, "ical") <- ic_attributes
}
}
class(ical_tibble) <- c("ical", class(ical_tibble))
ical_tibble
```
## ic_dataframe
```{r, eval=FALSE}
## taken from the source code of ic_dataframe
x <- y
if (methods::is(object = x, class2 = "data.frame")) {
return(x)
}
stopifnot(methods::is(object = x, class2 = "character") |
methods::is(object = x, class2 = "list"))
if (methods::is(object = x, class2 = "character")) {
x_list <- ic_list(x) ## seems fine here
}
else if (methods::is(object = x, class2 = "list")) {
x_list <- x
}
x_list_named <- lapply(x_list, function(x) {
ic_vector(x) ## fine here
})
x_df <- ic_bind_list(x_list_named) ## good here
date_cols <- grepl(pattern = "VALUE=DATE", x = names(x_df)) ## this is the right columns
if (any(date_cols)) {
tmp <- lapply(x_df[, date_cols], ic_date) ## here's the issue. I renamed the variable to tmp to debug
x_df <- x_df %>%
mutate(`DTSTART;VALUE=DATE` = ymd(`DTSTART;VALUE=DATE`)) # this works on my particular data
}
datetime_cols <- names(x_df) %in% c("DTSTART", "DTEND")
if (any(datetime_cols)) {
x_df[datetime_cols] <- lapply(x_df[, datetime_cols],
ic_datetime)
}
x_df
```
## Working code
Since I can't see what the issue is (so terrible at `tapply`) I didn't think I could fix the function itself. Instead, I just copied the working code into a messy long chunk. Here's what works for me:
```{r}
birthdays <- readLines("birthdays.ics")
y <- c()
for (line in birthdays) {
if (!grepl(pattern = "[A-Z]-?[A-Z]:|[A-Z];", x = line)) {
y[length(y)] <- paste0(y[length(y)], "\n", line)
}
else {
y[length(y) + 1] <- line
}
} ## to this point, the data is still fine
x_list <- ic_list(y)
x_list_named <- lapply(x_list, function(x) {
ic_vector(x) ## fine here
})
x_df <- ic_bind_list(x_list_named)
x_df <- x_df %>%
mutate(`DTSTART;VALUE=DATE` = ymd(`DTSTART;VALUE=DATE`))
ical_tibble <- tibble::as_tibble(x_df)
attr(ical_tibble, "ical") <- ic_attributes_vec()
class(ical_tibble) <- c("ical", class(ical_tibble))
```
## Parsing close friends
```{r}
birthday_names <- ical_tibble %>%
select(SUMMARY)
birthday_names <- birthday_names %>%
separate(SUMMARY, into = "name", sep="'s Birthday", remove=FALSE)
```
```{r}
facebookfriends_augmented <- read_csv("facebookfriends_augmented.csv") %>%
filter(Rank %in% c(1,2))
which(birthday_names$name %in% facebookfriends_augmented$name)
birthdays <- ical_tibble[which(birthday_names$name %in% facebookfriends_augmented$name),]
```
```{r, eval=FALSE}
ic_write(birthdays, "close_friends_birthdays.ics")
```
I was able to read the .ics file into my Calendar app and it all seems to have worked! Spot-checked birthdays are correct.
Of course, not every close friend had their birthday listed on facebook. So, there is also the question of friends I should learn the birthday of:
```{r}
find_birthdays <- facebookfriends_augmented[which(!facebookfriends_augmented$name %in% birthday_names$name),]
```
```{r, eval=FALSE}
write_csv(find_birthdays, "learn_birthdays.csv")
```
================================================
FILE: parsing_birthdays.md
================================================
Parsing friend’s birthdays
================
``` r
knitr::opts_chunk$set(echo = TRUE, eval = FALSE)
library(calendar)
library(here)
library(dplyr)
library(readr)
```
There is a package called `calendar`, which is supposed to parse .ics
files. However, it doesn’t work right out of the box on a
facebook-generated .ics file.
``` r
birthdays_calendar_pkg <- ic_read(here("../Dropbox/birthdays.ics"))
```
All of the birthdays get set to the same date (October 3), which was the
first birthday in my file.
So, now we need to debug to figure out what’s going wrong. I am not a
master debugger, so what I do is look at the source code for a function,
and then run it step-by-step on my data to see where the error is.
## ic\_read
The top-level function is `ic_read`.
``` r
## taken from the source code of ic_read
birthdays <- readLines("birthdays.ics")
y <- c()
for (line in birthdays) {
if (!grepl(pattern = "[A-Z]-?[A-Z]:|[A-Z];", x = line)) {
y[length(y)] <- paste0(y[length(y)], "\n", line)
}
else {
y[length(y) + 1] <- line
}
} ## to this point, the data is still fine
ical(y) ## this is where everything becomes October 3
```
## ical
``` r
## taken from the source code of ical
x <- y
is_df <- is.data.frame(x)
if (methods::is(x, "character")) {
ical_df <- ic_dataframe(x) ## here's where the next problem happens
ical_tibble <- tibble::as_tibble(ical_df)
if (is.null(ic_attributes)) {
attr(ical_tibble, "ical") <- ic_attributes_vec(x)
}
else {
attr(ical_tibble, "ical") <- ic_attributes
}
}
else {
if (!is_df)
stop("x must be a data frame or charcter strings")
n <- names(x)
is_core = calendar::properties_core %in% n
if (!all(is_core)) {
stop(paste0("x must contain column names: ", paste0(calendar::properties_core,
collapse = ", ")))
}
ical_tibble <- tibble::as_tibble(x)
if (is.null(ic_attributes)) {
attr(ical_tibble, "ical") <- ic_attributes_vec()
}
else {
attr(ical_tibble, "ical") <- ic_attributes
}
}
class(ical_tibble) <- c("ical", class(ical_tibble))
ical_tibble
```
## ic\_dataframe
``` r
## taken from the source code of ic_dataframe
x <- y
if (methods::is(object = x, class2 = "data.frame")) {
return(x)
}
stopifnot(methods::is(object = x, class2 = "character") |
methods::is(object = x, class2 = "list"))
if (methods::is(object = x, class2 = "character")) {
x_list <- ic_list(x) ## seems fine here
}
else if (methods::is(object = x, class2 = "list")) {
x_list <- x
}
x_list_named <- lapply(x_list, function(x) {
ic_vector(x) ## fine here
})
x_df <- ic_bind_list(x_list_named) ## good here
date_cols <- grepl(pattern = "VALUE=DATE", x = names(x_df)) ## this is the right columns
if (any(date_cols)) {
tmp <- lapply(x_df[, date_cols], ic_date) ## here's the issue. I renamed the variable to tmp to debug
x_df <- x_df %>%
mutate(`DTSTART;VALUE=DATE` = ymd(`DTSTART;VALUE=DATE`)) # this works on my particular data
}
datetime_cols <- names(x_df) %in% c("DTSTART", "DTEND")
if (any(datetime_cols)) {
x_df[datetime_cols] <- lapply(x_df[, datetime_cols],
ic_datetime)
}
x_df
```
## Working code
Since I can’t see what the issue is (so terrible at `tapply`) I didn’t
think I could fix the function itself. Instead, I just copied the
working code into a messy long chunk. Here’s what works for me:
``` r
birthdays <- readLines("birthdays.ics")
y <- c()
for (line in birthdays) {
if (!grepl(pattern = "[A-Z]-?[A-Z]:|[A-Z];", x = line)) {
y[length(y)] <- paste0(y[length(y)], "\n", line)
}
else {
y[length(y) + 1] <- line
}
} ## to this point, the data is still fine
x_list <- ic_list(y)
x_list_named <- lapply(x_list, function(x) {
ic_vector(x) ## fine here
})
x_df <- ic_bind_list(x_list_named)
x_df <- x_df %>%
mutate(`DTSTART;VALUE=DATE` = ymd(`DTSTART;VALUE=DATE`))
ical_tibble <- tibble::as_tibble(x_df)
attr(ical_tibble, "ical") <- ic_attributes_vec()
class(ical_tibble) <- c("ical", class(ical_tibble))
```
## Parsing close friends
``` r
birthday_names <- ical_tibble %>%
select(SUMMARY)
birthday_names <- birthday_names %>%
separate(SUMMARY, into = "name", sep="'s Birthday", remove=FALSE)
```
``` r
facebookfriends_augmented <- read_csv("facebookfriends_augmented.csv") %>%
filter(Rank %in% c(1,2))
which(birthday_names$name %in% facebookfriends_augmented$name)
birthdays <- ical_tibble[which(birthday_names$name %in% facebookfriends_augmented$name),]
```
``` r
ic_write(birthdays, "close_friends_birthdays.ics")
```
I was able to read the .ics file into my Calendar app and it all seems
to have worked\! Spot-checked birthdays are correct.
Of course, not every close friend had their birthday listed on facebook.
So, there is also the question of friends I should learn the birthday
of:
``` r
find_birthdays <- facebookfriends_augmented[which(!facebookfriends_augmented$name %in% birthday_names$name),]
```
``` r
write_csv(find_birthdays, "learn_birthdays.csv")
```
gitextract_g6lawsdp/ ├── .gitignore ├── FacebookFriends.Rmd ├── FacebookFriends.md ├── FacebookPhotosOfYou.Rmd ├── FacebookPhotosOfYou.md ├── LeavingFacebook.txt ├── README.md ├── deleting_facebook.Rproj ├── parsing_birthdays.Rmd └── parsing_birthdays.md
Condensed preview — 10 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (29K chars).
[
{
"path": ".gitignore",
"chars": 129,
"preview": ".DS_Store\n*.html\n!FacebookFriends.html\n!FacebookPhotosOfYou.html\n!UselessScraps.html\n*.csv\n.Rproj.user\n/img/*\n*.jpeg\n*.j"
},
{
"path": "FacebookFriends.Rmd",
"chars": 807,
"preview": "---\ntitle: \"Parsing Facebook friends HTML\"\noutput: github_document\n---\n\n```{r setup, include=FALSE}\nknitr::opts_chunk$se"
},
{
"path": "FacebookFriends.md",
"chars": 688,
"preview": "Parsing Facebook friends HTML\n================\n\nParsing Facebook friends from HTML (not recommended)\n-------------------"
},
{
"path": "FacebookPhotosOfYou.Rmd",
"chars": 4017,
"preview": "---\ntitle: \"Scraping tagged photos\"\noutput: github_document\n---\n\n```{r setup, include=FALSE}\nknitr::opts_chunk$set(echo "
},
{
"path": "FacebookPhotosOfYou.md",
"chars": 3917,
"preview": "Scraping tagged photos\n================\n\n## Tagged photos of me\n\nAnother goal was to get tagged “photos of you” pictures"
},
{
"path": "LeavingFacebook.txt",
"chars": 2739,
"preview": "In two weeks, I will be deleting my facebook account.\n\nI’ve thought about this for years, and while I love facebook and "
},
{
"path": "README.md",
"chars": 4312,
"preview": "# Documentation of what I did to prepare to delete my Facebook account. \n\nIf you want a more narrative view of this whol"
},
{
"path": "deleting_facebook.Rproj",
"chars": 204,
"preview": "Version: 1.0\n\nRestoreWorkspace: Default\nSaveWorkspace: Default\nAlwaysSaveHistory: Default\n\nEnableCodeIndexing: Yes\nUseSp"
},
{
"path": "parsing_birthdays.Rmd",
"chars": 5503,
"preview": "---\ntitle: \"Parsing friend's birthdays\"\noutput: github_document\n---\n\n```{r setup, include=TRUE, message=FALSE}\nknitr::op"
},
{
"path": "parsing_birthdays.md",
"chars": 5354,
"preview": "Parsing friend’s birthdays\n================\n\n``` r\nknitr::opts_chunk$set(echo = TRUE, eval = FALSE)\nlibrary(calendar)\nli"
}
]
About this extraction
This page contains the full source code of the AmeliaMN/deleting_facebook GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 10 files (27.0 KB), approximately 7.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.