如何从推特挖掘情报(1):一个流行工具的具体介绍
- 多玩几次就能很熟练了
这里不是对于该工具的第一次介绍,因为它真的很流行;比如下面:
以及其他。
是的,它就是 Twint。
我们已经讲述了一整年,所以本文将假设您对开源情报调查已经有所了解(如果您还不了解,可以参见这篇文章《开源调查应该是一种心智:得到它你需要几个步骤》;以及整个“开源情报”类目)。
这就是基本需求,没有更多了。
Twint 项目可以在这里找到。
其实有很多有关 Twint 的文章在网上,但是这些文章只是解释了如何使用它来刮取一些推文。虽然没什么问题,但是,这只是其中一个步骤而已 —— 并非全部功能。
本文将以几个完整的调查周期来展示 Twint,以帮助您理解如何最大程度地使用它,因为每个功能都值得使用。
在开始之前
做开源情报调查几乎就像在玩乐高积木一样,每个人都可以从相同的拼接开始,其结果取决于您的积木。
并没有终结者一般的超级数据库或工具,这里只有您、您的头脑和您手边的基本工具。
事实上这正是使开源情报调查极具魅力的原因。
1、基本用法
覆盖一些示例,以帮您了解如何处理 Twint。
01 用户名+标签
通过此代码段,您将获得noneprivacy
发送的包含#osint
主题标签的每条推文。
import twint c = twint.Config() c.Username = "noneprivacy" c.Search = "#osint" twint.run.Search(c)
02 关注者/关注的人
下面的代码段将刮擦用户名为 Twitter 的每个关注者,要得到每个他关注的人,只需把最下面那行换成 twint.run.Following(c)。
import twint c = twint.Config() c.Username = "twitter" twint.run.Followers(c)
请考虑到 Twitter 在阻止抓取工具方面做得很好,因此,您可能无法获得所有关注者/关注对象。在这种情况下,使用 Twitter API 来获取数据肯定没错。
请记住,在侦察阶段中,您很可能会使用其他工具(侦查工具太多了铺天盖地,这些年很多开发者都在投入对社交媒体情报的挖掘能力)。于是,如果某个工具无法返回预期的结果或无法正常工作,请使用其他适合您需求的工具 —— 工具多也是有好处滴。
2、实践
到目前为止,基本脚本已经足够,您可以在Wiki中找到其他示例。
假设这里想要获取一个名为 target 的用户的关注者,并且抓取限于拥有1000个关注者及以上的用户。
步骤是首先获得所有 followers, 然后从中提取具有1000个关注者及以上的用户。
那么就是这样的:
import twint # get the followers first c = twint.Config() c.Username = "target" c.Store_object = True c.User_full = True twint.run.Followers(c) # save them in a list target_followers = twint.output.user_object # iterate over them and save in a new list K_followers = [] for user in target_followers: if user.followers >= 1000: K_followers.append(user) # now we can save them in an CSV file, for example with open('K_followers.csv', 'w') as output: output.write('id,username,followers, following\n') for u in K_followers: output.write('{},{},{},{}\n'.format(u.id, u.username, u.followers, u.following))
这是一个非常简单的示例,它之所以如此“特别”,是因为它遵循了开源情报调查的三个基本步骤:
- 定义侦查目标,以及想要实现的目的;
- 获取需要的信息;
- 分析和过滤获得的数据。
在这一点上,应该深入研究这些关注者,例如获取其推文、并提取使用量最高的10个主题标签 —— 这些是重要的情报线索。
来看看怎么样。
3、提取前N个标签
现在继续前面的示例。在这种情况下,您已经有了一个用户列表,如果长度小于27,则可以使用一个查询。甚至可以生成各种流程来加快抓取过程。
import twint custom_query = "" hashtags = {} with open('K_followers.csv', 'r') as input: # we can ignore the first row input.readline() line = input.readline() while line: user = line.split(',')[1] hashtags.update({user: {}}) custom_query += "from:{} OR ".format(user) line = input.readline() custom_query = custom_query[:-4] c = twint.Config() c.Custom_query = custom_query c.Store_object = True c.Store_csv = True c.Output = "tweets.csv" # we want to hide the output, there will be a lot of tweets and the terminal might crash c.Hide_output = True twint.run.Search(c) tweets = twint.output.tweets_object # now we have all the tweets, let's elaborate the data # first iterate over the tweets for t in tweets: # then iterate over the hashtags of that single tweet for h in t.hashtags: # increment the count if the hashtag already exists, otherwise initialize it to 1 try: hashtags[t.username][h] += 1 except KeyError: hashtags[t.username].update({h: 1}) # now save the data with open('hashtags.csv', 'w') as output: output.write('username,hashtag,count\n') for user in hashtags: for h in hashtags[user]: output.write('{},{},{}\n'.format(user, h, hashtags[user][h]))
现在已经获取了数据并对其进行了过滤,您必须对其进行分析,以获取有关调查目标的更多信息,从而对其进行分类。
为此,可以制作一个条形图,例如,查看每个单个标签在每个单个用户中所占的百分比。
另外,您可以看到被共享次数最多的标签,以查看您的调查目标是否属于某个“社区”。
请注意:这也是喜欢玩连锅端的追踪者常用的思考方式。
4、那些社区在谈论什么?
要识别社区,仅凭标签是不够的。
您需要获取用户之间的交互;例如,谁回答谁、谁提到谁,等等。
假设发现有一些用户似乎属于同一社区,因为他们几乎使用相同的 #标签。那么现在要实现的目标就是扩大这个圈子到其他用户。
import requests import twint custom_query = "" mentioned = {} replied = {} mentions = ['mention1', 'mention2', 'mention3'] replies = ['reply1', 'reply2', 'reply3'] for m in mentions: mentioned.update({m: {}}) custom_query += "@{} OR ".format(m) custom_query = custom_query[:-3] # -3 because we want to leave a space for r in replies: replied.update({r: {}}) custom_query += "to:{} OR ".format(r) custom_query = custom_query[:-4] # Twint setup here c = twint.Config() c.Custom_query = custom_query c.Store_object = True c.Store_csv = True c.Output = "tweets_mentions_replies.csv" # we want to hide the output, there will be a lot of tweets and the terminal might crash c.Hide_output = True twint.run.Search(c) tweets = twint.output.tweets_object # now iterate over the tweets to do a bit of statistics # we will determine the most mentioned users # and the user that got the most replies for t in tweets: # iterate over the mentioned users for m in t.mentions: try: mentioned[m]['count'] += 1 except ValueError: mentioned.update({m: {'by': t.username, 'count': 1}}) # we don't have the user which we reply in the tweet object, tweet # but from the tweet ID we can get redirected to the original URL # and from the URL, extract the username _reply_to = requests.get('https://twitter.com/statuses/{}'.format(t.conversation_id)).request.path_url.split('/')[1] try: replied[_reply_to]['count'] += 1 except KeyError: replied.update({_reply_to: {'by': t.username, 'count': 1}}) print('.', end='', flush=True) # and now save to CSV for further analysis actions = {'mentioned': mentioned, 'replied': replied} for a in actions: action = actions[a] with open('{}.csv'.format(a), 'w') as output: output.write('author,{},count\n'.format(a)) for user in action: output.write('{},{},{}\n'.format(action[user]['by'], user, action[user]['count']))
现在查看 mentioned.csv 和 replied.csv,将能够看到谁提及最多,谁得到了最多的提及,对于回复也是如此。
例如,如果有两个用户既是被提及最多的人、又是提及他人最多的人,就可以推断出它们是讨论的核心。
5、一致性
数据的一致性具备所有推文都包含特定的主题标签、和/或关键字的事实。
怎么才能知道数据集是一致的?因为它是 Twitter 本身为搜索查询返回的数据,所以一切都取决于您是否为调查目标确定了正确的查询范围。
因此,再次强调,确保要寻找的是您所需要的,反之亦然。
6、准确性
最常用的词正是我们所寻找的词,做到这一事实就是准确性。
是啊,但是?
这不是一环循环。您可以提取其他最常用的词并重新运行脚本。
“最”这个字或多或少是相对的,不要误解,例如,您不需要提取使用率最高前三个词。您必须选择“具有统计意义”的字词;意思是,如果使用的三个词的出现率约为2%,那就算了吧,因为出现率太小了。
但是在执行此操作之前,您必须过滤数据集,例如 不要计算文章、名词、代词、和“太笼统”的单词。这些通常称为“stop words”。
说起来好像有点复杂,但是,稍微玩一下您就能掌握,并且可以增加很多信心。
请使用 Twitter 高级搜索进行一些测试,以了解有关用于排除或包含特定单词的运算符的更多信息。您可以在这里回顾推特运算符的使用:《从推特中挖掘真相不需要太复杂的工具:一个常用工具的全面指南》。
您还需要更改代码。强烈建议您对Python脚本编写有一定的信心。
如何建立查询
在前面的示例中,我们始终假定查询正确,即 该查询返回了所需的数据。但是,如何构建查询呢?
有些时候指定几个标签和/或关键字可能就足够了。例如,如果要想挖掘有关开源情报的推文,则搜索“ #OSINT” 就足够了。对于其他字段(如信息安全、隐私 等等)也是如此。
如果您想要挖掘经常被一个或多个主题标签标识的社区网络(并假设对此一无所知),首先,可以使用OR运算符搜索这些标签,在最高百分比中提取用户名,然后搜索提及这些用户或回复这些用户的推文。
这样一来也许很快可以确定覆盖主要角色的用户。
深入分析和研究图表的“方向性”,可以区分四种主要角色:
- 创造了新内容的人;
- 谁提到了谁;
- 谁在答复;
- 谁在转发。
您可以在具有四个级别的树形图中绘制交互关系。首先放置创建内容的用户,然后转到最后放置转发者的位置。
还可以更深入一些,但是本文的目的不是解释社交媒体/网络分析。这里的主题是调查,于是下面还需要识别交互网络,所以分析就到此为止了。
对开源情报有用的查询
幸运的是,在这种情况下,由于大多数人通常会搜索详细的信息,以便:
- 将该帐户与其他社交圈中的其他人联系起来;
- 以及,发现联系方式和其他多汁的情报。
所以无需更多解释。
1、将Twitter帐户连接到其他社交网站
可以只搜索包含完整域的推文;但由于 Twitter 有字符限制,因此也需要搜索缩短的URL。然后分析数据。
以下是完整和缩短URL的有用列表:
Facebook:
facebook.com
;fb.me
;on.fb.me
.
Youtube:
youtube.com
;youtu.be
.
Instagram:
instagram.com
;instagr.am
;instagr.com
.
Google:
google.com
;goo.gl
.
Linkedin:
linkedin.com
;lnkd.in
.
Instagram 的案例
import twint # get the followers first c = twint.Config() c.Store_object = True c.Username = "target" c.Search = "\"instagram.com\"" # "instagram.com" = the tweet must contain this word # we might add other keywords to scrape only one search # c.Search = "\"instagram.com\" \"fb.me\"", the tweet must contain at least one of two twint.run.Search(c) # let's analyze the data # I'll go straight to the content, and you'll see why tweets = twint.output.tweets_object links = [] for tweet in tweets: text = tweet.text.split(' ') for t in text: if t.startswith('instagram.com'): links.append(t) ig_users = {} for l in links: l = l.replace('instagram.com/', '') # clean up the data l = l.split('?')[0] # remove tracking codes, like ?igshid=1x89qxivizphf slashes = len(l.split('/')) user = l.split('/')[0] try: ig_users[user] += 1 except KeyError: ig_users[user] = 1 # now let's get some rank and let's see the top 5 users import operator i = 0 sorted_users = dict(sorted(ig_users.items(), key=operator.itemgetter(1), reverse=True)) for s_user in sorted_users: if i == 4: break print('User: {} | Rank: {}'.format(s_user, sorted_users[s_user])) i += 1
现在,如果您看一下输出,您会发现p /的计数很高。事实证明 p / 不是一个用户,而是代表指向用户特定帖子的 link。
就此可以推断出,如果 Twitter 用户分享了包含 instagram.com/p/ 的链接,那么他/她正在共享的是一个帖子。否则他/她分享的是一个 Instagram 用户。
基本上,假设您要搜索分享了 Instagram 帖子的 Twitter 用户,则需要在查询上做一些优化,比如这样 c.Search = "\"instagram.com/p/\""
.
随之而来的是,如果您要搜索分享了一个特定链接的帖子的用户,那么就是这样 c.Search = "\"instagram.com/p/postID\""
.
同样,如果您要搜索分享了链接到特定 Instagram 帐户的用户,就是这样 c.Search = "\"instagram.com/accountName\""
.
共享ID几乎是唯一的,因此,如果两个用户共享了具有相同ID的 Instagram 帖子的链接,则它们之间可能会发生强烈的交互。或者他们甚至可能是同一个人的两个分身。
从简单的信息(如域名)开始获取所需的更多信息,获取数据,进行过滤和分析。在对其进行分析时,第一个一般范围信息分为两类:用户和帖子。
知道了这一点现在就可以进行更多丰富的查询,从而请求更详细的数据。
此方法在 Facebook 上会非常有效。下面是一些示例。
YouTube 示例
Youtube 不像 Instagram,当用户将其 Twitter 帐户连接到 Youtube 时,就会泄漏更多信息。
您可以搜索有关点赞的视频、甚至对某个目标视频发表评论的用户的推文,或者仅仅是分享该视频的推文。
在进入脚本之前,需要先搜索您真正想要的内容。因此,在第一阶段,需要了解以下模式:
- 点赞了视频的推文;
- 分享了视频的推文;
- 评论了视频的推文。
1、点赞的视频
用这个来查找: "youtu.be" "youtube.com" "Liked on YouTube"
.
你会第一眼看到 Liked on YouTube,这里有视频的标题。
此信息非常有用,因为如果由于某种原因删除了该视频,您仍然可以知道用户点赞了的视频,因此可以在其他平台中搜索该视频。
您也可以应用反向搜索,例如,如果要搜索点赞了特定视频且使用了缩短网址的用户,那么就这样 https://youtu.be/wcLiJHz3JRc” “Liked on YouTube。
2、分享的视频
如果仅搜索缩短网址,例如这样 “https://youtu.be/wcLiJHz3JRc”;你会看到一种特定的模式,其中包含一些随机文本,缩短的URL,然后是 “via @YouTube”。
由于文本几乎是随机的,因此我们将搜索静态部分。所以查询是 "youtu.be" "youtube.com" via @YouTube
.
反向查询是 https://youtu.be/3z9sq9e5iu4 via @YouTube
.
如果您不知道视频的网址,但是有几个关键字可以让您猜测视频的名称,那么,只需将它们放在查询中即可,但请注意,这与搜索 "keyword1 keyword2"
和keyword1 keyword2
完全不同。
多玩一会就能对布尔搜索充满信心了。
3、评论过的视频
via @youtube "Check out this comment"
.
您能看到这个搜索与发布的评论有关,而不是视频。我希望将其重定向到视频,但我尝试的链接返回了404。运气不好。
Facebook 示例
无论如何,直接搜索和反向搜索均有效。
userID/posts/postID
;username/posts/postID
;story.php?story_fbid=postID&id=userID
;permalink.php?story_fbid=postID&id=userID
;events/eventID
。
也许还有其他一些,但即使是大多数情况下,仅凭这些也就足够了。
如果不知道 postID,那么可以拆分该网址,然后搜索,这样:"facebook.com/story.php" "&id=userID"
.
Google 和 Linkedin
Google 和 Linkedin 通常会在网址末尾附加一个类似哈希的内容,因此这里也没有模式。但是仍然可以对链接进行直接和反向搜索。
很多时候都可以使用为 Instagram 编写的相同代码,而无需进行任何更改。
发现联系方式
在这种情况下,您几乎可以使用所需的每种组合,以下有几种:
"@gmail.com"
,和/或其他电子邮件服务;[at]gmail[dot]com
,和/或其他电子邮件服务;"contact us"
;"hotline" "contact"
,人们有时会提起 Whatsapp 或其他IM服务的联系人;- 先前示例的其他各种组合。
Keybase 允许您验证自己拥有的 Twitter 帐户,通过 Keybase 给您发送的定制推文进行验证。也许它并不流行,但是它可能有用。
因为在 Keybase 上,您可以验证其他社交网络上其他帐户的所有权,并添加一个PGP密钥,其中可能包含电子邮件地址。
要提取电子邮件地址,您可以使用此代码段;link to the gist
import requests, base64, re, sys r = requests.get("https://keybase.io/" + sys.argv[1] + "/key.asc") body = r.text.split("\n\n") key = body[1].split("-----") for email in re.findall(r' <(.*?)>', str(base64.b64decode(key[0]))): print(email)
活动分析
确定一个用户(或更多用户)的每日和每周活动。
给定一个时区,您可以通过查看目标对象的活跃时间来估算目标所在的时区。
为了简化此操作,您可以使用 Excel 或 Kibana。在第一种情况下,您将需要从 Time 字段中“提取”小时,并从 date 字段中“提取”星期几。
建议您在必须处理大量推文时使用 Kibana。
最后
就是这样!这是是挖掘推特情报的第一部分内容。后面将介绍其他角度的玩法。
如果您有更好的方法,欢迎留言探讨,也许我们可以更新它。玩得开心。⚪️