7月15日,《釜山行》的续集《釜山行2:半岛》在首尔正式上映,不少朋友都对它充满期待。
四年前,第一部《釜山行》上映,丧尸沦陷中,釜山是希望。四年后,整个半岛全部沦陷,正式成为丧尸半岛。
据小伙伴观后感说,这部《釜山行2》,惊喜其实并不多。倒不至于有多烂,只不过,相比让人惊喜的第一部,作为续集的《釜山行2》,投资更大、场面更大,但是,影片本身带给观众的冲击却少了很多。
今天我们就用Python来回顾一下四年前的《釜山行》吧!
今天的项目将使用python编写代码实现对《釜山行》文本的人物关系提取,最终利用Gephi软件对提取的人物关系绘制人物关系图。
实体间的共现是一种基于统计的信息提取。关系紧密的人物往往会在文本中多段内同时出现,可以通过识别文本中已确定的实体(人名),计算不同实体共同出现的次数和比率。
当比率大于某一阈值,我们认为两个实体间存在某种联系。这种联系可以具体细化,但提取过程也更加复杂。因此在此课程只介绍最基础的共现网络。
1.开发环境
剧本
http://7xktmz.com1.z0.glb.clouddn.com/Train%20to%20Busan.txt
字典
http://labfile.oss.aliyuncs.com/courses/677/dict.txt
gephi
Python2+jieba库
2.实验过程
开始编写我们的代码。
import os, sys
import jieba, codecs, math
import jieba.posseg as pseg
names = {}
relationships = {}
lineNames = []
字典类型names保存人物,该字典的键为人物名称,值为该人物在全文中出现的次数。
字典类型relationships保存人物关系的有向边,该字典的键为有向边的起点,值为一个字典edge,edge的键是有向边的终点,值是有向边的权值,代表两个人物之间联系的紧密程度。
lineNames是一个缓存变量,保存对每一段分词得到当前段中出现的人物名称,lineNames[i]是一个列表,列表中存储第i段中出现过的人物。
jieba.load_userdict("dict.txt")
with codecs.open("to_train.txt", "r", "utf8") as f:
for line in f.readlines():
poss = pseg.cut(line)
lineNames.append([])
for w in poss:
if w.flag != "nr" or len(w.word) < 2:
continue
lineNames[-1].append(w.word)
if names.get(w.word) is None:
names[w.word] = 0
relationships[w.word] = {}
names[w.word] += 1
在具体实现过程中,读入剧本的每一行,对其做分词。
提取该行中出现的人物集存入lineNames中。之后对出现的人物,更新他们在names中的出现次数。
for line in lineNames:
for name1 in line:
for name2 in line:
if name1 == name2:
continue
if relationships[name1].get(name2) is None:
relationships[name1][name2]= 1
else:
relationships[name1][name2] = relationships[name1][name2]+ 1
对于lineNames中每一行,我们为该行中出现的所有人物两两相连。
如果两个人物之间尚未有边建立,则将新建的边权值设为1,否则将已存在的边的权值加1。
这种方法将产生很多的冗余边,这些冗余边将在最后处理。
with codecs.open("node.txt", "w", "gbk") as f:
f.write("Id Label Weight\r\n")
for name, times in names.items():
f.write(name + " " + name + " " + str(times) + "\r\n")
with codecs.open("edge.txt", "w", "gbk") as f:
f.write("Source Target Weight\r\n")
for name, edges in relationships.items():
for v, w in edges.items():
if w > 3:
f.write(name + " " + v + " " + str(w) + "\r\n")
将已经建好的names和relationships输出到文本,以方便gephi可视化处理。输出边的过程中可以过滤可能是冗余的边,这里假设共同出现次数少于3次的是冗余边,则在输出时跳过这样的边。
完整的代码如下:
运行得到节点集合node.txt,边集合edge.txt。
下面使用gephi这个软件来将人物关系可视化。
启动gephi,分别选择节点表格和边表格导入上面代码中生成的两个文件,分隔符选择空格,编码选择GB2312。
可以在最上方的数据资料选项卡中查看图中所有的边和节点,对于分词不准确导致的噪音可以手动删除。
分别点击右侧统计栏中平均度和模块化运行计算。模块化运算时Resolution值填写0.5。
点击左上角外观中节点第一个选项卡,选择数值设定,选择Modularity Class,点击应用。
点击左上角外观中节点第二个选项卡,选择数值设定,选择连入度,最小尺寸填10,最大尺寸填40,点击应用。
选择左下角布局中的Force Atlas,斥力强度填写20000.0,吸引强度填写 1.0。点击运行,稍后点击停止。
点染色根据模块化计算结果不定,但染色效果大致相同。
点击最上方的预览按钮,选中左侧节点标签中显示标签选项,并选择一种字体。
点击刷新按钮,右侧显示最终的人物关系图。为了优化显示的效果,还可以调整左边的参数。
以上就是釜山行人物关系的图谱,是不是很简单?
说起丧尸,我就想起僵尸。今天再教你一招,用Python揪出你微信里的僵尸粉!
你一定也遇到过,突然想联系一位很久没有联系的朋友,发现对方早就已经把你删除了,而你还一无所知(好尴尬呀)。
这就是所谓的僵尸粉,他们默默地躺在你微信联系人中,你傻傻的以为对方还是好朋友,而其实,对方早就把你从好友列表中删除了,现在我们来揪出他们吧!
1、准备工作
在开始编写脚本之前,需要做好以下准备工作:
一部 Root 后的 Android 手机或者模拟器,如果没有 Root 的设备,可以使用网易 MuMu 模拟器;
Android 的开发环境、Android Studio;
sqlcipher 图形化工具;
自动化工具:Python 虚拟环境下安装 pocoui。
整个操作分为 3 个步骤,分别是破解微信数据库,筛选出通信录中的好友、模拟给好友转账得到僵尸粉的数据、同时删除好友中所有的僵尸粉。
第 1 步,我们需要破解微信 App 的数据库。
首先,我们使用 Android Studio 新建一个项目,在项目初始化的时候,授予应用管理员权限,以及修改微信目录的读写权限功能。
接下来,我们来获取一下微信数据库的密码。
数据库的密码是由设备的 imei 和微信的 uid 通过 md5 算法生成得到的。
下面,我们就可以使用 SQLCipher 依赖库来对微信数据库进行查询,我们需要为项目添加以下依赖,方便我们操作数据库。
//我们需要对项目增加依赖
implementation 'net.zetetic:android-database-sqlcipher:3.5.4@aar'
通过上面得到的密码能够打开加密数据库,然后查询rcontact表获取微信通讯录内所有好友的微信号、微信名称以及用户名等一些数据。
这里需要注意,数据库中 rcontact 表的数据相对是比较杂乱的,除了正常的好友数据之外,微信的黑名单好友、已经删除的好友、关注的公众号、加入的微信群等一些其他的数据也包含在里面,需要我们通过 type 和 verifyFlag 字段进行筛选。
为了进行 Python 接下来的操作,我们要将查询到的好友数据写入到 csv 文件中来。
/***
* 写入数据到csv中
* @param output_path
* @param contacts
*/
public static void writeCsvFile(String output_path, List<Contact> contacts)
{
try
{
File file = new File(output_path);
//删除之前保存的文件
if (file.exists())
{
file.delete();
}
BufferedWriter bw = new BufferedWriter(new FileWriter(file, true));
// 添加头部名称
bw.write("userName" + "," + "alias" + "," + "nickName");
bw.newLine();
for (int i = 0; i < contacts.size(); i++)
{
bw.write(contacts.get(i).getUserName() + "," + contacts.get(i).getAlias() + "," + contacts.get(i).getNickName());
bw.newLine();
}
bw.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
第 2 步,模拟给好友转账,从而判断这个好友是否是「僵尸粉」。
首先,我们需要初始化 Airtest,接下来利用 adb 把第 1 步生成的数据从手机里导出到本地中去。
def __init_airtest(self):
"""
初始化Airtest
:return:
"""
device_1 = Android('822QEDTL225T7')
# device_1 = Android('emulator-5554')
connect_device("android:///")
self.poco = AndroidUiautomationPoco(device_1, screenshot_each_action=False)
auto_setup(__file__)
def export_wx_db_from_phone(target_path):
"""
从手机中导出通信录数据
:param target_path:
:return:
"""
# 微信通信录数据
wx_db_source_path = "/data/data/com.xingag.crack_wx/wx_data.csv"
# 导出到本地
os.popen('adb pull %s %s' % (wx_db_source_path, target_path))
接下来就是一系列的自动化操作。
打开微信App,遍历所有的好友列表,获取到每一个好友的微信号去搜索对应的好友,然后跳转到与好友的聊天界面中。
def __to_friend_chat_page(self, weixin_id):
"""
点击到一个好友的聊天界面
:param weixin_id:
:param weixin_name:
:return:
"""
# 1、点击搜索
element_search = self.__wait_for_element_exists(self.id_search)
element_search.click()
print('点击搜索')
# 2、搜索框
element_search_input = self.__wait_for_element_exists(self.id_search_input)
element_search_input.set_text(weixin_id)
# 3、搜索列表
element_search_result_list = self.__wait_for_element_exists(self.id_search_result_list)
# 3.1 是否存在对应的联系人,如果存在就在第一个子View布局下
# 注意:可能出现最常用的聊天列表,这里需要进行判断
index_tips = 0
for index, element_search_result in enumerate(element_search_result_list.children()):
# 联系人的Tips
# if element_search_result_list.children()[0].offspring(self.id_contact_tips).exists():
if element_search_result.offspring(text=self.text_contact_tips).exists():
index_tips = index
break
# 4、点击第一个联系人进入聊天界面
element_search_result_list.children()[index_tips + 1].click()
下面我们尝试着给对方转账,如果好友关系是正常的,就会跳出一个支付页面让你输入密码。
def __judge_is_friend(self, weixin_id, weixin_name):
"""
判断是不是微信好友
:param weixin_id: 微信号
:return:
"""
# 尝试给好友转账,设置一个小额度,以防止刷脸直接支付了
# 如果对方是你的好友,接下来会让你输入密码,关掉页面就行了
# 如果对方不是你的好友,会提示不是你的好友,不能继续操作了
# 5、点击好友界面的+按钮
self.poco(self.id_chat_more_button).click()
# 6、点击转账按钮
self.poco(self.id_chat_more_container).offspring(text=self.text_chat_transfer_account_text).click()
# 7、输入金额
self.poco(self.id_transfer_account_input).set_text(self.money)
# 8、点击转账按钮
self.poco(self.id_transfer_account_container).offspring(text=self.text_chat_transfer_account_text).click()
如果是「僵尸粉」,就会弹出一个警告提示的对话框,提示你不是收款方的微信好友,没法完成转账的操作。如下图:
通过警告对话框是否存在,就可以判断出好友的关系是否正常。非正常的好友关系,包含:僵尸粉、对方账号异常等一些其他的情况。
# 10.弹出警告对话框# 弹出好友关系不正常
if element_transfer_account_result_button:
# 提示内容
ransfer_account_result_tips = self.poco(self.id_transfer_account_result_tips).get_text()
if self.text_friend_no_tips in transfer_account_result_tips:
print('注意!%s已经把你拉黑了!!!' % weixin_name)
self.friend_black_list.append({
'id': weixin_id,
'nickName': weixin_name
})
write_to_file(self.path_black_list, 'id:%s,nickName:%s' % (weixin_id, weixin_name))
elif self.text_friend_limit_tips in transfer_account_result_tips:
print('%s账号收到限制!!!' % weixin_name)
write_to_file(self.path_account_limit, 'id:%s,nickName:%s' % (weixin_id, weixin_name))
elif self.text_friend_is_norm in transfer_account_result_tips:
print('%s好友关系不正常!!!' % weixin_name)
write_to_file(self.path_relationship_unnormal, 'id:%s,nickName:%s' % (weixin_id, weixin_name))
# 点击确认按钮
element_transfer_account_result_button.click()
# 返回到主页面
self.__back_to_home()
else:
# 包含正常好友关系和对方账号限制的情况
print('好友关系正常')
self.__back_to_home()
最后,点击手机的返回键,一直到微信主界面为止。
def __back_to_home(self):
"""
回退到主界面
:return:
"""
print('准备回退到主界面')
home_tips = ['微信', '通讯录', '发现', '我']
while True:
keyevent('BACK')
is_home = False
# 判断是否到达首页
if self.poco(text=home_tips[0]).exists() and self.poco(text=home_tips[1]).exists() and self.poco(
text=home_tips[2]).exists() and self.poco(text=home_tips[3]).exists():
is_home = True
if is_home:
print('已经回到微信首页~')
break
重复操作上面的步骤,就可以判断出哪些是「僵尸粉」,哪些好友的账号被微信限制,哪些是正常的好友了。
第3步,删除获取到的「僵尸粉」。
拿到上面的僵尸粉数据之后,就可以利用上面的方式进行一系列自动化操作,将这些「僵尸粉」删除掉了。
def del_friend_black(self, weixin_id):
"""
删除黑名单好友
:return:
"""
# 到好友聊天界面
self.__to_friend_chat_page(weixin_id)
# 点击聊天界面右上角,进入到好友的详细信息界面
self.poco(self.id_person_msg_button).click()
# 点击好友头像
self.poco(self.id_person_head_url).click()
# 点击个人名片的右上角,弹出好友操作菜单
self.poco(self.id_person_manage_menu).click()
# 查找删除操作栏
# 注意:对于目前主流的手机,都需要滑动到最底部才能出现【删除】这一操作栏
self.poco.swipe([0.5, 0.9], [0.5, 0.3], duration=0.2)
# 点击删除,弹出删除对话框
self.poco(self.id_person_del, text=self.text_person_del).click()
# 确定删除好友【确定删除】
# 界面会直接回到主界面
self.poco(self.id_person_del_sure, text=self.text_person_del).click()
这样一系列操作之后,「僵尸粉」就被你完美的清理掉了。
是不是很神奇,不错,代码就是这样神奇,想知道Python还有哪些神操作吗?那就关注小编,慢慢听小编一一道来吧!