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 3import 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 3images = 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.
SignalEleven said on fosstodon.org:
@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...).
More comments on Mastodon.