宋小宝通过哪几个人可以找到詹妮弗劳伦斯——利用Web Scraping测试“六度分隔假说”
来源:互联网 发布:阿里云售后自助服务 编辑:程序博客网 时间:2024/04/29 08:48
六度分隔假说:你和任何一个陌生人之间所间隔的人不会超过六个
这条假说意味着大多数人有认识地球上任何一个人的可能性。然而在现实中,即使两个陌生人之间存在着某种联系,但信息的不对称性往往使得这些联系极难被挖掘。于此不同,网络上储存了公共人物大量公开的信息数据,利用这些数据,可以很轻松地找到两个名人之间的人物关系链。
那么我们的问题是:
1.宋小宝同学通过哪几个人可以找到大表姐詹妮弗劳伦斯?
2.这些人又是怎么认识的?
本文将利用Python Web Scraping解决这两个问题。
Web Scraping介绍
Web Scraping(网络爬虫),是一种按照一定的规则,自动地抓取网络信息的程序或者脚本。简单地说,Web Scraping就是从网站抽取信息, 利用程序来模拟人浏览网页的过程,发送http请求,从http响应中获得结果。
Python提供了便利的Web Scraping基础,有很多第三方库。加上其语言的简便性,Web Scraping目前常常与Python绑定在一起。
本文所涉及的库有:
Requests:http://www.python-requests.org/en/master/
BeautifulSoup:http://www.crummy.com/software/BeautifulSoup/
- Pymysql:https://github.com/PyMySQL/PyMySQL
数据爬取
数据来源于百度百科,其HTML页面结构较为稳定,因此并未使用框架进行爬取。
首先做一些初始化准备工作。
import requestsimport pymysqlfrom bs4 import BeautifulSoup# 与Mysql数据库交互conn = pymysql.connect( host='127.0.0.1', port=3306, user='******', passwd='******', charset='utf8', db='deepsearch',)cur = conn.cursor()def insertName(name): cur.execute("select * from searched_name where name = %s", name) if cur.rowcount == 0: cur.execute("insert into searched_name (name) values(%s)", name) conn.commit()def insertLinks(fromname, toname): cur.execute( "select * from names_link where fromname = %s and toname = %s", (fromname, toname)) if cur.rowcount == 0: cur.execute( "insert into names_link (fromname,toname) values (%s,%s)", (fromname, toname)) conn.commit()
在正式爬取数据之前,数据库中已建立好了searched_name,names_link两个表格,用来存储已爬取的名字,名字的对应关系。大致结构如下:
searched_name
names_link
*原始数据表为空,此处数据仅为了方便理解而编造。
接下来设置一些辅助函数。
(此处代码写得较为冗余,不感兴趣的可直接跳过,后面有对代码的解释)
def getFilms(name): filmList = [] try: r = requests.get("http://baike.baidu.com/item/" + name) except HTTPError as e: print(e) return filmList r.encoding = 'utf-8' bs = BeautifulSoup(r.text, "lxml") for i in bs.findAll("b", {"class": "title"}): if i.a != None: if ('href' in i.a.attrs) and (i.a.attrs['href'] not in filmList): film = findCorrectFilm(name, i.a.attrs['href']) filmList.append(film) return filmListdef findCorrectFilm(name, film): actors = [] samefilms = [] r = requests.get("http://baike.baidu.com/" + film) r.encoding = 'utf-8' bs = BeautifulSoup(r.text, 'lxml') same_films_table = bs.find( 'ul', {'class': ['polysemantList-wrapper', 'cmn-clearfix']}) if same_films_table != None: same_films_list = same_films_table.findAll('li') if same_films_list: for j in same_films_list: j = j.a if j != None: if 'href' in j.attrs: samefilms.append(j.attrs['href']) for i in bs.findAll('dl', {'class': 'info'}): if i.a != None: tempname = i.a.get_text() if tempname not in actors: actors.append(tempname) if name not in actors: for t in range(0, len(samefilms)): r = requests.get("http://baike.baidu.com/" + samefilms[t]) r.encoding = 'utf-8' bs = BeautifulSoup(r.text, 'lxml') for i in bs.findAll('dl', {'class': 'info'}): if i.a != None: tempname = i.a.get_text() if tempname not in actors: actors.append(tempname) if name in actors: return samefilms[t] else: return film else: return film
这两个函数体现了此次数据爬取的思路,即如何在百度百科上找到指定演员所认识的人。我的做法是
1.先爬取该指定演员出演过的全部电影
2.再对这些电影进行遍历,爬取每部电影对应的所有演员、职员
而这些演员、职员即是该指定演员所认识的人。
在上述代码中getFilms函数返回了指定演员出演的所有作品,findCorrectFilm函数则对同名电影进行了过滤,找到属于指定演员真正出演的电影。
最后对人物关系进行爬取和存储。
SEARCHEDNAME = set()def getFriends(name, deep): global SEARCHEDNAME if name not in SEARCHEDNAME: SEARCHEDNAME.add(name) if deep > 5: return insertName(name) filmlist = getFilms(name) friendlist = [] for film in filmlist: if film != None: try: r = requests.get("http://baike.baidu.com" + film) except HTTPError as e: return r.encoding = 'utf-8' bs = BeautifulSoup(r.text, 'lxml') for i in bs.findAll('dl', {'class': 'info'}): if i.a != None: toname = i.a.get_text() if toname not in friendlist: insertLinks(name, toname) friendlist.append(toname) for newname in friendlist: getFriends(newname, deep + 1) else: returntry: getFriends("宋小宝", 0)finally: cur.close() conn.close()
此处代码完成了上述爬取思路中的第二步,即爬取指定演员所演电影对应的所有演员、职员的名字,并把这些对应关系存入数据库。
在此过程中采用了递归调用,并设置了变量deep来跟跟踪函数递归的次数,当deep值为6时,函数会自动返回,防止数据太大导致内存堆栈溢出。
因为无法预估此程序运行时间,我将其放置在云服务器上运行,最后程序运行时间为4118.0s。
数据分析
此时,数据已经安安静静地躺在数据库中。
那么问题来了:
宋小宝通过哪几个人可以找到詹妮弗劳伦斯?
不考虑性能,用获得的数据,本程序实现了一个深度优先,找到即返回的搜索算法,代码如下:
class Found(RuntimeError): def __init__(self, message): self.message = messagedef getNames(fromname): cur.execute( "select toname from names_link where fromname = %s ", fromname) names = [] for i in cur.fetchall(): if i[0] != fromname: names.append(i[0]) return namessearched_name = set()key_name = []def DeepSearch(fromname, toname, depth): global searched_name global key_name if fromname not in searched_name: searched_name.add(fromname) if depth > 5: return names = getNames(fromname) if(names): if toname in names: print("找到:" + toname) key_name.append(toname) key_name.append(fromname) raise Found("来自:" + fromname) for name in names: if name not in searched_name: try: DeepSearch(name, toname, depth + 1) except Found as f: print(f.message) key_name.append(fromname) raise Found("来自:" + fromname) else: returntry: DeepSearch("宋小宝", "詹妮弗·劳伦斯", 0) print("找不到两人之间的关系")except Found as f: print(f.message)
这里的getNames函数是辅助函数,用来从数据库中获取指定名字所认识的名字列表。
程序运行规则如下:
1.如果递归达到次数限制,停止搜索。
2.如果函数获取的名字列表不为空,则在该列表上进行搜索。如果名字列表为空,则返回上一层进行搜索。
3.如果当前列表包含要搜索的目标名字,就将该名字打印,并抛出异常,显示目标名字已经搜索到。递归过程中的每个栈都会打印当前名字,然后抛出异常显示目标名字已经搜索到,最终在屏幕上打印完整的名字路径。
4.如果在当前列表未找到目标名字,把递归次数加1,调用函数搜索下一层名字列表。
程序运行结果如下:
至此,人物关系链已经找到。然而我们还需解决另一个问题:
这些人是怎么认识的?
在上述代码中,变量key_name已将人物关系链中的名字进行了储存。在此基础上,我对其中每一个名字进行重新爬取,找到他们的共同电影。代码如下:
def getFilms(name): filmList = [] try: r = requests.get("http://baike.baidu.com/item/" + name) except HTTPError as e: print(e) return filmList r.encoding = 'utf-8' bs = BeautifulSoup(r.text, "lxml") for i in bs.findAll("b", {"class": "title"}): if i.a != None: filmList.append(i.a.get_text()) return filmListdef findCommonFilm(filmlist1, filmlist2): for film in filmlist1: if film in filmlist2: return filmdef relationShip(): common_film = [] chain = "" for i in range(0, len(key_name) - 1): filmlist1 = getFilms(key_name[i]) filmlist2 = getFilms(key_name[i + 1]) common_film.append(findCommonFilm(filmlist1, filmlist2)) for i in range(1, len(common_film) + 1): chain += str(key_name[-i]) + "因电影<<" + str(common_film[-i]) + ">>" + "认识了" + str(key_name[-i - 1]) + "," + "\n" return chainchain = relationShip()print("人物关系:" + chain )
程序运行结果如下:
出于好奇,我对其它一些名字进行了测试。
总结
通过这个小程序我们找到了宋小宝和大表姐的关系,他们之间间隔了4个人,分别是赵本山、张洪杰、张豆豆、贝尔。在此过程中,通过总结发现了以下一些问题:
1.数据存在局限性。由于数据来源于百度百科,考虑到爬取的难度,目前获取的数据都集中在影视圈,对于跨圈的人际关系无法得到很好的验证。
2.实现的搜索算法较为简陋。只找了众多路径中的一条,且并未解决“A指向B,B指向C,但同时A又直接指向C“的问题,这导致了人物关系链的增长。
3.我和吴彦祖一定是有交集的。
- 宋小宝通过哪几个人可以找到詹妮弗劳伦斯——利用Web Scraping测试“六度分隔假说”
- 好莱坞艳照门女星曝光 奥斯卡影后詹妮弗劳伦斯艳照流出
- Web Scraping
- 句子深度假说——冯志伟
- 六度分隔理论
- 六度分隔理论
- 六度分隔理论
- 六度分隔理论
- Best Web Scraping Books
- Python-web-scraping简介
- Sapir—Whorf Hypothesis (萨皮尔—沃尔夫假说)
- 社会网络、六度分隔
- 六度分隔与SNS
- SNS:六度分隔理论
- 什么是六度分隔理论
- Scraping Web Pages With R
- Great toolset for Web Scraping
- 《Web Scraping with Python》读书笔记
- unity中出现UriFormatException: URI scheme must start with a letter and must consist of one of alphabet,
- map
- iOS开发之跑马灯
- raml-for-jax-rs插件
- 参加第三届国家信息安全与国产化战略高层论坛有感
- 宋小宝通过哪几个人可以找到詹妮弗劳伦斯——利用Web Scraping测试“六度分隔假说”
- String s1 = new String("abc");这句话创建了几个对象?
- HTTP协议
- APScheduler代码流程简要分析
- GCD中的dispatch_barrier_async函数的使用(栅栏函数)
- bzoj1012
- hdu The Frog's Games(二分枚举)
- 使用Eclipse开发ABAP程序
- $_SERVER