IMDB拥有着全球最大的电影数据库,最近我也在研究如何获取到这些宝贵的电影数据资源
根据时间来划分,可以将所有的电影分为:已上映和未上映。下面我将分这两种情况,分别去寻找方案
未上映电影数据
在IMDB官网,可以找到一些爬虫入口页面:http://www.imdb.com/movies-coming-soon/2016-01,http://www.imdb.com/movies-coming-soon/2016-02等等,通过分析url很容易发现:
url中的’movies-coming-soon’表示该页面的电影即将上映至影院,后面的日期为2016-xx,代表着该页面的电影预计2016年xx月上映
这样的话,我们就可以找到2016一整年的预映电影的入口页面
随意进入任何一个入口页面,里面陈列着该日期下所有预映电影的简短信息,包括指向某具体电影详情页面的link,随意点击某部电影,进入并得到link,比如:http://www.imdb.com/title/tt3460252/?ref_=cs_ov_tt
其中?ref_=cs_ov_tt是多余的,可以省略,于是link精简为:http://www.imdb.com/title/tt3460252,‘?ref_=cs_ov_tt’只是代表某些跳转信息(我猜的)
进入到电影详情页面,我们就可以抓取一切我们想要的数据了,原理很简单:
模拟http请求,然后解析HTML DOM树,主要用到了Python的request库和beautifulSoup4库,我还用到了阻塞队列Queue和多线程,解析电影详情页面的线程代码如下:
def detail_thread():
while True:
try:
m_url = movie_url.get()
print 'detail_thread fetch: ', m_url
r = requests.get(m_url)
soup = BeautifulSoup(r.content)
nMovie = newMovie()
# id
nMovie.id = m_url[m_url.index('/tt')+3:]
# year
nMovie.year = year
# title
nMovie.title = soup.find('h1', {'class': 'header'}).find(attrs={'class': 'itemprop', 'itemprop': 'name'}).text
title_details = soup.find('div', {'id': 'titleDetails'})
txt_block = title_details.findAll('div', {'class': 'txt-block'})
for t in txt_block:
try:
# countries
if t.find('h4').text == 'Country:':
c_links = t.findAll('a')
if len(c_links) > 0:
country = c_links[0].text
for c in c_links[1:]:
country = country + ':' + c.text
nMovie.countries = country
# languages
if t.find('h4').text == 'Language:':
l_links = t.findAll('a')
if len(l_links) > 0:
lan = l_links[0].text
for l in l_links[1:]:
lan = lan + ':' + l.text
nMovie.languages = lan
# production_companies
if t.find('h4').text == 'Production Co:':
p_c_links = t.findAll('a')
if len(p_c_links) > 0:
p_companies = p_c_links[0].text
for p_c in p_c_links[1:]:
p_companies = p_companies + ':' + p_c.text
nMovie.production_companies = p_companies
except:
continue
#print 'detail_thread: txt_block find no h4 tag'
# keywords
keywords = soup.findAll(attrs={'itemprop': 'keywords', 'class': 'itemprop'})
#print m_url, ' keywords:', len(keywords)
if len(keywords) > 0:
key_words = keywords[0].text
for k in keywords[1:]:
key_words = key_words + ':' + k.text
nMovie.keywords = key_words
# genres
genres_div = soup.find(attrs={'class': 'see-more inline canwrap', 'itemprop': 'genre'})
if genres_div != None:
g_links = genres_div.findAll('a')
#print m_url, ' genres:', len(g_links)
if len(g_links) > 0:
genres = g_links[0].text
for g in g_links[1:]:
genres = genres + ':' + g.text
#print genres
nMovie.genres = genres
# cast
cast_td = soup.findAll(attrs={'itemprop': 'actor'})
#print m_url, ' cast:', len(cast_td)
if len(cast_td) > 0:
casts = cast_td[0].find(attrs={'class': 'itemprop', 'itemprop': 'name'}).text
for c in cast_td[1:]:
casts = casts + ':' + c.find(attrs={'class': 'itemprop', 'itemprop': 'name'}).text
#print casts
nMovie.cast = casts
# ??
# editor
# producer
# writer
w_div = soup.find(attrs={'class': 'txt-block', 'itemprop': 'creator'})
if w_div != None:
w_links = w_div.findAll('a')
#print m_url, ' writer:', len(w_links)
if len(w_links) > 0:
writer = w_links[0].text
for w in w_links[1:]:
writer = writer + ':' + w.text
nMovie.writer = writer
#print nMovie.writer
# director
d_div = soup.find(attrs={'class': 'txt-block', 'itemprop': 'director'})
if d_div != None:
d_links = d_div.findAll('a')
#print m_url, ' director:', len(d_links)
if len(d_links) > 0:
director = d_links[0].text
for d in d_links[1:]:
director = director + ':' + d.text
nMovie.director = director
entities.put(nMovie)
movie_url.task_done()
except Exception as e:
print 'detail_thread: An {} exception occured'.format(e)
当然,在运行detail_thread这个线程之前,肯定要开启另外一个线程,作用是将所有电影详情页面的url放入movie_url这个Queue中,否则,detail_thread永远阻塞至死。所以,总的来设计,我运用了多线程+多队列搭建起了‘多级生产者和消费者’模型:
index_thread生产所有入口页面,放入index_url阻塞队列;movie_thread从index_url队列取入口页面,生产电影详情页面url,放入movie_url;detail_thread从movie_url获取link,解析生成nMovie的实体,实体中保存着有关电影的详细信息,放入entitie队列;最后交由db_thread做入库持久化操作
运行时,可以视实际运行快慢,给其中的某些线程增加数量,增快其运行速度
总的代码,可以查看我的https://github.com/su-kaiyao/mrp/blob/master/dataSet/newMovieCrawl.py
我自己运行了一把,抓了141条新电影,查阅了所有入口页面(2016-01 ~ 2016-12),数了数是145个,少了4个,是因为那4个电影的详细信息太少了,被脚本过滤了
已上映电影数据
移步:https://7color94.github.io/blog/2016/01/imdb-db-structure/