从Tweepy Streaming API输出中过滤垃圾邮件

时间:2018-10-05 07:51:43

标签: python streaming tweepy

我有以下脚本收集所有包含搜索词 $ 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)

问题是输出中有很多垃圾邮件。例如:

enter image description here

理想情况下,我想过滤掉垃圾邮件。因此,我检查了此类推文的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'

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

  • tweet ['friend_count'] <50
  • tweet ['followers_count']> 50
  • tweet ['entities'] ['urls'] ['display_url']包含指向特定垃圾邮件网站的链接

结果,我在脚本中编写了一个新函数: 导入系统     导入时间     导入json     将熊猫作为pd导入     从tweepy导入OAuthHandler     从tweepy导入流     从tweepy.streaming导入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

1 个答案:

答案 0 :(得分:0)

作为注释,您在代码中先写了tweet['followers_count'] > 50data['user']['followers_count'] < 50

过滤垃圾邮件是一项艰巨的工作,我想甚至Twitter也无法解决。 我认为这不是关于关注者或朋友人数的问题。 即使您写了一个黑名单的骗局和垃圾邮件站点,它也每天都会改变。 也许您也可以将用户列入黑名单。

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

编辑:垃圾邮件标准的另一个主意:每天发布的推文数量。您提供的示例用户每天发布近42条推文。因此测试可以为if tweetsPerDay > 10: spam。 (我得到的计数数字包括现在创建的用户帐户和现在的日期,将其转换为天数,再加上总的推文计数,然后计算平均值)。但这可能不是很准确,例如检查关注者和朋友的人数。