ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

用Python爬取微博热搜TOP50并写入Excel的实战教程

用Python爬取微博热搜TOP50并写入Excel的实战教程 简介面向Python爬虫入门者、数据分析人员及需要采集社交媒体热点的开发人员该资源提供了一套抓取微博热搜并自动写入Excel表格的轻量方案可直接用于学习或改造。压缩包内共2个文件一个main.py爬虫脚本、一份热搜.xlsx结果样例包体大小仅7KB脚本覆盖requests发送请求、BeautifulSoup解析页面、pandas结构化导出等关键环节并加入时间戳记录、基础异常重试机制与清晰注释代码简洁便于理解二次修改。已有476人学习下载通过该案例可直观掌握静态网页爬取、HTML标签定位、数据清洗与Excel存储的完整流程拿到手即可运行查看输出效果也可将解析逻辑迁移到其他同类榜单场景。无论是用于课程设计、毕业项目还是个人工具均能快速上手是一份兼具教学与实用价值的爬虫入门素材。 很多人一开始学Python爬虫喜欢挑那种静态网页练手——博客文章列表、天气数据、豆瓣电影Top250。这些项目不是不好只是数据太死了页面结构规整得像教科书练完语法之后总觉得少了点真实感。我这两年给学生和朋友推荐练手项目时越来越倾向于选有动态变化、接口带真实业务字段、产出能直接拿出手的题目微博热搜TOP50写进Excel就是其中一个性价比很高的选择。这个项目能练到的东西很实在用requests发请求拿到JSON数据解析出热搜词和热度值再通过openpyxl生成一份带样式的Excel表格。整个流程没有绕弯每一步的产出都看得见摸得着而且数据是实时的上午抓和下午抓结果不一样做出来之后发给别人看对方一眼就能明白你在干什么。不管你是刚装好Python还没写过完整项目的新手还是想给数据分析找个真实动态数据源的爱好者这篇内容应该都有参考价值。下面我会从数据源分析、核心代码、Excel写入到踩坑排查完整讲一遍代码都是可以直接复制跑的。1. 选型环节为什么拿微博热搜做爬虫练手项目1.1 动态数据加稳定接口正好卡在合适难度练手项目最怕什么一怕数据太简单没有成就感二怕反爬太强劝退新手。微博热搜正好卡在中间数据每小时都在变属于典型的动态数据你需要理解接口返回什么就解析什么的思路但它又不像电商平台那样有复杂的风控体系公开的热搜接口连登录都不需要。另一个容易被忽略的点是热搜数据的字段设计非常贴近真实业务场景。一条热搜不但有词条名称还有热度值、标签类型热、沸、新、商等、分类信息。这些字段在写Excel时能直接对应列也方便后续按标签做颜色标记。相比那些只有标题和URL的静态页面处理起来更有数据分析的味道。我遇到过不少人一上来就用Selenium模拟浏览器去抓热搜页面这属于典型的杀鸡用牛刀。浏览器自动化框架要启动一个完整的浏览器实例内存占用高、速度慢而且一个页面结构变动就可能让整段代码失效。正确做法是找到页面背后那个返回JSON的接口直接请求它又快又稳。1.2 技术栈选择requests和openpyxl别一上来就整框架这个项目我建议只用两个库requests负责网络请求openpyxl负责生成Excel。不需要Scrapy不需要BeautifulSoup更不需要pandas。原因很简单数据量就50条用不上分布式爬虫那套东西接口返回的是JSON而不是HTML解析HTML的库也派不上用场。pandas虽然也能写Excel但它的to_excel底层还是调用openpyxl而且对单元格级别的样式控制反而不如直接用openpyxl方便。前提条件是你的本地环境装好了Python我用的是3.10版本3.8以上应该都没问题。编辑器推荐VSCode配Python插件装依赖就一行命令pip install requests openpyxl没有配过Python环境的话先去官网下载安装包安装时把Add Python to PATH勾上后面就能直接用pip了。这一步卡住的人不少但解决起来也就一分钟的事。2. 找数据源页面上的内容并不是抓取的最佳对象2.1 为什么我绕开了HTML解析如果你在浏览器里打开微博热搜页面按F12查看网页源码会发现热搜词条确实都在HTML里。但直接用requests抓这个页面再解析HTML会碰到两个问题。第一个问题是纯HTML解析非常脆弱。热搜列表嵌在多层div和span标签里你要写很长一串CSS选择器或者XPath去定位微博前端稍一改版选择器就失效代码就得重写。第二个问题是页面里有大量JavaScript动态渲染的内容requests拿到的HTML可能只是空壳真正数据是JS加载后再填进去的直接解析等于白忙。我在早期项目里吃过不少HTML结构变动的亏后来养成一个习惯凡是目标网站上存在Ajax接口的优先找接口而不是去啃页面源码。接口返回的是结构化数据字段名一目了然解析起来又简单又稳定。2.2 从网页抓包定位到JSON接口定位接口的方法很简单打开浏览器开发者工具F12切到Network网络面板刷新热搜页面然后在筛选栏里点XHR逐个查看请求找到名字里带hotSearch的那个Ajax请求。点开之后在Preview页签能看到完整的JSON返回里面有realtime、hotgov两个核心字段。realtime是一个数组每个元素就是一条热搜属性包括word热搜词条名称num热度值通常是数字label_name标签比如热沸新商category内容分类这个接口是公开的Ajax数据接口不需要携带登录Cookie只需要在请求头里带一个正常的User-Agent和Referer就能稳定拿到数据。我不建议在这个环节做逆向或者模拟加密参数微博的热搜数据本身就是给前端页面展示用的公开聚合数据不需要走破解那套思路。2.3 realtime和hotgov两类热搜数据的不同处理接口返回里除了realtime还有一个hotgov字段这是置顶的热搜位一般是重要通知或推广内容结构和realtime里的单条热搜不太一样。我在第一批代码里没注意到这个字段结果发现榜单第一位始终没有出现后来对比页面才反应过来是漏了置顶位。处理方式有两种一是把置顶位单独提取出来作为榜单第一条二是忽略置顶只取realtime里的实时热搜。我实际做的时候选了第二种因为题目要求是TOP50realtime本身就有50条左右加上置顶会让结构变复杂。但代码里我保留了对hotgov的判断逻辑万一以后想加改动成本很低。3. 核心抓取代码请求、解析和数值清洗3.1 一个能直接跑的抓取函数确定了数据源代码写起来就不复杂了。下面这个函数负责发起请求、解析JSON、返回热搜列表import requests def fetch_hot_search(): url https://weibo.com/ajax/side/hotSearch headers { User-Agent: ( Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 ), Referer: https://weibo.com/, } resp requests.get(url, headersheaders, timeout10) resp.raise_for_status() json_data resp.json() data json_data.get(data, {}) realtime data.get(realtime, []) hotgov data.get(hotgov, {}) return realtime, hotgov这里有个细节值得说一下headers里我只带了两个字段一个是User-Agent一个是Referer。UA是告诉服务器我是一个正常的浏览器Referer是告诉服务器我是从微博页面过来的。不带Referer的情况下偶尔会被拒绝访问加上之后基本就稳了。3.2 热度值从数字到万的处理逻辑接口返回的num字段是纯数字比如2337000这个数字代表热度值直接写进Excel不是不行但和微博页面上展示的233.7万相比可读性差了不少。所以我做了一个小函数统一处理def format_hot_value(num): num int(num) if num 10000: return f{num / 10000:.1f}万 return str(num)这个函数的逻辑很简单大于等于1万就除以1万保留一位小数后面加个万字小于1万就直接显示数字。实际用下来榜单里绝大多数热搜都是几十万到几百万的量级转换之后表格的视觉效果和微博App保持一致。有一点要注意num字段在JSON里有的情况下是整数有的情况下是字符串所以函数入口我先用int()做了一次强制转换避免在算除法时出现类型错误。3.3 让请求更稳定重试与异常处理网络请求再稳也有抖动的时候尤其公共接口偶尔会在高峰期响应超时。我直接写了一个带重试的包装函数连续请求3次每次失败等2秒再试保证脚本不会因为一次网络抖动就直接崩溃import time def fetch_with_retry(max_retry3): for attempt in range(max_retry): try: realtime, hotgov fetch_hot_search() if realtime: return realtime, hotgov except Exception as exc: print(f第{attempt 1}次请求失败{exc}) time.sleep(2) return [], {}这里的判空逻辑也很关键。有时候接口返回200但data字段是空的这种情况属于软失败如果不加if realtime这个判断后面写入Excel的就是一张空表。把请求和判空放在一起做重试效率和可靠性都能兼顾。4. 写进Excel表头、样式和超链接这些体验细节4.1 用openpyxl生成结构化表格数据抓到了下一步是把它们写进Excel。我直接用openpyxl从零创建工作簿表头固定为排名、热搜词、热度值、标签、抓取时间抓取时间用当前时间填充这样每次跑完都能知道这份数据是什么时候抓的。from datetime import datetime from openpyxl import Workbook from openpyxl.styles import Alignment, Font, PatternFill def save_to_excel(items, filename): wb Workbook() ws wb.active ws.title 热搜榜单 headers [排名, 热搜词, 热度值, 标签, 抓取时间] ws.append(headers) header_font Font(boldTrue, colorFFFFFF, size12) header_fill PatternFill(solid, fgColorFF8200) for cell in ws[1]: cell.font header_font cell.fill header_fill cell.alignment Alignment(horizontalcenter, verticalcenter) now_str datetime.now().strftime(%Y-%m-%d %H:%M:%S) for index, item in enumerate(items, start1): word item.get(word, ) num item.get(num, 0) label item.get(label_name, ) ws.append([index, word, format_hot_value(num), label, now_str]) width_map {A: 6, B: 42, C: 14, D: 8, E: 22} for col, width in width_map.items(): ws.column_dimensions[col].width width ws.freeze_panes A2 wb.save(filename)表头背景色我选了微博品牌色系里的橙色FF8200白色加粗字体。列宽按内容长度调整尤其是热搜词那一列给了42的宽度防止中文词条被挤成两行。freeze_panes A2的意思是冻结第一行往下滚动时表头始终可见这在表格超过一屏时体验特别好。4.2 按标签给热搜行标色热搜标签里有热沸新商这些状态光是写进标签列还不够直观。我在写入数据时对整行做了背景色标记用不同颜色区分热度级别label_colors { 热: FF4D4F, 沸: FF7A45, 新: 52C41A, 商: FAAD14, } for row in ws.iter_rows(min_row2, max_col5): label row[3].value if label in label_colors: fill PatternFill(solid, fgColorlabel_colors[label]) for cell in row: cell.fill fill我自己的习惯是热用红色、沸用橙色、新用绿色、商用金色。红色代表热度最高绿色代表新上榜金色代表商业推广。这样不用细看标签文字扫一眼颜色就知道榜单的构成。如果是做日报或者周报这个细节能省不少讲解时间。4.3 给热搜词加上微博搜索超链接还有一个让表格更好用的小招把热搜词单元格加上超链接指向微博搜索页面。Excel里点一下这个词就能跳到对应搜索结果比自己复制到浏览器再搜索方便太多。from urllib.parse import quote for row in ws.iter_rows(min_row2, max_col2): word row[1].value if word: search_url fhttps://s.weibo.com/weibo?q{quote(word)} row[1].hyperlink search_url row[1].style Hyperlink这里用urllib.parse.quote对中文词条做URL编码是因为微博搜索URL里不能直接放中文。这个细节不处理的话点击链接大概率跳到一个错误页面。5. 实际运行中踩过的坑与排查过程5.1 只带User-Agent还是被拦加Referer之后通了我第一次写这段代码时headers里只放了User-Agent结果请求发出去直接收到403。当时第一反应是被风控了还想着要不要换代理IP后来冷静下来对比了浏览器发出的请求头发现人家除了UA之外还带了Referer字段指向weibo.com。加上Referer: https://weibo.com/之后请求马上就通了。这个问题的本质是服务器会校验请求的来源页面纯爬虫工具发出的请求没有Referer容易被识别为异常访问。排查这类问题有个通用思路浏览器里能正常访问的接口用代码访问却被拦先对比请求头差异而不是急着上代理。5.2 Excel文件被占用导致PermissionError脚本跑第二次的时候我第一次遇到的报错是PermissionError: [Errno 13] Permission denied原因是刚才生成的Excel文件还开在桌面上没关。Windows系统下Excel打开文件时会锁定文件句柄openpyxl想覆盖保存同一个文件名就会失败。我的解决办法是文件名里带上时间戳每次运行生成一个带日期时间的新文件而不是固定写成weibo_hot_search.xlsx。这样既避免了文件占用冲突还能保留历史数据方便以后做对比分析。filename fweibo_hot_search_{datetime.now():%Y%m%d_%H%M%S}.xlsx5.3 Windows控制台打印中文报错的解决办法代码跑通之后我想在控制台顺便打印抓到的热搜词结果Windows的命令行窗口直接报UnicodeEncodeError特别是碰到热搜词里带emoji表情的时候报错概率几乎100%。这是Windows默认终端编码GBK和Python字符串编码UTF-8不一致导致的。最省事的修复方式是在脚本开头加一行import sys sys.stdout.reconfigure(encodingutf-8)这样强制标准输出用UTF-8编码控制台打印中文和emoji都不会再报错。顺带说一句这个报错只影响print不影响写入Excelxlsx格式内部本身就是UTF-8存储所以如果你不打印可能一直都不会碰到这个问题。5.4 接口数据偶发字段缺失时的防御运行几次之后我发现热搜条目里并非每条都带label_name字段有的词条这两个字段是空的。如果代码直接item[label_name]就会抛KeyError导致整个程序中断。解决办法是用item.get(label_name, )取不到就用空字符串兜底。这个习惯我后来在所有爬虫代码里都保持了下来解析JSON字段时一律用get而不是下标访问。谁也没法保证对方接口哪天少返回一个字段防御性写法能省掉一大半线上故障。6. 往后走从单次抓取到持续追踪的扩展思路6.1 定时抓取与增量数据积累这个脚本跑通之后我给它加了一个定时循环每隔半小时抓一次文件按时间戳命名一周之后就积累了几百份榜单数据。再往下就可以做时间序列分析对比同一个热词在一天内的热度变化曲线或者统计热搜标签里沸出现的频率分布。如果不想生成一堆文件也可以改成固定文件名追加写入。openpyxl在这种场景下要用load_workbook打开已有文件再在末尾追加行逻辑也不复杂。我更推荐时间戳文件的方案一是方便回溯二是避免文件锁问题。6.2 数据方向与合规尺度写爬虫这几年我的一个基本原则是抓取公开的聚合数据用于个人学习没问题但要注意频率控制单次抓取间隔1到2秒不做高并发循环不给对方服务器造成压力。像热搜这种本身就是公开给所有用户看的数据个人拿来做学习项目没问题但如果要做商业化的数据服务或对外展示就需要单独评估合规性。这个项目里涉及的字段只有热搜词和热度值不涉及任何用户个人信息属于最安全的爬虫类型。建议新手也尽量从这种公开聚合数据开始练习培养数据边界意识比研究各种突破风控的技巧重要得多。最后分享一个我在实际使用中很喜欢的组合把抓取频率调到每30分钟一次配合时间戳文件连续跑几天再用Excel打开最近几份表格对比能看到同一个热搜词从新变成热再到跌出榜单的完整过程。那种数据在眼前流动的感觉比单纯爬一个静态页面让人兴奋得多。整个项目从零到跑通大概只需要一个小时值得自己动手试一次。本文还有配套的精品资源点击获取
返回列表