Building a Social Media Cross-Poster Bot with Web Scraping
Posting the same content across Twitter/X, LinkedIn, Bluesky, and Mastodon manually is tedious. Let us build an automated cross-poster that adapts content for each platform.
Architecture
- Content source: Your blog RSS feed, a Google Sheet, or manual input
- Adapter layer: Reformats content per platform (character limits, hashtags, mentions)
- Poster layer: API clients for each platform
- Scheduler: Optimal posting times per platform
Setup
pip install requests beautifulsoup4 feedparser tweepy atproto schedule
Content Source: RSS Feed Scraper
# Implementation is proprietary (that IS the moat).
# Skip the build — use our ready-made Apify actor:
# see the CTA below for the link (fpr=yw6md3).
Platform Adapters
class ContentAdapter:
LIMITS = {
"twitter": 280,
"bluesky": 300,
"linkedin": 3000,
"mastodon": 500
}
def adapt(self, post, platform):
limit = self.LIMITS.get(platform, 280)
if platform == "twitter":
return self.format_twitter(post, limit)
elif platform == "bluesky":
return self.format_bluesky(post, limit)
elif platform == "linkedin":
return self.format_linkedin(post, limit)
elif platform == "mastodon":
return self.format_mastodon(post, limit)
def format_twitter(self, post, limit):
hashtags = " ".join(f"#{t}" for t in post["tags"][:3])
url = post["url"]
available = limit - len(url) - len(hashtags) - 4
text = post["summary"][:available]
return f"{text}\n\n{url}\n{hashtags}"
def format_bluesky(self, post, limit):
text = post["summary"][:limit - len(post["url"]) - 2]
return f"{text}\n{post['url']}"
def format_linkedin(self, post, limit):
hashtags = " ".join(f"#{t}" for t in post["tags"][:5])
return f"{post['title']}\n\n{post['summary']}\n\n{post['url']}\n\n{hashtags}"
def format_mastodon(self, post, limit):
hashtags = " ".join(f"#{t}" for t in post["tags"][:4])
text = post["summary"][:limit - len(post["url"]) - len(hashtags) - 4]
return f"{text}\n\n{post['url']}\n{hashtags}"
Platform Posters
# Implementation is proprietary (that IS the moat).
# Skip the build — use our ready-made Apify actor:
# see the CTA below for the link (fpr=yw6md3).
The Cross-Poster Bot
import time
import schedule
class CrossPoster:
def __init__(self):
self.source = ContentSource()
self.adapter = ContentAdapter()
self.platforms = {}
self.posted = set()
def add_platform(self, name, poster):
self.platforms[name] = poster
def cross_post(self, post):
post_id = post["url"] or post["title"]
if post_id in self.posted:
return
results = {}
for platform_name, poster in self.platforms.items():
try:
content = self.adapter.adapt(post, platform_name)
url = poster.post(content)
results[platform_name] = {"status": "success", "url": url}
print(f"Posted to {platform_name}: {url}")
time.sleep(5) # Stagger posts
except Exception as e:
results[platform_name] = {"status": "error", "error": str(e)}
print(f"Failed on {platform_name}: {e}")
self.posted.add(post_id)
return results
def auto_post_from_rss(self, feed_url):
posts = self.source.from_rss(feed_url, limit=1)
for post in posts:
self.cross_post(post)
# Setup
bot = CrossPoster()
bot.add_platform("bluesky", BlueskyPoster("you.bsky.social", "password"))
bot.add_platform("mastodon", MastodonPoster("https://mastodon.social", "token"))
# Post manually
bot.cross_post({
"title": "New blog post",
"url": "https://yourblog.com/post-1",
"summary": "Just published a guide on building cross-posting bots with Python!",
"tags": ["python", "automation", "socialmedia"]
})
# Or auto-post from RSS every hour
schedule.every(1).hours.do(bot.auto_post_from_rss, "https://yourblog.com/feed")
Scraping Optimal Posting Times
def scrape_best_times():
"""Research suggests these general windows work well."""
return {
"twitter": ["9:00", "12:00", "17:00"],
"linkedin": ["7:30", "12:00", "17:30"],
"bluesky": ["10:00", "14:00", "19:00"],
"mastodon": ["8:00", "13:00", "18:00"]
}
Proxy Services for Scraping
When scraping social media for content research:
- ScraperAPI — handles JS rendering for Twitter and LinkedIn
- ThorData — residential proxies for social media access
- ScrapeOps — monitor your scraping pipeline
Conclusion
A cross-poster bot saves hours of manual work each week. Start with two platforms, perfect the formatting, then expand. Always respect platform rate limits and avoid spammy behavior.
Follow for more Python automation projects!
Top comments (0)