使用Python、Pillow、Tweepy开发的推特机器人表情无法显示问题
Pillow绘制阿拉伯语文本时表情符号无法正常显示(推特机器人场景)
问题背景
开发推特机器人,功能是抓取提及自身的推文并将内容绘制到图片上,技术栈为Python、Pillow、Tweepy,同时使用librqam处理阿拉伯语。目前遇到的问题:抓取到的推文文本中的表情符号无法在生成的图片中正常显示,已尝试NotoColor系列字体但未解决。
代码示例
import tweepy from PIL import Image,ImageDraw , ImageFont import requests from io import BytesIO CONSUMER_KEY = "" CONSUMER_SECRET = '' ACCESS_KEY = '' ACCESS_SECRET = '' auth = tweepy.OAuthHandler(CONSUMER_KEY,CONSUMER_SECRET) auth.set_access_token(ACCESS_KEY,ACCESS_SECRET) api = tweepy.API(auth) mentions = api.mentions_timeline() print(mentions) for mention in mentions: tweet_id = api.get_status(mention.in_reply_to_status_id).id status_obj = api.get_status(tweet_id ,tweet_mode="extended") print(status_obj.full_text) save_id_inreplyto = status_obj.in_reply_to_status_id save_id_text = status_obj.full_text orginal_url = api.get_status(tweet_id).user.profile_image_url replaced_url = orginal_url.replace("normal", "400x400") response = requests.get(replaced_url) avatar = Image.open(BytesIO(response.content)) resized_av = avatar.resize((200,200)) tweet = save_id_text img = Image.new('RGB', (1080, 1080), color=(178, 255, 233)) # 裁剪头像为圆形 width, height = resized_av.size x = (width - height) // 2 img_cropped = resized_av.crop((x, 0, x + height, height)) mask = Image.new('L', img_cropped.size) mask_draw = ImageDraw.Draw(mask) width, height = img_cropped.size mask_draw.ellipse((0, 0, width, height), fill=255) img_cropped.putalpha(mask) # 粘贴头像和图标 twittericon = Image.open('twittericon.png') img.paste(img_cropped,(300, 100),mask) resized_ti = twittericon.resize((100, 100)) img.paste(resized_ti, (300, 200)) d = ImageDraw.Draw(img) fnt = ImageFont.truetype('C:\\Users\\kmm\\Desktop\\fonts\\NotoSansArabic-VariableFont_wdth,wght.ttf', 30) d.text((280, 150), "@ 😃"+api.get_status(mention.in_reply_to_status_id).user.screen_name, font=fnt, fill=(220, 180, 10)) d.text((540,540),tweet, font=fnt, fill=(220, 180, 10),anchor="ms") img.save('pil1_text'+str(mention.id)+'.jpg' ,format='JPEG', subsampling=0, quality=100) print("tweeted with media")
问题现象
原推文中的表情符号在生成的图片中无法正常显示(表现为空白或方块),阿拉伯语文本排版正常,但emoji缺失。
解决方案
核心原因
- 阿拉伯语专用字体不包含emoji字符集,无法渲染表情符号
- Pillow默认不支持自动字体 fallback,需手动区分文本类型并调用对应字体
- 旧版Pillow不支持彩色emoji渲染,需升级到9.1.0+版本
步骤1:安装依赖库
pip install --upgrade pillow emoji librqam
步骤2:下载支持彩色emoji的字体
获取Noto Color Emoji字体(可从Google Fonts官方下载),保存到本地路径。
步骤3:修改代码实现双字体渲染
添加文本拆分与多字体绘制逻辑,同时确保阿拉伯语RTL排版正确:
import tweepy from PIL import Image, ImageDraw, ImageFont import requests from io import BytesIO import emoji from librqam import arabic_shape CONSUMER_KEY = "" CONSUMER_SECRET = '' ACCESS_KEY = '' ACCESS_SECRET = '' auth = tweepy.OAuthHandler(CONSUMER_KEY, CONSUMER_SECRET) auth.set_access_token(ACCESS_KEY, ACCESS_SECRET) api = tweepy.API(auth) def split_text_by_type(text): """拆分文本为阿拉伯语片段和emoji片段""" parts = [] current_arabic = [] for char in text: if emoji.is_emoji(char): if current_arabic: # 处理阿拉伯语RTL排版 processed_arabic = arabic_shape(''.join(current_arabic)) parts.append(('arabic', processed_arabic)) current_arabic = [] parts.append(('emoji', char)) else: current_arabic.append(char) if current_arabic: processed_arabic = arabic_shape(''.join(current_arabic)) parts.append(('arabic', processed_arabic)) return parts def draw_mixed_text(draw, pos, text, arabic_font, emoji_font, fill, anchor="ms"): """绘制混合阿拉伯语和emoji的文本""" x, y = pos for part_type, content in split_text_by_type(text): if part_type == 'arabic': # 阿拉伯语从右向左排版,调整x坐标 text_width = draw.textlength(content, font=arabic_font) draw.text((x, y), content, font=arabic_font, fill=fill, anchor=anchor) x -= text_width else: # emoji从左向右排版,调整x坐标 text_width = draw.textlength(content, font=emoji_font) draw.text((x, y), content, font=emoji_font, fill=fill, anchor=anchor) x += text_width mentions = api.mentions_timeline() print(mentions) for mention in mentions: tweet_id = api.get_status(mention.in_reply_to_status_id).id status_obj = api.get_status(tweet_id, tweet_mode="extended") save_id_text = status_obj.full_text orginal_url = api.get_status(tweet_id).user.profile_image_url replaced_url = orginal_url.replace("normal", "400x400") response = requests.get(replaced_url) avatar = Image.open(BytesIO(response.content)) resized_av = avatar.resize((200,200)) tweet = save_id_text img = Image.new('RGBA', (1080, 1080), color=(178, 255, 233, 255)) # 裁剪头像为圆形 width, height = resized_av.size x = (width - height) // 2 img_cropped = resized_av.crop((x, 0, x + height, height)) mask = Image.new('L', img_cropped.size) mask_draw = ImageDraw.Draw(mask) width, height = img_cropped.size mask_draw.ellipse((0, 0, width, height), fill=255) img_cropped.putalpha(mask) # 粘贴头像和图标 twittericon = Image.open('twittericon.png') img.paste(img_cropped,(300, 100), mask) resized_ti = twittericon.resize((100, 100)) img.paste(resized_ti, (300, 200)) d = ImageDraw.Draw(img) # 加载两种字体 arabic_fnt = ImageFont.truetype('C:\\Users\\kmm\\Desktop\\fonts\\NotoSansArabic-VariableFont_wdth,wght.ttf', 30) emoji_fnt = ImageFont.truetype('C:\\Users\\kmm\\Desktop\\fonts\\NotoColorEmoji.ttf', 30) # 替换为你的emoji字体路径 # 绘制用户名行 username_text = "@ 😃" + api.get_status(mention.in_reply_to_status_id).user.screen_name draw_mixed_text(d, (280, 150), username_text, arabic_fnt, emoji_fnt, fill=(220, 180, 10), anchor="lt") # 绘制推文内容 draw_mixed_text(d, (540, 540), tweet, arabic_fnt, emoji_fnt, fill=(220, 180, 10), anchor="ms") # 保存为PNG以支持彩色emoji的透明通道 img.save('pil1_text'+str(mention.id)+'.png' ,format='PNG', quality=100) print("tweeted with media")
关键修改点
- 新增
split_text_by_type函数,区分阿拉伯语和emoji片段,并用arabic_shape处理阿拉伯语RTL排版 - 新增
draw_mixed_text函数,根据片段类型调用对应字体绘制,调整坐标适配RTL/LTR排版 - 加载阿拉伯语字体和emoji字体两种字体
- 将图片格式从JPEG改为PNG,避免彩色emoji的透明通道丢失
- 升级Pillow到支持彩色emoji的版本
内容的提问来源于stack exchange,提问作者eng.Khaled
相关产品推荐
相关产品推荐

