首页 > 解决方案 > 从 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'

我确定了以下标准来将推文分类为垃圾邮件:

结果,我在脚本中写了一个新函数: 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

标签: pythonstreamingtweepy

解决方案


作为评论,您在代码中编写了tweet['followers_count'] > 50then data['user']['followers_count'] < 50

过滤垃圾邮件是一项艰巨的工作,我想即使是 Twitter 也无法解决它。我不认为这是关于追随者或朋友人数的问题。即使你写了一份诈骗和垃圾邮件网站的黑名单,它也会每天都在变化。也许您还可以制作用户黑名单。

据我说,if测试非常快(我有一些国际象棋编程经验)。所以,是的,您可以在流式传输时进行检查。但是如果你的测试会增长,那么你可以直接推送推文而无需任何过程,例如在 Redis 中。然后另一个脚本(工作人员)可以完成从 redis 获取推文并进行垃圾邮件测试的工作。

编辑:垃圾邮件标准的另一个想法:每天发布的推文数量。您作为示例提供的用户每天发布近 42 条推文。所以可以进行测试if tweetsPerDay > 10: spam。(我用创建的用户帐户和现在的日期得到这个计数,转换为天,以及总推文计数,然后计算平均值)。但它可能不是很准确,比如检查关注者和朋友的数量。


推荐阅读