学习了《简明Python教程》,然后想着实战一下,搜索了一些资料,然后对豆瓣电影排行250进行了一个抓取,后续还会对数据进行一些分析。
这篇文章主要是对抓取豆瓣电影top250过程的一个梳理,方便日后自己查阅,也希望可以方便到有需要的人。
下面是整个抓取过程的思维导图:
1. 生成URL信息
首先观察豆瓣电影TOP250的网页地址,多点开几页,就能发现规律。每一页都是展示了25个电影。
2. 分析网页标签
这个就是对网页html文件的解析了,可以先在网页中浏览目标信息,然后在网页的源码中通过搜索查找到相应的标签。最后一定要搞清楚定位每一个目标信息标签的方式。
我在这是采用的是BeautifulSoup对html进行的解析。
3. 请求网页数据
我是使用urllib.request中的urlopen()方法对网页进行请求的。
urlopen(url).read().decode('utf-8')
urlopen(url)获取到的是HTTPResponse对象,然后调用read()方法获得网页的字节数据,字节数据没法直接处理,所以最后调用decode('utf-8')方法将获取的字节对象编码成文本字符串(string)
4. 存储信息
先把数据生成pandas库的DataFrame对象,然后调用其to_csv()方法将数据存储到csv文件中,非常的方便。
完整的代码
算是自己完成了第一个小的爬虫程序,以后会在此基础上不断的扩展与完善。下面是获取豆瓣电影TOP250的Python完整代码。
# encoding=utf-8
from collections import defaultdict
from urllib.request import urlopen
from bs4 import BeautifulSoup
import pandas as pd
import time
import re
class DoubanMovieTop250:
# 生成url和相应存储最终数据的容器
def __init__(self):
self.top250_urls = ['https://movie.douban.com/top250?start={0}&filter='.format(i * 25) for i in range(10)]
self.data = defaultdict(list)
self.columns = ['title', 'link', 'score', 'score_cnt', 'top_no', 'directors', 'writers', 'actors', 'types',
'edit_location', 'language', 'dates', 'play_location', 'length', 'rating_per',
'betters', 'had_seen', 'want_see', 'tags', 'review', 'ask', 'discussion']
self.df=None
def get_info(self):
for url in self.top250_urls:
# 将获取的网页封装成BeautifulSoup
print(url)
html = urlopen(url).read().decode('utf-8')
bsobj = BeautifulSoup(html)
# 开始解析网页中的数据
main = bsobj.find('ol', {'class': 'grid_view'})
# 标题及链接信息
title_obj = main.findAll('div', {'class': 'hd'})
titles = [ i.find('span', {'class': 'title'}).text for i in title_obj]
links = [ i.find('a')['href'] for i in title_obj]
# 评分信息
score_obj = main.findAll('div', {'class': 'star'})
scores = [i.find('span', {'class': 'rating_num', 'property': 'v:average'}).text for i in score_obj]
score_cnts = [ i.findAll('span')[-1].text for i in score_obj]
# 将解析到的数据存入到容器中
for title, link, score, score_cnt in zip(titles, links, scores, score_cnts):
self.data[title].extend([title, link, score, score_cnt])
# 解析更多、更详细的信息
more_data = self.get_more_info(link)
self.data[title].extend(more_data)
print(self.data[title])
print(len(self.data))
time.sleep(1)
def get_more_info(self, link):
html = urlopen(link).read().decode('utf-8')
bsobj = BeautifulSoup(html)
# 榜单排名 top_no
top_no = bsobj.find('span', {'class': 'top250-no'}).text
# 导演 directors
main = bsobj.find('div', {'id': 'info'})
directors = [i.text for i in main.findAll('a', {'rel': 'v:directedBy'})]
# 编剧 writers 可能没有编剧
try:
writers = [i.text for i in main.findAll('span', {'class': 'attrs'})[1].findAll('a')]
except Exception as ex:
writers = []
print(ex)
# 主演 actors 可能没有主演
try:
actors_obj = main.findAll('a', {'rel': 'v:starring'})
actors = [i.text for i in actors_obj]
except Exception as ex:
actors = []
print(ex)
# 类型 types
types_obj = main.findAll('span', {'property': 'v:genre'})
types = [i.text for i in types_obj]
# 制片地区 edit_location
pattern = re.compile('地区:(.*)\n语言', re.S)
edit_location = re.findall(pattern, main.text)[0]
# 语言 language
pattern2 = re.compile('语言:(.*)\n上映日期', re.S)
language = re.findall(pattern2, main.text)[0]
# 上映日期和地区 dates play_location
date_boj = main.findAll('span', {'property': 'v:initialReleaseDate'})
dates = [i['content'].split('(')[0] for i in date_boj]
play_location = [i['content'].split('(')[1] for i in date_boj]
# 片长 length
length = main.find('span', {'property': 'v:runtime'})['content']
# 5星到1星的比例 rating_per 一共是5个数字
rating_per = [i.text for i in bsobj.findAll('span', {'class': 'rating_per'})]
# 好于 betters
better_obj = bsobj.find('div', {'class': 'rating_betterthan'}).findAll('a')
betters = [i.text for i in better_obj]
# 想看/看过 had_seen want_see
fit_obj = bsobj.find('div', {'class': 'subject-others-interests-ft'})
had_seen = fit_obj.find('a').text[:-3]
want_see = fit_obj.findAll('a')[1].text[:-3]
# 标签 tags
tags = [i.text for i in bsobj.find('div', {'class': 'tags-body'}).findAll('a')]
# 短评 review (有多少个短评)
review = bsobj.find('div', {'id': 'comments-section'}).find('h2').find('a').text
# 问题 ask (有多少个问题)
ask = bsobj.find('div', {'id': 'askmatrix'}).find('a').text.strip()
# 讨论 discussion (有多少个讨论)
discussion = bsobj.find('p', {'class': 'pl', 'align':'right'}).find('a').text.strip().split('(')[1][2:-2]
more_data=[top_no, directors, writers, actors, types, edit_location, language, dates, play_location, length, rating_per, betters, had_seen, want_see,tags, review, ask, discussion]
return more_data
# 存储数据到csv文件
def dump_data(self):
data = []
for title, value in self.data.items():
data.append(value)
self.df=pd.DataFrame(data, columns=self.columns)
self.df.to_csv('exercise_douban_movie_Top250.csv')
if __name__ == '__main__':
douban = DoubanMovieTop250()
douban.get_info()
douban.dump_data()