Compare commits

...

18 Commits

Author SHA1 Message Date
dependabot[bot] ed96dd7e6a
Bump urllib3 from 1.25.2 to 1.25.8 (#162) 3 years ago
Sean Leavey b93f528f99
Ensure protocol is provided in mobile URLs (fixes #160) (#161) 3 years ago
dependabot[bot] ddf358bed2
Bump pygments from 2.4.2 to 2.7.4 (#158) 3 years ago
dependabot[bot] 262c7f84c6
Bump lxml from 4.6.2 to 4.6.3 (#159) 3 years ago
Ian Hunter a92fd3eb35
Update Required Python version (#157) 3 years ago
Wesley Kerfoot d64ed5a3db
Update README.md 3 years ago
Wesley Kerfoot 39b2437f1d
Update README.md 3 years ago
dependabot[bot] 72eb1bdcde
Bump bleach from 3.1.4 to 3.3.0 (#155) 3 years ago
Wesley Kerfoot 86403534b9
bump versions of cattrs and attrs (#151) 3 years ago
dependabot[bot] 11c21f6d2c
Bump lxml from 4.4.0 to 4.6.2 (#150) 3 years ago
tklam a5ed694a46
Add "Hide from profile" (#145) 4 years ago
Wesley Kerfoot ede08a42a9 bump version 4 years ago
Thomas c1af411453
WebDriverException not defined (#139) 4 years ago
Wesley Kerfoot 7d24450b0f
add FUNDING.yml (#137) 4 years ago
Wesley Kerfoot 1a846f1dac bump version 4 years ago
Wesley Kerfoot a182eaaaa3
fixes https://github.com/weskerfoot/DeleteFB/issues/135 (#136) 4 years ago
Wesley Kerfoot 50162ba996
fix unliking (#134) 4 years ago
Wesley Kerfoot 9747f0a00d bump version 4 years ago
  1. 2
      FUNDING.yml
  2. 8
      README.md
  3. 1
      deletefb/tools/chrome_driver.py
  4. 21
      deletefb/tools/common.py
  5. 42
      deletefb/tools/likes.py
  6. 2
      deletefb/tools/wall.py
  7. 12
      requirements.txt
  8. 6
      setup.py

2
FUNDING.yml

@ -0,0 +1,2 @@
github: [weskerfoot]
custom: "39qHYvjVcMCNFr3RPAVetci9mKjzYGTQPz"

8
README.md

@ -1,3 +1,7 @@
WARNING:
This currently only works for English language Facebook accounts, due to the lack of a usable API.
Also, year by year deletion is currently broken. Feel free to fork or make pull requests.
## Why? ## Why?
I needed a simple and reliable way to delete Facebook posts. There are I needed a simple and reliable way to delete Facebook posts. There are
@ -18,7 +22,7 @@ Personally, I did this so I would feel less attached to my Facebook profile
(and hence feel the need to use it less). (and hence feel the need to use it less).
## Dependencies ## Dependencies
- This tool requires at least Python 3.6 in order to run. - This tool requires at least Python 3.7 in order to run.
- A recent copy of Chrome or Chromium installed and available in your `$PATH` - A recent copy of Chrome or Chromium installed and available in your `$PATH`
## Installation ## Installation
@ -57,7 +61,7 @@ optional arguments:
``` ```
* Make sure that you have a recent version of Python 3.x installed (preferably * Make sure that you have a recent version of Python 3.x installed (preferably
3.6 or greater) 3.7 or greater)
* Make sure that you have Google Chrome installed and that it is up to date * Make sure that you have Google Chrome installed and that it is up to date
* The tool will attempt to automatically install chromedriver for Selenium. See [here](https://sites.google.com/a/chromium.org/chromedriver/home) for an explanation of what the chromedriver does. You may have to manually install it if auto-install fails. * The tool will attempt to automatically install chromedriver for Selenium. See [here](https://sites.google.com/a/chromium.org/chromedriver/home) for an explanation of what the chromedriver does. You may have to manually install it if auto-install fails.
* On Linux, it will be called something like `chromium-chromedriver` or just * On Linux, it will be called something like `chromium-chromedriver` or just

1
deletefb/tools/chrome_driver.py

@ -2,6 +2,7 @@ from ..exceptions import UnknownOSException, ChromeError
from .common import NO_CHROME_DRIVER from .common import NO_CHROME_DRIVER
from clint.textui import puts, colored from clint.textui import puts, colored
from selenium import webdriver from selenium import webdriver
from selenium.common.exceptions import WebDriverException
from shutil import which from shutil import which
from subprocess import check_output from subprocess import check_output
from urllib.request import urlretrieve from urllib.request import urlretrieve

21
deletefb/tools/common.py

@ -20,13 +20,20 @@ SELENIUM_EXCEPTIONS = (
TimeoutException TimeoutException
) )
def click_button(driver, el): def click_button(driver, el):
""" """
Click a button using Javascript Click a button using Javascript
""" """
driver.execute_script("arguments[0].click();", el) driver.execute_script("arguments[0].click();", el)
def scroll_until_element_exists(driver, xpath_expr):
while True:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight)")
try:
element = driver.find_element_by_xpath(xpath_expr)
except SELENIUM_EXCEPTIONS:
continue
break
def scroll_to(driver, el): def scroll_to(driver, el):
""" """
@ -37,7 +44,6 @@ def scroll_to(driver, el):
except SELENIUM_EXCEPTIONS: except SELENIUM_EXCEPTIONS:
return return
def logger(name): def logger(name):
""" """
Args: Args:
@ -57,12 +63,11 @@ def logger(name):
logging.config.dictConfig(config["logging"]) logging.config.dictConfig(config["logging"])
return logging.getLogger(name) return logging.getLogger(name)
def wait_xpath(driver, expr, timeout=20):
def wait_xpath(driver, expr):
""" """
Takes an XPath expression, and waits at most 20 seconds until it exists Takes an XPath expression, and waits at most `timeout` seconds until it exists
""" """
wait = WebDriverWait(driver, 20) wait = WebDriverWait(driver, timeout)
try: try:
wait.until(EC.presence_of_element_located((By.XPATH, expr))) wait.until(EC.presence_of_element_located((By.XPATH, expr)))
except SELENIUM_EXCEPTIONS: except SELENIUM_EXCEPTIONS:
@ -73,7 +78,9 @@ def force_mobile(url):
Force a url to use the mobile site. Force a url to use the mobile site.
""" """
parsed = urlparse.urlparse(url) parsed = urlparse.urlparse(url)
return urlparse.urlunparse((parsed.scheme, # Ensure a protocol is given (needed by selenium).
scheme = parsed.scheme or "https"
return urlparse.urlunparse((scheme,
"mobile.facebook.com", "mobile.facebook.com",
parsed.path, parsed.path,
parsed.params, parsed.params,

42
deletefb/tools/likes.py

@ -1,6 +1,6 @@
from .archive import archiver from .archive import archiver
from ..types import Page from ..types import Page
from .common import SELENIUM_EXCEPTIONS, logger, click_button from .common import SELENIUM_EXCEPTIONS, logger, click_button, wait_xpath, force_mobile, scroll_to, scroll_until_element_exists
from selenium.webdriver.common.by import By from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support.ui import WebDriverWait
@ -18,17 +18,25 @@ def load_likes(driver, profile_url):
None None
""" """
driver.get("{0}/likes_all".format(profile_url)) likes_link_xpath = "//div[normalize-space(text())='Likes']/../..//a[contains(@href, 'app_section')]"
wait = WebDriverWait(driver, 20) all_likes_link_xpath = "//div[normalize-space(text())='All Likes']/../../../..//a[contains(@href, 'app_collection')]"
try: driver.get(force_mobile("{0}/about".format(profile_url)))
wait.until(
EC.presence_of_element_located((By.CSS_SELECTOR, ".PageLikeButton")) scroll_until_element_exists(driver, "//div[text()='Likes']")
)
except SELENIUM_EXCEPTIONS: likes_link_el = driver.find_element_by_xpath(likes_link_xpath)
LOG.exception("Traceback of load_likes")
return driver.get(likes_link_el.get_attribute("href"))
wait_xpath(driver, all_likes_link_xpath)
all_likes_link_el = driver.find_element_by_xpath(all_likes_link_xpath)
all_likes_link = all_likes_link_el.get_attribute("href")
driver.get(all_likes_link)
def get_page_links(driver): def get_page_links(driver):
""" """
@ -39,8 +47,7 @@ def get_page_links(driver):
Returns: Returns:
List of URLs to pages List of URLs to pages
""" """
pages = driver.find_elements_by_xpath("//li//div/div/a[contains(@class, 'lfloat')]") pages = driver.find_elements_by_xpath("//header/..//a")
return [page.get_attribute("href").replace("www", "mobile") for page in pages] return [page.get_attribute("href").replace("www", "mobile") for page in pages]
def unlike_page(driver, url, archive=None): def unlike_page(driver, url, archive=None):
@ -60,7 +67,7 @@ def unlike_page(driver, url, archive=None):
print("Unliking {0}".format(url)) print("Unliking {0}".format(url))
wait = WebDriverWait(driver, 20) wait = WebDriverWait(driver, 5)
try: try:
wait.until( wait.until(
@ -70,19 +77,14 @@ def unlike_page(driver, url, archive=None):
# Something went wrong with this page, so skip it # Something went wrong with this page, so skip it
return return
button = driver.find_element_by_xpath("//*[text()='Liked']") driver.find_element_by_xpath("//*[text()='Liked']/../../../..").click()
# Click the "Liked" button to open up "Unlike"
click_button(driver, button)
wait.until( wait.until(
EC.presence_of_element_located((By.XPATH, "//*[text()='Unlike']")) EC.presence_of_element_located((By.XPATH, "//*[text()='Unlike']"))
) )
# There should be an "Unlike" button now, click it # There should be an "Unlike" button now, click it
unlike_button = driver.find_element_by_xpath("//*[text()='Unlike']/..") driver.find_element_by_xpath("//*[text()='Unlike']/..").click()
click_button(driver, unlike_button)
if archive: if archive:
archive(Page(name=url)) archive(Page(name=url))

2
deletefb/tools/wall.py

@ -45,7 +45,7 @@ def delete_posts(driver,
# Tries to be pretty resilient against DOM re-organizations # Tries to be pretty resilient against DOM re-organizations
timestamp_exp = "//article//*/header//*/div/a[contains(@href, 'story_fbid')]//text()/.." timestamp_exp = "//article//*/header//*/div/a[contains(@href, 'story_fbid')]//text()/.."
button_types = ["Delete post", "Remove Tag", "Hide from timeline"] button_types = ["Delete post", "Remove tag", "Hide from timeline", "Hide from profile"]
while True: while True:
try: try:

12
requirements.txt

@ -1,20 +1,20 @@
appdirs==1.4.3 appdirs==1.4.3
args==0.1.0 args==0.1.0
attrs==19.1.0 attrs==20.3.0
bitarray==0.9.3 bitarray==0.9.3
bleach==3.1.4 bleach==3.3.0
cattrs-3.8==0.9.1 cattrs==1.1.2
certifi==2018.11.29 certifi==2018.11.29
chardet==3.0.4 chardet==3.0.4
clint==0.5.1 clint==0.5.1
docutils==0.14 docutils==0.14
idna==2.8 idna==2.8
lxml==4.4.0 lxml==4.6.3
pendulum==2.0.5 pendulum==2.0.5
pkginfo==1.5.0.1 pkginfo==1.5.0.1
progressbar==2.5 progressbar==2.5
pybloom-live==3.0.0 pybloom-live==3.0.0
Pygments==2.4.2 Pygments==2.7.4
python-dateutil==2.8.0 python-dateutil==2.8.0
pytzdata==2019.2 pytzdata==2019.2
readme-renderer==24.0 readme-renderer==24.0
@ -28,5 +28,5 @@ tldextract==2.2.0
tqdm==4.32.2 tqdm==4.32.2
twine==1.13.0 twine==1.13.0
typing==3.7.4 typing==3.7.4
urllib3==1.25.2 urllib3==1.25.8
webencodings==0.5.1 webencodings==0.5.1

6
setup.py

@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
setuptools.setup( setuptools.setup(
name="delete-facebook-posts", name="delete-facebook-posts",
version="1.1.13", version="1.1.17",
author="Wesley Kerfoot", author="Wesley Kerfoot",
author_email="wes@wesk.tech", author_email="wes@wesk.tech",
description="A Selenium Script to Delete Facebook Posts", description="A Selenium Script to Delete Facebook Posts",
@ -24,8 +24,8 @@ setuptools.setup(
"selenium-requests", "selenium-requests",
"requests", "requests",
"pybloom-live", "pybloom-live",
"attrs", "attrs>=20.3.0",
"cattrs-3.8", "cattrs>=1.1.2",
"lxml", "lxml",
"pendulum", "pendulum",
"clint", "clint",

Loading…
Cancel
Save