You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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缺失。

解决方案

核心原因

  1. 阿拉伯语专用字体不包含emoji字符集,无法渲染表情符号
  2. Pillow默认不支持自动字体 fallback,需手动区分文本类型并调用对应字体
  3. 旧版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")

关键修改点

  1. 新增split_text_by_type函数,区分阿拉伯语和emoji片段,并用arabic_shape处理阿拉伯语RTL排版
  2. 新增draw_mixed_text函数,根据片段类型调用对应字体绘制,调整坐标适配RTL/LTR排版
  3. 加载阿拉伯语字体和emoji字体两种字体
  4. 将图片格式从JPEG改为PNG,避免彩色emoji的透明通道丢失
  5. 升级Pillow到支持彩色emoji的版本

内容的提问来源于stack exchange,提问作者eng.Khaled

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.22 15:45:26