HOW TO: Delete all Substack Drafts!
In this one: Bulk deletion of substack drafts using RSelenium
We automated substack! That was great fun and easy enough using RSelenium. In doing so though we created a *bunch* of drafts. Every iteration through, every peculiar logical navigation that Substack requires, each required creating a draft. Deleting that by hand would take forever and having so many drafts had already tripped us up with an image update. So…. we fixed it. We went from a couple of hundred drafts to zero in a couple of minutes. This is a work in progress/hack script, your milage may vary (particularly if the view more images change).
Here’s the R code, much as before we are using the RSelenium package
# Substack does not have a “Delete all drafts” button, so we built one using RSelenium.
# uses the same connection code as before:
## Connect
seleniumConnect <- function(){
## selenium connection created here:
sel <- remoteDriver(
remoteServerAddr = "localhost",
port = 4445L,
browserName = "firefox"
)
sel$open()
print(sel$getStatus());
gs.exportAll(sel);
return(sel);
}
NavigateSubstackAndLogin <- function(sel){
sel$navigate("http://www.substack.com/");
## signin here
print(sel$getCurrentUrl());
sel$navigate("https://substack.com/sign-in?redirect=%2F");
## find the web element for email webElem <- sel$findElement(using = "css", "[name = 'email']");
## enter our username
webElem$clearElement();
webElem$sendKeysToElement(list("probablyNotMy@email.address.com"));
## same again for password
## first click the sign in with password option
webElem <- sel$findElement(using= "css", "[class='login-option substack-login__login-option']");
print(webElem);
webElem$clickElement()
## now locate and enter the password
webElem <- sel$findElement(using = "css", "[name = 'password']");
webElem$clearElement();
webElem$sendKeysToElement(list(vulpusPassword));
print(webElem);
print("Hitting the password continue");
## click the sign in button webElem <- sel$findElement(using= "css", "[class='button primary']")
webElem$clickElement()
## force a wait for a button
Sys.sleep(5);
ignoreMe <- sel$findElements(using= "css", "[type='button']")
print("Login should be complete");
}
## And BULK delete drafts
## Be careful, this is an automated delete:-
## if the navigation is wrong, YOU WILL DELETE SOMETHING ELSE!
## the keep parameter is how many drafts you want to remain (useful for testing)
vulpus_delete_drafts <- function(sel=NULL, keep=5){
if(is.null(sel)){
sel <- seleniumConnect()
navigateSubstackAndLogin(sel);
}
sel$navigate("https://vulpus.substack.com/publish/posts");
Sys.sleep(2);
drafts <- sel$findElement(using = "xpath", '//div[contains(text(), "Drafts")]')
drafts$clickElement();
Sys.sleep(2);
while(TRUE){
## maybe adapt this to look at the y position of the elements print("refreshing viewMores");
## SVG has an unusual namespace
viewMores <- sel$findElements(using = "xpath", '//button/*[name()="svg"]/*[1][name()="circle" and @r=1][1]/parent::*/parent::*');
print(length(viewMores));
gs.exportAll(viewMores);
if(length(viewMores) <= keep){
print("Seems to be below keeper limit");
break;
}else{
for(xx in 1:(min(c(3, length(viewMores))))){
viewMores[[xx]]$clickElement();
Sys.sleep(1.2);
deletePopup <- sel$findElement(using = "xpath", '//div[contains(text(), "Delete")]')
deletePopup$clickElement();
Sys.sleep(1.5);
inputs <- sel$findElements(using = "xpath", '//input')
if(length(inputs) >= 2){
inputs[[2]]$clickElement();
inputs[[2]]$sendKeysToElement(list("delete"))
deleteButton <- sel$findElement(using = "xpath", '//button[contains(text(), "Delete")]')
deleteButton$clickElement();
}
Sys.sleep(1);
}
}
}
Best of luck in all things,
Vulpus