Replacing Twitter Embeds With Images
I logged into Twitter using a fresh account last week. No followers, no preferences set. The default experience was an unending slurry of racism and porn. I don't care to use Twitter any more. Whatever good that was there is now drowned in a cess-pit of violent filth.
I still have a lot of Tweets embedded on this blog. Using WordPress, it was easy to paste in a link and have it converted to an embed. But I don't want to direct people to a dangerous site.
So here's a somewhat automated way to replace embedded Tweets with screenshots.
Demo

Use the Embed Platform
Take the ID of the Tweet you want to convert. Add it on to the end of an embed URl like this - https://platform.twitter.com/embed/Tweet.html?dnt=true&embedId=twitter-widget-0&frame=false&hideCard=false&hideThread=true&lang=en&theme=light&width=550px&id=1092852483033055232
Let's make that a bit more readable:
https://platform.twitter.com/embed/Tweet.html?
hideCard=false
&hideThread=true
&lang=en
&theme=light
&width=550px
&id=1092852483033055232
You can change whether to show a card (the attached image or link), show the preceding message in the thread or not, what UI language to show, dark or light mode, and how wide you want the embed to be.
Use Selenium to automate the screenshot
Using Python, we can use Selenium's Chrome Webdriver to open pages, find elements, and take screenshots:
Python 3
import time import io from PIL import Image from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.common.keys import Keys from selenium.webdriver.common.by import By # Chrome's headless options chrome_options = Options() chrome_options.add_argument('--headless=new') chrome_options.add_argument('--window-size=1920,1080') # Turn off everything chrome_options.add_argument("--disable-gpu") chrome_options.add_argument("--no-sandbox") chrome_options.add_argument("--disable-dev-shm-usage") chrome_options.add_argument("--disable-extensions") chrome_options.add_argument("--disable-infobars") chrome_options.add_argument("--disable-logging") chrome_options.add_argument("--log-level=3") # Start Chrome driver = webdriver.Chrome(options=chrome_options) # Open the page driver.get("https://platform.twitter.com/embed/Tweet.html?dnt=true&embedId=twitter-widget-0&frame=false&hideCard=false&hideThread=true&lang=en&theme=light&width=550px&id=1092852483033055232") # Twitter is slow! time.sleep(5) # Get the Tweet tweet = driver.find_element(By.TAG_NAME, "article") # Use the parent element for more padding tweet = driver.execute_script("return arguments[0].parentNode;", tweet) # Save as an image print("Save") image_binary = tweet.screenshot_as_png img = Image.open(io.BytesIO(image_binary)) img.save("tweet.png")
Get the alt text
Accessibility is important. Getting the text of the Tweet is as simple as:
Python 3
# Get the alt text alt = tweet.text
But that retrieves all the text - including things like "Copy link to post" and "Read more on X" - because Twitter doesn't believe in semantic HTML. There's also no way to easily get the number of likes and retweets - which might be useful information.
If there are images in the post, it's useful to get their alt text. This is simpler:
Python 3
images = tweet.find_elements(By.TAG_NAME, "img") print("\nAlt text of images:") for img in images: alt_text = img.get_attribute("alt") if alt_text: print(alt_text)
Use The API
There is a better way to get all the text, alt text, and metadata. Use the hidden syndication API! But that's a blog post for another time…
Get the Code
Grab the code from GitHub - and if it is useful to you, please star the repo or leave a friendly comment.
Of course, if we can use the API to get the pure data, perhaps it is possible to make some lovely semantic HTML rather than an image...? 😉
Chris R says:
Confused! In the black box, is the following string (taken from the URI above) missing?
"dnt=true&embedId=twitter-widget-0&frame=false"
Unsure if that bit's accidentally or deliberately missing!
@edent says:
You can use it with or without. I've given the minimum viable example.
@Edent this is amazing. I would love to have it in discourse. It used to save the text and images via oembed (I think) but of course that route is long gone. I will look into it while on vacation maybe.
I wonder if it's against any TOS (I wouldn't care too much but my association might...).
Reply to original comment on fosstodon.org
|More comments on Mastodon.