如何通过tweepy将流式推文保存为json?
我学习Python已经几个月了,主要是通过网上课程,现在想通过一个真实的小项目来进一步提高自己的技能。
在这个项目中,我想从Twitter的实时数据接口收集推文,并把它们存储为json格式(当然,你也可以选择只保存一些关键信息,比如状态文本和状态ID,但有人建议我最好是保存所有数据,然后再进行处理)。不过,当我加入了on_data()这个功能后,代码就不再工作了。有人能帮帮我吗?我也很欢迎关于如何最好地存储和处理推文的建议!我的最终目标是能够根据一些人口统计变量(比如国家、用户年龄等)来跟踪推文,以及分析特定品牌(比如苹果、HTC、三星)的情感倾向。
此外,我还想尝试根据位置和关键词来过滤推文。我已经从如何在tweepy模块中添加位置过滤器的内容中调整了代码。不过,当关键词数量不多时,它能正常工作,但当关键词增多时,它就停止了。我猜我的代码效率不高。有没有更好的方法呢?
### code to save tweets in json###
import sys
import tweepy
import json
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)
file = open('today.txt', 'a')
class CustomStreamListener(tweepy.StreamListener):
def on_status(self, status):
print status.text
def on_data(self, data):
json_data = json.loads(data)
file.write(str(json_data))
def on_error(self, status_code):
print >> sys.stderr, 'Encountered error with status code:', status_code
return True # Don't kill the stream
def on_timeout(self):
print >> sys.stderr, 'Timeout...'
return True # Don't kill the stream
sapi = tweepy.streaming.Stream(auth, CustomStreamListener())
sapi.filter(track=['twitter'])
3 个回答
我直接把原始的JSON数据放进数据库里。虽然这样做看起来有点丑,也有点像是变通的办法,但确实能用。有一个明显的问题就是,推文的创建日期是以字符串的形式存储的。如何比较存储在MongoDB中的Twitter数据的日期? 这篇文章提供了一种解决方法(我在代码里插入了注释,说明了在哪里可以进行这个操作)
# ...
client = pymongo.MongoClient()
db = client.twitter_db
twitter_collection = db.tweets
# ...
class CustomStreamListener(tweepy.StreamListener):
# ...
def on_status(self, status):
try:
twitter_json = status._json
# TODO: Transform created_at to Date objects before insertion
tweet_id = twitter_collection.insert(twitter_json)
except:
# Catch any unicode errors while printing to console
# and just ignore them to avoid breaking application.
pass
# ...
stream = tweepy.Stream(auth, CustomStreamListener(), timeout=None, compression=True)
stream.sample()
我找到了一种方法,可以把推文保存到一个json文件里。很高兴听到大家有什么改进的建议!
# initialize blank list to contain tweets
tweets = []
# file name that you want to open is the second argument
save_file = open('9may.json', 'a')
class CustomStreamListener(tweepy.StreamListener):
def __init__(self, api):
self.api = api
super(tweepy.StreamListener, self).__init__()
self.save_file = tweets
def on_data(self, tweet):
self.save_file.append(json.loads(tweet))
print tweet
save_file.write(str(tweet))
在重新阅读你的原始问题时,我发现你问了很多小问题。我会尽量在这里回答大部分,但有些问题可能需要你在StackOverflow上单独提问。
- 为什么加上
on_data
后会出错?
没有看到具体的错误信息,很难判断原因。其实在我重新生成了我的消费者/访问密钥之前,这个功能也没能正常工作,所以你可以试试这个方法。
我可能会用一些不同的方法来处理这个问题。
tweets
是一个全局列表。这意味着如果你有多个StreamListeners
(比如在多个线程中),那么任何一个流监听器收集到的每一条推文都会被添加到这个列表里。这是因为在Python中,列表是指向内存中某个位置的——如果这让你感到困惑,下面是一个简单的例子:
>>> bar = []
>>> foo = bar
>>> foo.append(7)
>>> print bar
[7]
注意,虽然你以为把7添加到了foo
中,但实际上foo
和bar
指向的是同一个东西(所以改变一个会改变两个)。
如果你本来就是想这样做,那这个方法挺不错的。不过,如果你想把不同监听器的推文分开,那可能会很麻烦。我个人会这样构建我的类:
class CustomStreamListener(tweepy.StreamListener):
def __init__(self, api):
self.api = api
super(tweepy.StreamListener, self).__init__()
self.list_of_tweets = []
这样做会让推文列表只在你的类的范围内。还有,我觉得把属性名从self.save_file
改成self.list_of_tweets
是合适的,因为你还把文件命名为save_file
。虽然这不会严格导致错误,但对我来说,self.save_file
是一个列表,而save_file
是一个文件,这样的命名会让人困惑。这样做有助于未来的你和其他阅读你代码的人理解每个部分的作用。关于变量命名的更多信息。
在我的评论中,我提到过不应该使用file
作为变量名。file
是Python的一个内置函数,用于创建一个新的file
类型的对象。你可以技术上覆盖它,但这样做是非常糟糕的主意。想了解更多内置函数,可以查看Python文档。
- 如何根据多个关键词过滤结果?
在这种类型的搜索中,所有关键词是用OR
连接的,来源:
sapi.filter(track=['twitter', 'python', 'tweepy'])
这意味着会获取包含'twitter'、'python'或'tweepy'的推文。如果你想要所有关键词的交集(AND
),你需要在后处理时检查推文是否包含你想搜索的所有关键词。
- 如何根据位置和关键词过滤结果?
我刚刚意识到你确实把这个作为一个独立的问题提出来了,我正准备建议这一点。使用正则表达式后处理是一个不错的解决方案。你也可以尝试像这样同时根据位置和关键词进行过滤:
sapi.filter(locations=[103.60998,1.25752,104.03295,1.44973], track=['twitter'])
- 存储/处理推文的最佳方法是什么?
这取决于你要收集多少推文。我个人喜欢使用数据库,尤其是当你打算对大量推文进行情感分析时。当你收集数据时,应该只收集你需要的内容。这意味着在你的on_data
方法中保存结果到数据库或其他地方时,应该从JSON中提取重要部分,而不是保存整个推文的JSON数据,因为那样只会占用不必要的空间。