六度空间的应用找出两个陌生人之间的关系.docx
《六度空间的应用找出两个陌生人之间的关系.docx》由会员分享,可在线阅读,更多相关《六度空间的应用找出两个陌生人之间的关系.docx(17页珍藏版)》请在冰豆网上搜索。
![六度空间的应用找出两个陌生人之间的关系.docx](https://file1.bdocx.com/fileroot1/2022-11/23/89f7e96a-86a8-42cc-a58c-22feef329374/89f7e96a-86a8-42cc-a58c-22feef3293741.gif)
六度空间的应用找出两个陌生人之间的关系
"六度空间"的应用——找出两个陌生人之间的关系
(一)
前几日在人人网上看到有位北京大学的做了一个"人人网六度空间"的Flash,觉得很好玩,遂向其请教一二,自己也做了一个,这篇就来做个梳理和总结吧,哪些性能方面不好的希望大家能够指出并改进.本篇没有完整的代码或程序可以下载,更没有我获取到的数据可以下载,数据也很大,我用XML存储了我们学校整个人际关系用了几百兆!
切勿用文章内的思路做盗取他人隐私违法犯罪的商业应用!
"六度空间"可行性分析
六度空间”理论又称作六度分隔(SixDegreesofSeparation)理论。
这个理论可以通俗地阐述为:
“你和任何一个陌生人之间所间隔的人不会超过六个,也就是说,最多通过六个人你就能够认识任何一个陌生人。
”该理论产生于20世纪60年代,由美国心理学家米尔格伦提出。
输入一个人A,找到另一个人B,通过在线联网的方式(本地不保存数据)找到A和B之间的关系那当然好,但在人人网没有提供一个能快速找到任意一个人的好友的API情况下做到这点几乎是不可能的(时间方面).我做了一个测试,如果一个人有1000个好友,在120kbps的网速下找到他的所有好友大概需要8s钟!
可想而知,你想找出A和B之间的关系,采用双向搜索自然来得快不少,先找A所有的好友,再找B的好友,看看有没有交集...没有继续找A所有的好友的好友,找B所有的好友的好友,看看有没有交集...到这一步需要的时间地球大概已经转了半圈了,我昨晚找了C的好友,再找C的好友的所有好友,从早上1:
58找到早上6:
01...所以我说这么多意思是没有快速API的情况下做成在线版几乎不可能,除非你本地保存了数据,说着A和B是直接好友关系.
那么就下载所有人人网用户的信息?
人人网有近2亿账号,如果每个只需要5秒的话, 对于我这个带宽120kbps,硬盘250GB的电脑显然也不可能...那么就从小范围做起吧,就找出我们学校所有人之间的关系吧.
得到学校里每个人,以及每个人的好友
假设学校里的每个人都注册了人人网(对于那些没有注册人人网的俺也没法找到他们,呵呵),我偶然想到了一个特殊的人人网用户A,这个用户是学校一个聊天论坛在人人网里的一个账号,好友数量已经达到2000,我有如下假设:
任何一个学校里的人B,一定有一个他认识的人认识A,按照人人网里的话说叫做"A和B有共同好友",这个假设根据我个人经验认为是正确的,如果B不满足这个条件,这个人一定孤陋寡闻,不参加社交-_-!
!
所以找到学校里每一个人,可以以这个A为起点,找到A的所有好友集合S1,再找到S的所有好友,那么整个学校的人的名单90%以上已经到手.在昨天那个月黑风高的夜晚,我整整用了4个小时把学校所有人都下载下来了(大概15W条记录).
存储人和人之间的关系
光下载到到人的名单还不行,我并不想以A为中心建立大家之间的关系,因为A毕竟是一个组织,不是一个人,以它为中心的关系几乎都是浮云,现在已经有A的所有好友,以及A的好友的好友集合S2{sa,sb,sc...},学校的所有人都包含在S2中了,任何人之间的关系可以用图表示,在人人网中且是无向图(没有A是B的好友,B不是A的好友这种情况),所以还得再逆向的找出S2中的每个人的好友,并存储这个关系!
图可以用矩阵法和邻接表表示,因为这个关系图属于稀疏矩阵,应该用邻接表表示更省空间(用矩阵表示也是几乎不可能的事情,因为如果有学校有10000个人,那么就得建立一个10000*10000的矩阵,当然可以利用矩阵的一些运算比如找出矩阵特殊值等方法压缩这个矩阵,但压缩之后的矩阵仍然不小...).因此我建立了如下一个XML格式的文件存储人和人之间的关系.
1
2
3
4
5
6
7
8
9
10
11
12
xml version="1.0"?
>
........
........
这个把全校所有人都爬虫出来的代码是这样的,先找出A的所有好友:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public void downLoadData()
{
XmlDocumentdoc=new XmlDocument();
doc.Load(@"C:
\Users\ChenHua\Desktop\Relation.xml");
renrenoperationrenren=new renrenoperation();
Console.WriteLine("LoginSuccess");
//获得A的好友
var peopleList=renren.Get_FriendList("A的id号码");
foreach (Personpersonin peopleList)
{
var node=doc.CreateElement("Person");
node.SetAttribute("id",person.id);
node.SetAttribute("name",person.name);
node.SetAttribute("school",person.school);
node.SetAttribute("faceUri",person.faceUri);
doc.DocumentElement.AppendChild(node);
var list2=renren.Get_FriendList(person.id);
Console.WriteLine(person.name+"WriteSuccess.");
//获得A的每一个好友的好友
foreach (Personperson2in list2)
{
if (person2.school.Contains("某某学校")||person2.school.Contains("南京市"))
{
var friend=doc.CreateElement("Friend");
friend.SetAttribute("id",person2.id);
friend.SetAttribute("name",person2.name);
friend.SetAttribute("school",person2.school);
friend.SetAttribute("faceUri",person2.faceUri);
node.AppendChild(friend);
}
}
}
}
接下来就是把A的好友的好友提取出来,找出A的好友的好友的好友...(逻辑有点那个...不知道大家有没有想通)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
static void Main(string[]args)
{
XmlDocumentdoc=new XmlDocument();
doc.Load(@"C:
\Users\ChenHua\Desktop\Relation.xml");
Console.WriteLine("XML加载完毕.");
var PersonList=doc.SelectNodes("//Person");//所有的Person
foreach (XmlNodeEveryPersonNodein PersonList) //Person中所有的节点
{
foreach (XmlNodeEveryPersonChildNodein EveryPersonNode.ChildNodes)//每个Person的子节点,朋友
{
if (ContainedThisPerson(EveryPersonChildNode,PersonList)) //如果发现这个朋友已经在Person集合中
{}
else //如果不存在
{
var NewPersonNode=doc.CreateElement("Person");//新建一个节点
NewPersonNode.SetAttribute("id",EveryPersonChildNode.Attributes["id"].InnerText);//设置id,名字,学校,头像等
NewPersonNode.SetAttribute("name",EveryPersonChildNode.Attributes["name"].InnerText);
NewPersonNode.SetAttribute("school",EveryPersonChildNode.Attributes["school"].InnerText);
NewPersonNode.SetAttribute("faceUri",EveryPersonChildNode.Attributes["faceUri"].InnerText);
{//开始加载这个节点有哪些朋友
foreach (XmlNodeAnotherPersonListin PersonList)
foreach (XmlNodeAnotherPersonListChildNodein AnotherPersonList.ChildNodes)
if (AnotherPersonListChildNode.Attributes["id"].InnerText==NewPersonNode.Attributes["id"].InnerText)
{
var newFriendNode=doc.CreateElement("Friend");
newFriendNode.SetAttribute("id",AnotherPersonListChildNode.ParentNode.Attributes["id"].InnerText);
newFriendNode.SetAttribute("name",AnotherPersonListChildNode.ParentNode.Attributes["name"].InnerText);
newFriendNode.SetAttribute("school",AnotherPersonListChildNode.ParentNode.Attributes["school"].InnerText);
newFriendNode.SetAttribute("faceUri",AnotherPersonListChildNode.ParentNode.Attributes["faceUri"].InnerText);
NewPersonNode.AppendChild(newFriendNode);
break;
}
}
doc.DocumentElement.AppendChild(NewPersonNode);
Console.WriteLine("AddNewNode" +NewPersonNode.Attributes["name"].InnerText);
}
}
}
doc.Save(@"C:
\Users\ChenHua\Desktop\Relation.xml");
}
private static bool ContainedThisPerson(XmlNodenode,XmlNodeListnodeList)
{
foreach (XmlNodenodeInListin nodeList)
{
if (node.Attributes["id"].InnerText==nodeInList.Attributes["id"].InnerText)
return true;
}
return false;
}
获得一个人的好友列表
上面说了那么多,怎么获得好友列表呢?
1.分析登陆协议,看登陆时POST了哪些信息,写一个登陆方法
2.去
获得第2步每一页的字符串,用正则表达式提取出好友的信息,正则表达式我写好了:
@"//www\.renren\.com/profile\.do\?
id=([0-9]*)"">//href=""[^\s]*"">(.*)[\s]+[\s]?
.*[\s]?
.*[\s]?
(.*)"
下图是一个获取好友列表的测试图片:
先写这么多,程序还在哗啦啦的运行中,下篇继续...争取多一些程序优化方面的.
"六度空间"的应用——找出两个陌生人之间的关系
(二)
终于"完工"
哎,熬了好几个夜,掉了好多根头发,终于接近完工,如果真的要拿给别人用还需要修补很多东西.先发几张程序运行的图片吧:
)第一张是找出两人关系,我试了很多人,几乎都只需要通过一个人就能找到另一个人,第二张是寻找XML文件中某个人有哪些好友.
存储内容的改进
在写上篇博客的时候程序一直在运行(在保存人和人之间的完整对应关系),我估算还需要好几个小时才能停下来,所存储的XML文件也会大的惊人,估算达到1GB以上,其实运算之前这个时候的XML中已经包含了所有的人,只不过暂时是一种类似"有向图"(A是B的好友,B不是A的好友)的关系,现在运算的也只是把有向图的关系完全拓展开,变成无向图(A是B的好友,B也是A的好友而已).在发现完整关系的情况下,存储所需要的空间如此之大时,我放弃了这种方法.在下载完所有人的头像照片(4万多张)后,我把XML中每个节点的school和faceUri删除掉了,这样存储人与人的关系只用了15.8MB.
现在的存储结构如下:
1
2
3
4
5
6
7
8
9
10
11
12
xml version="1.0"?
>
........
........
从这个XML中通过一些XML的操作方法可以就可以得到很多信息了.
RelationOperate类
1.每次定义一个RelationOperate类,需要传进一个XML文件地址,以对它进行操作
1
2
3
4
private XmlDocumentxmlDoc=new XmlDocument();
public RelationOperate(string xmlDocPath)
{
xmlDoc.Load(xmlDocPath);
}
2.通过一个id找到他的好友列表
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
///
///获取一个id的好友
///
///要查找的id
///该id的好友id列表
public ListgetFriend(string id)
{
ListfriendList=new List();
var PersonList=xmlDoc.SelectNodes("//Person");//所有的Person节点
foreach (XmlNodexmlNodein PersonList)
//若果Person节点已经包含了该id的用户
if (xmlNode.Attributes["id"].InnerText==id)
{
//则增加这个Person节点的每一个子节点到friendList中
//因为他的每一个子节点都是他的好友
foreach (XmlNodenodein xmlNode.ChildNodes)
friendList.Add(node.Attributes["id"].InnerText);
return friendList;
}
//上面的条件不满足时,就查找哪些Person的子节点(朋友)里边包含这个id
foreach (XmlNodexmlNodein PersonList)
foreach (XmlNodenodein xmlNode.ChildNodes)
{
if (node.Attributes["id"].InnerText==id)
{
friendList.Add(xmlNode.Attributes["id"].InnerText);
break;
}
}
return friendList;
}
3.获取一个id的姓名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public string getName(string id)
{
var PersonList=xmlDoc.SelectNodes("//Person");//所有的Person节点
foreach (XmlNodexmlNodein PersonList)
//若果Person节点已经包含了该id的用户
if (xmlNode.Attributes["id"].InnerText==id)
{
return xmlNode.Attributes["name"].InnerText;
}
//上面的条件不满足时,就查找哪些Person的子节点(朋友)里边包含这个id
string name="";
foreach (XmlNodexmlNodein PersonList)
foreach (XmlNodenodein x