python - 从 Tweepy Streaming API 输出中过滤垃圾推文
问题描述
我有以下脚本收集包含搜索词$BTC或$ETH的所有推文
import sys
import time
import json
import pandas as pd
from tweepy import OAuthHandler
from tweepy import Stream
from tweepy.streaming import StreamListener
USER_KEY = ''
USER_SECRET = ''
ACCESS_TOKEN = ''
ACCESS_SECRET = ''
class StdOutListener(StreamListener):
def on_data(self, data):
tweet = json.loads(data)
print(tweet)
def on_error(self, status):
print(status)
return False
if __name__ == "__main__":
listener = StdOutListener()
auth = OAuthHandler(USER_KEY, USER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)
stream = Stream(auth, l)
stream.filter(languages=['en'], track=['$BTC', '$ETH'], async=True)
问题是输出中有很多垃圾邮件。例如:
理想情况下,我想过滤掉垃圾邮件。所以我检查了这类推文的 JSON 输出。
{
'created_at': 'Fri Oct 05 07:09:19 +0000 2018',
'id': 1048107851829452800,
'id_str': '1048107851829452800',
'text': 'Current $ARK price: $0.69 \n\nWe checked! Binance registration is
currently open \n\n➡️ url_that_is_forbidden_by_stack_overflow…
url_that_is_forbidden_by_stack_overflow',
'display_text_range': [
0,
140
],
'source': '<a href="http://www.google.com"
rel="nofollow">medicinetoletitwin</a>',
'truncated': True,
'in_reply_to_status_id': None,
'in_reply_to_status_id_str': None,
'in_reply_to_user_id': None,
'in_reply_to_user_id_str': None,
'in_reply_to_screen_name': None,
'user': {
'id': 937395812748820480,
'id_str': '937395812748820480',
'name': 'Brenna ',
'screen_name': 'BrennaPham',
'location': 'San Jose, CA',
'url': url_that_is_forbidden_by_stack_overflow,
'description': 'Gamer ~',
'translator_type': 'none',
'protected': False,
'verified': False,
'followers_count': 168,
'friends_count': 22,
'listed_count': 2,
'favourites_count': 17,
'statuses_count': 12620,
'created_at': 'Sun Dec 03 18:59:12 +0000 2017',
'utc_offset': None,
'time_zone': None,
'geo_enabled': False,
'lang': 'en',
'contributors_enabled': False,
'is_translator': False,
'profile_background_color': 'F5F8FA',
'profile_background_image_url': '',
'profile_background_image_url_https': '',
'profile_background_tile': False,
'profile_link_color': '1DA1F2',
'profile_sidebar_border_color': 'C0DEED',
'profile_sidebar_fill_color': 'DDEEF6',
'profile_text_color': '333333',
'profile_use_background_image': True,
'profile_image_url':
'http://pbs.twimg.com/profile_images/939260582460506112/oltTD-
f1_normal.jpg',
'profile_image_url_https':
'https://pbs.twimg.com/profile_images/939260582460506112/oltTD-
f1_normal.jpg',
'default_profile': True,
'default_profile_image': False,
'following': None,
'follow_request_sent': None,
'notifications': None
},
'geo': None,
'coordinates': None,
'place': None,
'contributors': None,
'is_quote_status': False,
'extended_tweet': {
'full_text': 'Current $ARK price: $0.69 \n\nWe checked! Binance
registration is currently open \n\n➡️
url_that_is_forbidden_by_stack_overflow\n\n$EOS $BCAP $RMC $TIME $RCN $XVC $PBL $BTC $WGR
$FLIXX $BTG $BCD $SMT $UKG $XWC $XEM $CRB $PASC $KMD $VIU $BNB $YOYOW
$INFX url_that_is_forbidden_by_stack_overflow',
'display_text_range': [
0,
236
],
'entities': {
'hashtags': [],
'urls': [
{
'url': 'url_that_is_forbidden_by_stack_overflow',
'expanded_url': 'http://binance.com/?ref=10078236',
'display_url': 'binance.com/?ref=10078236',
'indices': [
89,
112
]
}
],
'user_mentions': [],
'symbols': [
{
'text': 'ARK',
'indices': [
8,
12
]
},
{
'text': 'EOS',
'indices': [
114,
118
]
},
{
'text': 'BCAP',
'indices': [
119,
124
]
},
{
'text': 'RMC',
'indices': [
125,
129
]
},
{
'text': 'TIME',
'indices': [
130,
135
]
},
{
'text': 'RCN',
'indices': [
136,
140
]
},
{
'text': 'XVC',
'indices': [
141,
145
]
},
{
'text': 'PBL',
'indices': [
146,
150
]
},
{
'text': 'BTC',
'indices': [
151,
155
]
},
{
'text': 'WGR',
'indices': [
156,
160
]
},
{
'text': 'FLIXX',
'indices': [
161,
167
]
},
{
'text': 'BTG',
'indices': [
168,
172
]
},
{
'text': 'BCD',
'indices': [
173,
177
]
},
{
'text': 'SMT',
'indices': [
178,
182
]
},
{
'text': 'UKG',
'indices': [
183,
187
]
},
{
'text': 'XWC',
'indices': [
188,
192
]
},
{
'text': 'XEM',
'indices': [
193,
197
]
},
{
'text': 'CRB',
'indices': [
198,
202
]
},
{
'text': 'PASC',
'indices': [
203,
208
]
},
{
'text': 'KMD',
'indices': [
209,
213
]
},
{
'text': 'VIU',
'indices': [
214,
218
]
},
{
'text': 'BNB',
'indices': [
219,
223
]
},
{
'text': 'YOYOW',
'indices': [
224,
230
]
},
{
'text': 'INFX',
'indices': [
231,
236
]
}
],
'media': [
{
'id': 1048107850399137792,
'id_str': '1048107850399137792',
'indices': [
237,
260
],
'media_url': 'http://pbs.twimg.com/media/DougiW1WwAA_8PZ.jpg',
'media_url_https': 'https://pbs.twimg.com/media/DougiW1WwAA_8PZ.jpg',
'url': 'url_that_is_forbidden_by_stack_overflow',
'display_url': 'pic.twitter.com/2Y1evrvz3x',
'expanded_url': 'https://twitter.com/BrennaPham/status/1048107851829452800/photo/1',
'type': 'photo',
'sizes': {
'thumb': {
'w': 150,
'h': 150,
'resize': 'crop'
},
'large': {
'w': 1920,
'h': 1080,
'resize': 'fit'
},
'medium': {
'w': 1200,
'h': 675,
'resize': 'fit'
},
'small': {
'w': 680,
'h': 383,
'resize': 'fit'
}
}
}
]
},
'extended_entities': {
'media': [
{
'id': 1048107850399137792,
'id_str': '1048107850399137792',
'indices': [
237,
260
],
'media_url': 'http://pbs.twimg.com/media/DougiW1WwAA_8PZ.jpg',
'media_url_https': 'https://pbs.twimg.com/media/DougiW1WwAA_8PZ.jpg',
'url': url_that_is_forbidden_by_stack_overflow,
'display_url': 'pic.twitter.com/2Y1evrvz3x',
'expanded_url': 'https://twitter.com/BrennaPham/status/1048107851829452800/photo/1',
'type': 'photo',
'sizes': {
'thumb': {
'w': 150,
'h': 150,
'resize': 'crop'
},
'large': {
'w': 1920,
'h': 1080,
'resize': 'fit'
},
'medium': {
'w': 1200,
'h': 675,
'resize': 'fit'
},
'small': {
'w': 680,
'h': 383,
'resize': 'fit'
}
}
}
]
}
},
'quote_count': 0,
'reply_count': 0,
'retweet_count': 0,
'favorite_count': 0,
'entities': {
'hashtags': [],
'urls': [
{
'url': 'url_that_is_forbidden_by_stack_overflow',
'expanded_url': 'http://binance.com/?ref=10078236',
'display_url': 'binance.com/?ref=10078236',
'indices': [
89,
112
]
},
{
'url': 'url_that_is_forbidden_by_stack_overflow',
'expanded_url': 'https://twitter.com/i/web/status/1048107851829452800',
'display_url': 'twitter.com/i/web/status/1…',
'indices': [
114,
137
]
}
],
'user_mentions': [],
'symbols': [
{
'text': 'ARK',
'indices': [
8,
12
]
}
]
},
'favorited': False,
'retweeted': False,
'possibly_sensitive': False,
'filter_level': 'low',
'lang': 'en',
'timestamp_ms': '1538723359435'
我确定了以下标准来将推文分类为垃圾邮件:
- 推文['friend_count'] < 50
- 推文['followers_count'] > 50
- tweet['entities']['urls']['display_url'] 包含指向特定垃圾邮件网站的链接
结果,我在脚本中写了一个新函数: import sys import time import json import pandas as pd from tweepy import OAuthHandler from tweepy import Stream from tweepy.streaming import StreamListener
USER_KEY = ''
USER_SECRET = ''
ACCESS_TOKEN = ''
ACCESS_SECRET = ''
spam = ['spam_website_1', spam_website_2', 'spam_website_3']
class StdOutListener(StreamListener):
def on_data(self, data):
tweet = json.loads(data)
self.filter_tweet(tweet)
def filter_tweet(self, tweet):
url = data['entities']['urls']['display_url'] if 'display_url' in
data['entities']['urls'] else None
if len(url) != 0):
if data['user']['friends_count'] < 50:
if data['user']['followers_count'] < 50:
if any(x in url for x in spam):
pass
else:
print(tweet)
def on_error(self, status):
print(status)
return False
if __name__ == "__main__":
listener = StdOutListener()
auth = OAuthHandler(USER_KEY, USER_SECRET)
auth.set_access_token(ACCESS_TOKEN, ACCESS_SECRET)
stream = Stream(auth, l)
stream.filter(languages=['en'], track=['$BTC', '$ETH'], async=True)
我想知道这部分对于 Streaming API 输出是否足够快或者应该重写。有没有可能写得更优雅、更快?
if len(url) != 0):
if data['user']['friends_count'] < 50:
if data['user']['followers_count'] < 50:
if any(x in url for x in spam):
pass
解决方案
作为评论,您在代码中编写了tweet['followers_count'] > 50
then data['user']['followers_count'] < 50
。
过滤垃圾邮件是一项艰巨的工作,我想即使是 Twitter 也无法解决它。我不认为这是关于追随者或朋友人数的问题。即使你写了一份诈骗和垃圾邮件网站的黑名单,它也会每天都在变化。也许您还可以制作用户黑名单。
据我说,if
测试非常快(我有一些国际象棋编程经验)。所以,是的,您可以在流式传输时进行检查。但是如果你的测试会增长,那么你可以直接推送推文而无需任何过程,例如在 Redis 中。然后另一个脚本(工作人员)可以完成从 redis 获取推文并进行垃圾邮件测试的工作。
编辑:垃圾邮件标准的另一个想法:每天发布的推文数量。您作为示例提供的用户每天发布近 42 条推文。所以可以进行测试if tweetsPerDay > 10: spam
。(我用创建的用户帐户和现在的日期得到这个计数,转换为天,以及总推文计数,然后计算平均值)。但它可能不是很准确,比如检查关注者和朋友的数量。
推荐阅读
- node.js - 无法使用 npm 安装 zerorpc
- javascript - React 中的表单问题:受控到不受控
- c# - Azure Blob 存储帐户返回错误 (403) 已禁止
- javascript - 使用 vanilla javascript 在表格中显示带有图像的嵌套 json
- selenium - 通过 Selenium 显式等待
- laravel-5 - 如何在laravel中获取日期之间的所有记录?
- json - 如何从存储在表中的嵌套 JSON 对象返回多行
- android - 在 android canvas 中使用 RectF 创建一行 3 个圆(分段)
- java - 具有子模块时未部署主 pom.xml
- c++ - Is there a way in C++ to determine if a string is "interned" or in a read only persistent location?