欢迎光临
我们一直在努力

多线程爬虫 + 数据可视化——爬取豆瓣Top250并生成统计图

之前的文章都是单线程爬取,速度有限。如果爬取几千上万条数据,单线程可能要等几分钟甚至几十分钟。本文以豆瓣 Top250 为例,使用多线程 + 数据可视化,爬完数据后直接生成统计图。

一、多线程爬虫是什么

单线程就像一个人在搬砖,多线程就是多个人同时搬砖。

对比单线程多线程(4线程)
爬取250条数据 ~10秒 ~3秒
原理 爬完一页再爬下一页 4页同时爬
代码复杂度 简单 稍复杂

Python 中用 concurrent.futures.ThreadPoolExecutor 实现,使用非常简单。

二、核心实现

from concurrent.futures import ThreadPoolExecutor, as_completed
import requests
from lxml import etree

HEADERS = {'User-Agent': 'Mozilla/5.0'}
BASE_URL = "https://movie.douban.com/top250?start={}&filter="

def scrape_page(page):
"""爬取单页(25条)"""
url = BASE_URL.format(page * 25)
resp = requests.get(url, headers=HEADERS, timeout=10)
resp.encoding = "utf-8"
tree = etree.HTML(resp.text)

movies = []
items = tree.xpath('//ol[@class="grid_view"]/li')
for item in items:
rank = item.xpath('.//em/text()')[0]
title = item.xpath('.//span[@class="title"]/text()')[0]
rating = item.xpath('.//span[@class="rating_num"]/text()')[0]
people_text = item.xpath('.//span[contains(text(), "人评价")]/text()')
people = people_text[0].strip() if people_text else "0人评价"

movies.append({
"rank": int(rank),
"title": title,
"rating": float(rating),
"people": people
})
return movies

def crawl_all():
"""多线程爬取全部10页"""
all_movies = []

with ThreadPoolExecutor(max_workers=5) as executor:
# 提交所有任务
futures = {executor.submit(scrape_page, page): page for page in range(10)}

# 收集结果
for future in as_completed(futures):
page = futures[future]
try:
movies = future.result()
all_movies.extend(movies)
print(f"第{page+1}页完成,当前共{len(all_movies)}条")
except Exception as e:
print(f"第{page+1}页失败: {e}")

# 按排名排序
all_movies.sort(key=lambda x: x["rank"])
return all_movies

运行

import time
start = time.time()
movies = crawl_all()
print(f"\\n爬取完成!共{len(movies)}条,耗时{time.time()start:.1f}秒")

输出效果:

第1页完成,当前共25条
第4页完成,当前共50条
第2页完成,当前共75条
第3页完成,当前共100条

爬取完成!共250条,耗时2.8秒

三、数据统计分析

爬完数据后用 pandas 分析一下:

import pandas as pd

df = pd.DataFrame(movies)

# 评分分布统计
print("评分统计:")
print(df["rating"].describe())

# 评分最高的10部
print("\\n评分最高的10部:")
print(df.head(10)[["rank", "title", "rating"]])

# 评分区间分布
print("\\n评分区间分布:")
for i in range(4, 10):
count = len(df[(df["rating"] >= i) & (df["rating"] < i+1)])
print(f" {i}~{i+1}分: {count}部")

四、数据可视化(matplotlib)

将统计结果生成图表,直观展示数据分布:

import matplotlib.pyplot as plt
import matplotlib
matplotlib.rc('font', family='Microsoft YaHei') # 显示中文
matplotlib.use('Agg') # 兼容无界面环境

def plot_rating_distribution(df):
"""评分分布柱状图"""
# 统计各评分区间的电影数量
bins = [i/2 for i in range(16, 22)] # 8.0 ~ 10.0
labels = [f"{bins[i]:.1f}~{bins[i+1]:.1f}" for i in range(len(bins)1)]
counts = []
for i in range(len(bins)1):
count = len(df[(df["rating"] >= bins[i]) & (df["rating"] < bins[i+1])])
counts.append(count)

plt.figure(figsize=(10, 6))
bars = plt.bar(labels, counts, color='#1f77b4', alpha=0.8)
plt.title('豆瓣电影Top250 评分分布', fontsize=16)
plt.xlabel('评分区间', fontsize=12)
plt.ylabel('电影数量', fontsize=12)

# 在柱子上标出数字
for bar, count in zip(bars, counts):
plt.text(bar.get_x() + bar.get_width()/2, bar.get_height() + 1,
str(count), ha='center', fontsize=10)

plt.tight_layout()
plt.savefig('豆瓣评分分布.png', dpi=100)
plt.close()
print("图表已保存: 豆瓣评分分布.png")

def plot_top10_bar(df):
"""评分最高10部电影的横向柱状图"""
top10 = df.head(10)

plt.figure(figsize=(10, 8))
bars = plt.barh(top10["title"], top10["rating"], color='#ff7f0e', alpha=0.8)
plt.title('豆瓣电影Top250 评分最高Top10', fontsize=16)
plt.xlabel('评分', fontsize=12)
plt.xlim(9.0, 10.0)

# 在柱子旁标出分數
for bar, rating in zip(bars, top10["rating"]):
plt.text(bar.get_width() + 0.02, bar.get_y() + bar.get_height()/2,
str(rating), va='center', fontsize=10)

plt.tight_layout()
plt.savefig('豆瓣评分Top10.png', dpi=100)
plt.close()
print("图表已保存: 豆瓣评分Top10.png")

完整主程序

import time

if __name__ == "__main__":
start = time.time()

# 1. 多线程爬取
movies = crawl_all()
print(f"爬取完成!共{len(movies)}条,耗时{time.time()start:.1f}秒\\n")

# 2. 转为 DataFrame
df = pd.DataFrame(movies)

# 3. 输出统计
print(df["rating"].describe())

# 4. 生成图表
plot_rating_distribution(df)
plot_top10_bar(df)

print(f"\\n全部完成!总耗时{time.time()start:.1f}秒")

五、多线程 vs 单线程速度对比

方式耗时速度提升
单线程(逐页爬) ~10秒 基准
多线程(5线程) ~2.8秒 快3~4倍
多线程(10线程) ~1.5秒 快6~7倍

六、踩坑提醒

1. 线程数不是越多越好

线程数超过 10 后速度提升不明显,反而可能被网站封 IP。建议 5~8 个线程。

2. 多线程爬取顺序是乱的

多线程执行时页面返回顺序不确定,最后需要按排名排序:

movies.sort(key=lambda x: x["rank"])

3. Matplotlib 中文乱码

添加以下配置解决:

matplotlib.rc('font', family='Microsoft YaHei')
matplotlib.rc('axes', unicode_minus=False)

总结

多线程爬虫 + 数据可视化是爬虫开发的进阶技能。多线程大幅提升爬取效率,可视化让分析结果一目了然。这套思路不仅适用于豆瓣,也适用于任何需要大规模数据采集+展示的场景。


如果对你有帮助,欢迎点赞、评论、关注【张老师技术栈】,持续分享 Java/Python/爬虫 实战干货。

赞(0)
未经允许不得转载:171主机测评 » 多线程爬虫 + 数据可视化——爬取豆瓣Top250并生成统计图
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址