Python爬虫与数据可视化教程

0 阅读5分钟

对于经常写爬虫的技术来说了,可视化大大的提高工作效率,可以让获取的数据更直观的展示在面前,下面我将通过具体实操给大家展示下多种可视化具体教程,希望能都帮助大家。

a4.png

下面是一个完整的Python爬虫和数据可视化解决方案,我们将爬取豆瓣电影Top250数据并进行多种可视化展示。

import requests
from bs4 import BeautifulSoup
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.font_manager import FontProperties
import time
import random
from wordcloud import WordCloud
import jieba
from collections import Counter
​
# 设置中文字体
font = FontProperties(fname=r"simhei.ttf", size=12)
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号# 爬取豆瓣电影Top250数据
def scrape_douban_top250():
    base_url = "https://movie.douban.com/top250"
    headers = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'
    }
    
    movies = []
    
    for start in range(0, 250, 25):
        url = f"{base_url}?start={start}"
        try:
            response = requests.get(url, headers=headers)
            response.raise_for_status()
            soup = BeautifulSoup(response.text, 'html.parser')
            
            items = soup.find_all('div', class_='item')
            
            for item in items:
                # 排名
                rank = item.find('em').text
                
                # 标题
                title = item.find('span', class_='title').text
                
                # 信息
                info = item.find('div', class_='bd').p.text.strip().split('\n')[0].strip()
                
                # 评分
                rating = item.find('span', class_='rating_num').text
                
                # 评价人数
                num_ratings = item.find('div', class_='star').find_all('span')[-1].text[:-3]
                
                # 简介
                quote_tag = item.find('span', class_='inq')
                quote = quote_tag.text if quote_tag else ""
                
                # 提取年份和地区
                info_parts = info.split('/')
                year = info_parts[0].strip()[-4:]  # 提取年份
                region = info_parts[1].strip() if len(info_parts) > 1 else "未知"
                
                movies.append({
                    '排名': int(rank),
                    '标题': title,
                    '年份': int(year) if year.isdigit() else 0,
                    '地区': region,
                    '评分': float(rating),
                    '评价人数': int(num_ratings.replace(',', '')) if num_ratings else 0,
                    '简介': quote
                })
                
            # 随机延时,避免请求过快
            time.sleep(random.uniform(1, 2))
            
        except Exception as e:
            print(f"爬取第 {start//25 + 1} 页时出错: {e}")
    
    return movies
​
# 数据可视化
def visualize_movie_data(df):
    # 1. 创建画布
    plt.figure(figsize=(18, 12))
    plt.suptitle('豆瓣电影Top250数据分析', fontsize=20, fontweight='bold', fontproperties=font)
    
    # 2. 评分分布直方图
    plt.subplot(2, 3, 1)
    sns.histplot(df['评分'], bins=20, kde=True, color='skyblue')
    plt.title('电影评分分布', fontproperties=font)
    plt.xlabel('评分', fontproperties=font)
    plt.ylabel('电影数量', fontproperties=font)
    plt.axvline(df['评分'].mean(), color='red', linestyle='dashed', linewidth=1)
    plt.text(df['评分'].mean()+0.05, 20, f'平均分: {df["评分"].mean():.2f}', color='red')
    
    # 3. 评分与年份的关系
    plt.subplot(2, 3, 2)
    sns.scatterplot(x='年份', y='评分', data=df, hue='评分', palette='coolwarm', size='评价人数', sizes=(20, 200))
    plt.title('评分与年份的关系', fontproperties=font)
    plt.xlabel('年份', fontproperties=font)
    plt.ylabel('评分', fontproperties=font)
    plt.xticks(rotation=45)
    
    # 4. 各地区电影数量
    plt.subplot(2, 3, 3)
    region_counts = df['地区'].value_counts().head(8)
    colors = plt.cm.Paired(np.linspace(0, 1, len(region_counts)))
    region_counts.plot(kind='pie', autopct='%1.1f%%', colors=colors, shadow=True, startangle=90)
    plt.title('各地区电影占比', fontproperties=font)
    plt.ylabel('')
    
    # 5. 评分TOP10电影
    plt.subplot(2, 3, 4)
    top10 = df.sort_values('评分', ascending=False).head(10)
    sns.barplot(x='评分', y='标题', data=top10, palette='viridis')
    plt.title('评分TOP10电影', fontproperties=font)
    plt.xlabel('评分', fontproperties=font)
    plt.ylabel('电影标题', fontproperties=font)
    for i, v in enumerate(top10['评分']):
        plt.text(v - 0.4, i, str(v), color='white', va='center', fontweight='bold')
    
    # 6. 评价人数TOP10电影
    plt.subplot(2, 3, 5)
    top10_popular = df.sort_values('评价人数', ascending=False).head(10)
    sns.barplot(x='评价人数', y='标题', data=top10_popular, palette='magma')
    plt.title('评价人数TOP10电影', fontproperties=font)
    plt.xlabel('评价人数', fontproperties=font)
    plt.ylabel('电影标题', fontproperties=font)
    
    # 7. 年份分布
    plt.subplot(2, 3, 6)
    sns.countplot(x='年份', data=df, order=df['年份'].value_counts().index.sort_values(), palette='Set2')
    plt.title('电影年份分布', fontproperties=font)
    plt.xlabel('年份', fontproperties=font)
    plt.ylabel('数量', fontproperties=font)
    plt.xticks(rotation=90)
    
    plt.tight_layout(rect=[0, 0, 1, 0.96])
    plt.savefig('movie_analysis.png', dpi=300)
    plt.show()
    
    # 8. 词云图 - 电影标题关键词
    all_titles = ' '.join(df['标题'])
    word_list = jieba.cut(all_titles)
    word_count = Counter(word_list)
    
    # 过滤单字词
    filtered_words = {word: count for word, count in word_count.items() if len(word) > 1 and word not in ['电影', '豆瓣']}
    
    wc = WordCloud(
        font_path='simhei.ttf',
        background_color='white',
        max_words=200,
        width=1000,
        height=700
    ).generate_from_frequencies(filtered_words)
    
    plt.figure(figsize=(12, 8))
    plt.imshow(wc, interpolation='bilinear')
    plt.title('电影标题关键词词云', fontsize=16, fontproperties=font)
    plt.axis('off')
    plt.savefig('movie_wordcloud.png', dpi=300)
    plt.show()
​
# 主函数
def main():
    print("开始爬取豆瓣电影Top250数据...")
    movies = scrape_douban_top250()
    print(f"成功爬取 {len(movies)} 部电影数据")
    
    # 创建DataFrame
    df = pd.DataFrame(movies)
    
    # 保存到CSV
    df.to_csv('douban_top250.csv', index=False, encoding='utf_8_sig')
    print("数据已保存到 douban_top250.csv")
    
    # 数据可视化
    print("开始数据可视化...")
    visualize_movie_data(df)
    print("可视化完成,结果已保存为 movie_analysis.png 和 movie_wordcloud.png")
​
if __name__ == "__main__":
    main()

代码功能说明

1. 数据爬取部分

  • 使用requests库爬取豆瓣电影Top250的10个页面
  • 使用BeautifulSoup解析HTML
  • 提取每部电影的排名、标题、年份、地区、评分、评价人数和简介
  • 添加随机延时避免请求过快被封禁

2. 数据可视化部分

创建了7个可视化图表和1个词云图:

  1. 评分分布直方图:展示电影评分的分布情况,包含平均分参考线
  2. 评分与年份关系散点图:展示电影评分随年份的变化趋势
  3. 地区分布饼图:展示电影的地区分布比例
  4. 评分TOP10电影:展示评分最高的10部电影
  5. 评价人数TOP10电影:展示评价人数最多的10部电影
  6. 年份分布图:展示电影在不同年份的数量分布
  7. 电影标题关键词词云:展示电影标题中出现频率最高的关键词

3. 数据保存

  • 将爬取的数据保存为CSV文件(douban_top250.csv)
  • 将可视化结果保存为PNG图片

运行要求

  1. 安装必要的Python库:
pip install requests beautifulsoup4 pandas matplotlib seaborn jieba wordcloud

2. 需要中文字体支持:

  • 代码中使用了"simhei.ttf"(黑体),请确保系统中存在该字体
  • 或者替换为其他支持中文的字体文件路径

注意事项

  1. 网络爬虫可能受到网站反爬策略的限制,如果爬取失败,请尝试:

    • 增加请求头中的User-Agent信息
    • 增加请求之间的延时
    • 使用代理IP
  2. 本代码仅用于学习目的,请尊重网站的数据使用政策

运行此代码后,我们将获得一个包含爬取数据的CSV文件和多张高质量的可视化图表图片,总体来说数据展示的还是挺直观的,如有任何问题可以留言讨论。