多线程爬虫 + 数据可视化——爬取豆瓣Top250并生成统计图
之前的文章都是单线程爬取速度有限。如果爬取几千上万条数据单线程可能要等几分钟甚至几十分钟。本文以豆瓣 Top250 为例使用多线程 数据可视化爬完数据后直接生成统计图。一、多线程爬虫是什么单线程就像一个人在搬砖多线程就是多个人同时搬砖。对比单线程多线程4线程爬取250条数据~10秒~3秒原理爬完一页再爬下一页4页同时爬代码复杂度简单稍复杂Python 中用concurrent.futures.ThreadPoolExecutor实现使用非常简单。二、核心实现fromconcurrent.futuresimportThreadPoolExecutor,as_completedimportrequestsfromlxmlimportetree HEADERS{User-Agent:Mozilla/5.0}BASE_URLhttps://movie.douban.com/top250?start{}filterdefscrape_page(page):爬取单页25条urlBASE_URL.format(page*25)resprequests.get(url,headersHEADERS,timeout10)resp.encodingutf-8treeetree.HTML(resp.text)movies[]itemstree.xpath(//ol[classgrid_view]/li)foriteminitems:rankitem.xpath(.//em/text())[0]titleitem.xpath(.//span[classtitle]/text())[0]ratingitem.xpath(.//span[classrating_num]/text())[0]people_textitem.xpath(.//span[contains(text(), 人评价)]/text())peoplepeople_text[0].strip()ifpeople_textelse0人评价movies.append({rank:int(rank),title:title,rating:float(rating),people:people})returnmoviesdefcrawl_all():多线程爬取全部10页all_movies[]withThreadPoolExecutor(max_workers5)asexecutor:# 提交所有任务futures{executor.submit(scrape_page,page):pageforpageinrange(10)}# 收集结果forfutureinas_completed(futures):pagefutures[future]try:moviesfuture.result()all_movies.extend(movies)print(f第{page1}页完成当前共{len(all_movies)}条)exceptExceptionase:print(f第{page1}页失败:{e})# 按排名排序all_movies.sort(keylambdax:x[rank])returnall_movies运行importtime starttime.time()moviescrawl_all()print(f\n爬取完成共{len(movies)}条耗时{time.time()-start:.1f}秒)输出效果第1页完成当前共25条 第4页完成当前共50条 第2页完成当前共75条 第3页完成当前共100条 ... 爬取完成共250条耗时2.8秒三、数据统计分析爬完数据后用 pandas 分析一下importpandasaspd dfpd.DataFrame(movies)# 评分分布统计print(评分统计)print(df[rating].describe())# 评分最高的10部print(\n评分最高的10部)print(df.head(10)[[rank,title,rating]])# 评分区间分布print(\n评分区间分布)foriinrange(4,10):countlen(df[(df[rating]i)(df[rating]i1)])print(f{i}~{i1}分:{count}部)四、数据可视化matplotlib将统计结果生成图表直观展示数据分布importmatplotlib.pyplotaspltimportmatplotlib matplotlib.rc(font,familyMicrosoft YaHei)# 显示中文matplotlib.use(Agg)# 兼容无界面环境defplot_rating_distribution(df):评分分布柱状图# 统计各评分区间的电影数量bins[i/2foriinrange(16,22)]# 8.0 ~ 10.0labels[f{bins[i]:.1f}~{bins[i1]:.1f}foriinrange(len(bins)-1)]counts[]foriinrange(len(bins)-1):countlen(df[(df[rating]bins[i])(df[rating]bins[i1])])counts.append(count)plt.figure(figsize(10,6))barsplt.bar(labels,counts,color#1f77b4,alpha0.8)plt.title(豆瓣电影Top250 评分分布,fontsize16)plt.xlabel(评分区间,fontsize12)plt.ylabel(电影数量,fontsize12)# 在柱子上标出数字forbar,countinzip(bars,counts):plt.text(bar.get_x()bar.get_width()/2,bar.get_height()1,str(count),hacenter,fontsize10)plt.tight_layout()plt.savefig(豆瓣评分分布.png,dpi100)plt.close()print(图表已保存: 豆瓣评分分布.png)defplot_top10_bar(df):评分最高10部电影的横向柱状图top10df.head(10)plt.figure(figsize(10,8))barsplt.barh(top10[title],top10[rating],color#ff7f0e,alpha0.8)plt.title(豆瓣电影Top250 评分最高Top10,fontsize16)plt.xlabel(评分,fontsize12)plt.xlim(9.0,10.0)# 在柱子旁标出分數forbar,ratinginzip(bars,top10[rating]):plt.text(bar.get_width()0.02,bar.get_y()bar.get_height()/2,str(rating),vacenter,fontsize10)plt.tight_layout()plt.savefig(豆瓣评分Top10.png,dpi100)plt.close()print(图表已保存: 豆瓣评分Top10.png)完整主程序importtimeif__name____main__:starttime.time()# 1. 多线程爬取moviescrawl_all()print(f爬取完成共{len(movies)}条耗时{time.time()-start:.1f}秒\n)# 2. 转为 DataFramedfpd.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(keylambdax:x[rank])3. Matplotlib 中文乱码添加以下配置解决matplotlib.rc(font,familyMicrosoft YaHei)matplotlib.rc(axes,unicode_minusFalse)总结多线程爬虫 数据可视化是爬虫开发的进阶技能。多线程大幅提升爬取效率可视化让分析结果一目了然。这套思路不仅适用于豆瓣也适用于任何需要大规模数据采集展示的场景。如果对你有帮助欢迎点赞、评论、关注【张老师技术栈】持续分享 Java/Python/爬虫 实战干货。