数据获取


request获取网页内容

1
2
3
4
import requests
url = 'https://movie.douban.com/top250?start='
response = requests.get(url)
print(response)
1
2
>>> <Response [418]>
# 响应状态码418表示访问的网站有反爬虫机制,而解决方法就是带请求头header(user-agent)访问。

1.信息响应 (100–199)

2.成功响应(200–299)

3.重定向消息(300–399)

4.客户端错误响应 (400–499)

5.服务端错误响应(500–599)

如何获取 user-agent :

在网页中,按 F12 打开开发者模式,选网络,按F5刷新页面,点击标头,即可查到 User-Agent

通过设置合理的 User-Agent 来模拟真实用户的请求,从而降低被网站封禁或识别为爬虫的风险,合理即可,网上有很多。

设置好 User-Agent 后:

1
2
3
4
5
6
7
8
9
# 设置请求头
headers = {
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) "
"Chrome/117.0.0.0 Mobile Safari/537.36 Edg/117.0.2045.31"
}

response = requests.get(url, headers=headers)
print(response)
print(type(requests.get(url)))
1
2
>>> <Response [200]>
>>> <class 'requests.models.Response'>

响应状态码200表示服务器已成功处理了请求.

requests库的相关函数 :

函数说明
requests.get()发送 GET 请求,获取指定 URL 的内容
requests.post()发送 POST 请求,向指定 URL 提交数据
requests.put()发送 PUT 请求,更新指定 URL 的内容
requests.delete()发送 DELETE 请求,删除指定 URL 的资源
requests.head()发送 HEAD 请求,获取指定 URL 的响应头信息
requests.options()发送 OPTIONS 请求,获取指定 URL 支持的请求方法和头部信息
requests.request()通用接口,发送自定义请求方法(GET/POST/PUT/DELETE 等)
requests.session()创建一个 Session 对象,用于发送多个请求并保持会话状态
response.text获取响应内容(以文本形式返回)
response.json()获取响应内容,并将 JSON 字符串转换为 Python 字典或列表
response.status_code获取响应的状态码
response.headers获取响应的头部信息
response.cookies获取响应的 Cookie 信息
response.raise_for_status()如果响应状态码不是 200,抛出异常
response.content获取响应内容的原始字节流
response.url获取响应的 URL
response.request获取与响应相关的请求对象

电影相关信息的定位和获取

如图所示,我们接下来要定位的是 电影名称导演主演上映年份制片国家类型评分影评.

F12,查看网页源代码可以发现,每部电影的信息全部在 **

**里

定位相关信息位置

1
2
3
4
from bs4 import BeautifulSoup
soup.find_all('div', class_='info') # 获取全部标签为div class属性为info的所有元素,为一个列表
type(soup.find_all('div', class_='info'))
len(type(soup.find_all('div', class_='info')))
1
2
>>> bs4.element.ResultSet
>>> 25

定位电影名称,评分数据,影评

由上述 soup.find_all('div', class_='info') 返回一个列表,for 循环迭代即可:

1
2
3
4
5
6
7
8
for item in soup.find_all('div', class_='info'):
# 获取电影名称
name = item.find('span', class_='title').text.strip()
# 获取评分
rating = item.find('span', class_='rating_num').text.strip()
# 获取影评
inq = item.find('span', class_='inq').text.strip() if item.find('span', class_='inq') else "无"

1
2
3
4
5
6
>out[]:
># 肖申克的救赎 霸王别姬 阿甘正传 泰坦尼克号 这个杀手不太冷
># 千与千寻 美丽人生 星际穿越 辛德勒的名单 盗梦空间
># 楚门的世界 忠犬八公的故事 海上钢琴师 三傻大闹宝莱坞 放牛班的春天
># 机器人总动员 疯狂动物城 无间道 控方证人 大话西游之大圣娶亲
># 熔炉 教父 触不可及 当幸福来敲门 末代皇帝
1
2
3
4
5
6
>out[]:
># 9.7 9.6 9.5 9.5 9.4
># 9.4 9.6 9.4 9.6 9.4
># 9.4 9.4 9.3 9.2 9.3
># 9.3 9.2 9.3 9.6 9.2
># 9.4 9.3 9.3 9.2 9.3

已经成功获取到电影名称,评分数据,影评.


获取导演,主演,上映年份,制片国家,类型

由于这些信息,在一个标签下,虽然依旧可以用bs4,不过我在这里用正则表达式来定位具体信息。

1
2
3
4
5
6
# 正则表达式
pattern1 = r'导演: (.*?)\s' # 用于匹配 导演
pattern2 = r'主演: (.*?)\.\.\.' # 用于匹配 演员
pattern3 = r'(\d+)' # 用于匹配 上映年份
pattern4 = r'/\s*([\u4e00-\u9fa5\s]+)\s*/' # 用于匹配 制片国家
pattern5 = r'/(?P<genre>[^/\n]+)' # 用于匹配 类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
for item in soup.find_all('div', class_='info'):
item_o = item.p.text # 获取相关信息文本
# 获取导演
director_match = re.search(pattern1, item_o)
director = director_match.group(1) if director_match else '无'

# 获取主演
actor_match = re.search(pattern2, item_o)
actor = actor_match.group(1) if actor_match else '无'

# 获取年份
year_match = re.search(pattern3, item_o)
year = year_match.group(1) if year_match else '无'

# 获取制片国家
country_match = re.search(pattern4, item_o)
country = country_match.group(1) if country_match else None

# 获取类型
genres_match = re.findall(pattern5, item_o)
genre = genres_match[-1].replace("&nbsp;", "") if genres_match else ''

获取所有页电影信息

至此,我们现在已经获取到了所有与电影相关的信息,不过是其中一页的。

我们来分析一下 url

第一页:https://movie.douban.com/top250?start=0&filter=

第二页:https://movie.douban.com/top250?start=25&filter=

第三页:https://movie.douban.com/top250?start=50&filter=

可以看到,网页在只在 start= 有变化,我们只需要写一个循环,来更迭url

1
2
3
4
for start in range(0, 250 + 1, 25):
url = f"https://movie.douban.com/top250?start={start}&filter="
response = requests.get(url, headers=headers)
html = response.text

好了,再将之前代码跟在后边,就能获取所有的电影数据

数据存储

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
for start in range(0, 250 + 1, 25):
url = f"https://movie.douban.com/top250?start={start}&filter="
response = requests.get(url, headers=headers)
html = response.text
"""
相关代码
"""
movie = {'rank': start + i + 1, 'name': name, 'director': director, 'actor': actor, 'year': year,
'country': country, 'genre': genre, 'rating': rating, 'inq': inq}

movies.append(movie)

with open('movies.csv', 'w', newline='', encoding='utf-8') as csvfile:
fieldnames = ['Top', '电影名称', '导演', '主演', '上映年份', '制片国家', '类型', '评分', '影评']
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

writer.writeheader()
for movie in movies:
writer.writerow(
{'Top': movie['rank'], '电影名称': movie['name'], '导演': movie['director'],'主演': movie['actor'],
'上映年份': movie['year'], '制片国家': movie['country'].strip(), '类型': movie['genre'].strip(),
'评分': movie['rating'],
'影评': movie['inq']})

movie.csv 文件格式如:

至此,我们已经成功爬取到豆瓣Top250的电影数据


数据分析及可视化

1
2
3
4
5
import pandas as pd

# 读取电影信息数据
data = pd.read_csv('movies.csv')
data.head()
Top电影名称导演主演上映年份制片国家类型评分影评
01肖申克的救赎弗兰克·德拉邦特蒂姆·罗宾斯 Tim Robbins /1994美国犯罪 剧情9.7希望让人自由。
12霸王别姬陈凯歌张国荣 Leslie Cheung / 张丰毅 Fengyi Zha1993中国大陆 中国香港剧情 爱情 同性9.6风华绝代。
23阿甘正传罗伯特·泽米吉斯汤姆·汉克斯 Tom Hanks /1994美国剧情 爱情9.5一部美国近现代史。
34泰坦尼克号詹姆斯·卡梅隆莱昂纳多·迪卡普里奥 Leonardo1997美国 墨西哥剧情 爱情 灾难9.5失去的才是永恒的。
45这个杀手不太冷吕克·贝松让·雷诺 Jean Reno / 娜塔莉·波特曼1994法国 美国剧情 动作 犯罪9.4怪蜀黍和小萝莉不得不说的故事。
1
2
# 获取数据的基本信息
data.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 250 entries, 0 to 249
Data columns (total 9 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   Top     250 non-null    int64  
 1   电影名称    250 non-null    object 
 2   导演      250 non-null    object 
 3   主演      248 non-null    object 
 4   上映年份    250 non-null    int64  
 5   制片国家    250 non-null    object 
 6   类型      250 non-null    object 
 7   评分      250 non-null    float64
 8   影评      250 non-null    object 
dtypes: float64(1), int64(2), object(6)
memory usage: 17.7+ KB
1
2
# 统计数值列的统计信息
data.describe().round(2).T
countmeanstdmin25%50%75%max
Top250.0125.5072.311.063.25125.5187.75250.0
上映年份250.02000.5315.761931.01994.002004.02011.002021.0
评分250.08.930.278.48.708.99.109.7
1
2
3
# 计算评分的平均值
avg_rating = data['评分'].mean()
print("平均评分:", avg_rating)
平均评分: 8.932799999999999

接下来分析:

  1. 分析出电影中出现次数最多的导演(前5个);
  2. 分析出电影中出现次数最多的主演(以首个主演为准,前5个);
  3. 分析出电影中上映年份出现过的年份;
  4. 分析出电影中出现次数最多的制片国家(前5个);
  5. 分析出电影中出现次数最多的电影类型(前5个);
  6. 查找评分最高的5部电影,并获取它们的影评。
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
import pandas as pd

# 读取电影数据
df = pd.read_csv('movies.csv')

# 填充NaN值为''
df['主演'].fillna('', inplace=True)

# 过滤掉主演为"无"的电影
df = df[df['主演'] != '无']

# 分析导演和主演
directors = df['导演'].value_counts().index.tolist()[:5]
actors = df['主演'].apply(lambda x: x.split(' ')[0]).value_counts().index.tolist()[:5]

# 分析上映年份和制片国家
years = df['上映年份'].value_counts().index.tolist()
countries = df['制片国家'].value_counts().index.tolist()[:5]

# 分析电影类型
genres = []
for genres_str in df['类型']:
genres.extend(genres_str.split(' '))
genres = pd.Series(genres).value_counts().index.tolist()[:5]

# 分析评分和影评
top_ratings = df.sort_values(by='评分', ascending=False).head(5)
reviews = top_ratings['影评'].tolist()

# 打印分析结果
print('导演:',directors)
print('主演:', actors)
print('上映年份:', years)
print('制片国家:', countries)
print('电影类型:', genres)
print('影评:', reviews)

导演: ['宫崎骏', '克里斯托弗·诺兰', '史蒂文·斯皮尔伯格', '李安', '王家卫']
主演: ['丹尼尔·雷德克里夫', '周星驰', '马特·达蒙', '汤姆·汉克斯', '莱昂纳多·迪卡普里奥']
上映年份: [2004, 2010, 1994, 2013, 2014, 2008, 2009, 2003, 2016, 2001, 1997, 1993, 2015, 1995, 2002, 2011, 2006, 2018, 2012, 2000, 1991, 2017, 1999, 2005, 1987, 2007, 1998, 1990, 1988, 1984, 2019, 1992, 1989, 1986, 1957, 1974, 1954, 1940, 2021, 1950, 1966, 1931, 2020, 1968, 1952, 1960, 1982, 1961, 1979, 1972, 1996, 1939, 1953, 1936, 1975, 1965, 1971]
制片国家: ['美国', '日本', '中国香港', '英国 美国', '韩国']
电影类型: ['剧情', '爱情', '喜剧', '冒险', '犯罪']
影评: ['希望让人自由。', '最美的谎言。', '比利·怀德满分作品。', '风华绝代。', '拯救一个人,就是拯救整个世界。']
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import pandas as pd
import matplotlib.pyplot as plt
from wordcloud import WordCloud

plt.rcParams['font.sans-serif'] = ['SimHei'] # 防止中文乱码
plt.rcParams['axes.unicode_minus'] = False

# 读取电影数据
df = pd.read_csv('movies.csv')

# 填充NaN值为''
df['主演'].fillna('', inplace=True)

# 过滤掉主演为"无"的电影
df = df[df['主演'] != '无']

# 分析导演和主演
directors = df['导演'].value_counts().head(5)
actors = df['主演'].apply(lambda x: x.split(' ')[0]).value_counts().head(5)

# 分析评分和影评
top_ratings = df.sort_values(by='评分', ascending=False).head(25)
reviews = ' '.join(top_ratings['影评'].tolist())

1
2
3
4
5
6
7
8
9
10
11
12
13
import matplotlib.pyplot as plt

# 指定颜色和图例标签
colors = ['steelblue', 'darkorange', 'forestgreen', 'purple', 'red']
plt.figure(figsize=(10, 5))
directors.plot(kind='barh', color=colors)
plt.xlabel('电影数量', fontsize=12)
plt.ylabel('导演', fontsize=12)
plt.title('前5位导演的频率分布', fontsize=14)
plt.grid(axis='x', linestyle='--')

plt.show()

1
2
3
4
5
6
7
8
9
10
11
12
# 绘制主演频率分布的饼图
plt.figure(figsize=(6, 6))
actors_plot = actors.plot(kind='pie', autopct='%1.1f%%', colors=['skyblue', 'orange', 'limegreen', 'gold', 'red'])

plt.title('前5位主演的频率分布', fontsize=14)
plt.axis('equal') # 使饼图为正圆形

# 调整图例的位置和大小,不覆盖图片
actors_legend = actors_plot.legend(fontsize=10, loc='center left', bbox_to_anchor=(1, 0.5))

plt.show()

1
2
3
4
5
6
7
8
9
10
11
12
13
wordcloud = WordCloud(width=800, height=400,
background_color='white',
colormap='viridis',
font_path="D:\\主题\\经典宋体.ttf",
max_font_size=150,
max_words=200).generate(reviews)

plt.figure(figsize=(10, 5))
plt.imshow(wordcloud, interpolation='bilinear')
plt.axis('off')
plt.title('影评关键词词云图', fontsize=16)
plt.show()

总结

以上就是爬取豆瓣Top250的介绍,和数据分析以及可视化。

相关文件

[爬虫源码] 做了修改和优化,这里就不再讲解了。

[movies.csv] 爬取数据的csv文件

[Bean_Data_Analytics_section.ipynb] 数据分析及可视化部分

有问题的话,可以在评论区评论,也可以联系我。

备注:

本文仅供学习交流,对于爬虫浅尝辄止,以免对服务器造成负担


🆗,今天的分享就到这里啦!!!