Python JSON Decoder Error - 修复 Twitch TV 频道上线广播脚本

101 阅读2分钟

一位用户在使用一个 Python 脚本来广播 Twitch TV 频道上线状态时遇到问题。该脚本在 Twitch TV 频道下线时工作正常,但在频道上线时却会抛出如下错误:

2013-12-08 22:42:12+0000 [-] exceptions.ValueError: No JSON object could be decoded

2、解决方案

经过分析,问题的原因是脚本尝试解析一个无效的 JSON 响应。为了解决这个问题,需要在调用 json() 方法之前检查响应的内容是否有效。

具体地,可以在脚本中添加以下代码来打印响应的内容:

response = requests.get("http://api.justin.tv/api/stream/list.json?channel=" + channel)
print(response.text)

这样,就可以看到响应的内容,并确定它是否是一个有效的 JSON 对象。

如果响应的内容不是一个有效的 JSON 对象,则需要进一步检查请求的 URL 和其他参数,以确保它们是正确的。

另外,还可以尝试使用其他 JSON 解析库,例如 simplejsonujson,看看是否能解决问题。

代码例子

以下是在脚本中添加了打印响应内容的代码后的完整代码:

import requests
import yaml
from HTMLParser import HTMLParser
from sys import stdout

from twisted.internet import reactor, task, protocol
from twisted.python import log
from twisted.words.protocols import irc
from twisted.application import internet, service

with open('config.yml') as f:
    config = yaml.load(f.read())
HOST, PORT = config['host'], config['port']


def munge(inp):
    # Prevents highlight notification in most clients
    return inp[0] + u"\u200b" + inp[1:]


class TwitchProtocol(irc.IRCClient):
    password = config["password"] if "password" in config else None
    nickname = 'Twitch'
    username = 'Twitch'
    versionName = 'Twitch'
    versionNum = 'v1.0'
    realname = 'by blha303. https://gist.github.com/blha303'
    loopcall = None
    status = False


    def signedOn(self):
        for channel in self.factory.channels:
            self.join(channel)
        #Quakenet
        self._send_message("auth %s %s" % (self.nickname, config["nspass"]), "Q")
        #Nickserv
        self._send_message("identify %s %s" % (self.nickname, config["nspass"]), "NickServ")
        #Nickserv that doesn't support specifying a nickname
        self._send_message("identify %s" % config["nspass"], "NickServ")

        def restartloop(reason):
            reason.printTraceback()
            print "Loop crashed: " + reason.getErrorMessage()
            self.loopcall.start(5.0).addErrback(restartloop)
        self.loopcall = task.LoopingCall(self.getTwitchStatus)
        self.loopcall.start(5.0).addErrback(restartloop)

    def getTwitchStatus(self):
        channel = config["twitch"]
        response = requests.get("http://api.justin.tv/api/stream/list.json?channel=" + channel)
        print(response.text)
        data = response.json()
        if data and not self.status:
            fmt = "{}: {} {} ({})"
            self.status = True
            title = data[0]['title']
            playing = ("playing " + data[0]['meta_game']) if "meta_game" in data[0] else ""
            viewers = "\x033\x02Online now!\x02\x0f " + str(data[0]["channel_count"]) + " viewer"
            print viewers
            viewers = viewers + "s" if not " 1 view" in viewers else viewers
            print viewers
            h = HTMLParser()
            for ch in self.factory.channels:
                self._send_message(h.unescape(fmt.format(title, munge(channel), playing, viewers)), ch)
        elif not data and self.status:
            self.status = False
            for ch in self.factory.channels:
                self._send_message("%s is now offline." % channel, ch)


    def privmsg(self, user, channel, message):
        nick, _, host = user.partition('!')
        print "<%s> %s" % (nick, message)


    def _send_message(self, msg, target, nick=None):
        if nick:
            msg = '%s, %s' % (nick, msg)
        self.msg(target, msg)
        print "<%s> %s" % (self.nickname, msg)


class TwitchFactory(protocol.ReconnectingClientFactory):
    protocol = TwitchProtocol
    channels = config["channels"]

if __name__ == '__main__':
    reactor.connectTCP(HOST, PORT, TwitchFactory())
    log.startLogging(stdout)
    reactor.run()

elif __name__ == '__builtin__':
    application = service.Application('Twitch')
    ircService = internet.TCPClient(HOST, PORT, TwitchFactory())
    ircService.setServiceParent(application)