
1. 项目概述一个开箱即用的B站数据抓取工具最近在分析一些视频内容趋势时经常需要批量获取B站上特定关键词下的视频信息比如标题、播放量、UP主、发布时间这些基础数据。手动一个个去翻不仅效率低而且很难做横向对比和趋势分析。市面上虽然有一些现成的工具或网站但要么功能受限要么有调用频率限制用起来总感觉隔靴搔痒。所以我花时间封装了一个Python爬虫脚本目标很明确输入关键词直接返回结构化的视频数据列表并且尽可能模拟真实用户行为降低被反爬机制干扰的风险。这个脚本的核心价值在于“可直接运行”。我见过太多爬虫教程只给个思路或者一堆零散的代码片段初学者照着做总会卡在环境配置、依赖安装或者某个诡异的报错上。我这个项目你只需要准备好Python环境安装我列出的几个库复制代码就能跑起来。它解决的就是从关键词到结构化数据这个“最后一公里”的问题特别适合需要做市场分析、内容调研、竞品跟踪或者单纯想批量下载自己感兴趣视频列表的朋友。脚本会处理请求头伪装、简单的异常重试以及数据清洗你拿到手的就是一个干净的DataFrame或者JSON文件可以直接导入Excel或数据库进行下一步分析。2. 核心思路与工具选型解析2.1 为什么选择B站公开接口而非网页爬取做B站数据抓取首先面临的技术路线选择是爬取网页HTML并解析还是寻找并调用其内部API接口。我优先选择了后者。原因很简单效率和稳定性。直接爬取search.bilibili.com的搜索结果页你需要面对不断变化的HTML结构以及可能存在的动态渲染内容虽然B站搜索页主要还是服务端渲染。更麻烦的是页面里掺杂了大量用于展示的样式、脚本标签解析起来既复杂又脆弱页面结构一调整你的解析规则就可能失效。而B站的移动端/内部API返回的是纯正的JSON数据结构清晰、数据纯净。通过浏览器的开发者工具F12在Network网络选项卡中筛选XHR/Fetch请求很容易就能找到搜索时触发的API。通常其URL模式类似于https://api.bilibili.com/x/web-interface/search/type?search_typevideokeyword你的关键词。这种接口返回的数据包含了视频列表、分页信息等直接就是程序友好的数据结构省去了大量解析HTML的麻烦也大大降低了后续的维护成本。2.2 关键工具库Requests, Pandas, 与JSON整个脚本的构建依赖于几个核心Python库选择它们是基于轻量、高效和易用的原则。Requests这是进行HTTP请求的绝对主力。相比于Python自带的urllibRequests的API设计非常人性化设置请求头、传递参数、处理响应都异常简单。我们需要用它来模拟浏览器发送GET请求到B站的搜索API。Pandas数据处理和分析的神器。我们的目标是将爬取到的视频信息结构化保存。Pandas的DataFrame对象非常适合用来存储和操作这种表格型数据。你可以轻松地对数据进行筛选、排序、去重并且一键导出为CSV、Excel等多种格式无缝对接后续的数据分析流程。JSONPython标准库中的json模块用于解析API返回的JSON字符串将其转换为Python的字典或列表方便我们提取所需字段。这里不选用Scrapy这样的大型框架是因为我们的需求相对聚焦——单点搜索和列表抓取。Scrapy更适合大规模、复杂的爬虫项目其学习曲线和项目复杂度对于这个“开箱即用”的目标来说有点过重了。用RequestsPandas的组合代码更直观依赖更少更容易让初学者理解和修改。2.3 反爬策略考量低调与模拟B站对爬虫有一定程度的防护。我们的策略不是硬碰硬而是“低调地模仿一个普通用户”。请求头Headers伪装这是最基础也是最关键的一步。我们需要在Requests的请求中添加一个完整的headers字典其中必须包含User-Agent模拟特定浏览器、Referer表明请求来源等字段。一个真实的、从浏览器复制过来的User-Agent能有效避免被服务器直接拒绝。请求参数Params模拟仔细观察浏览器调用API时携带的URL参数比如search_type、page、page_size等我们需要在代码中完整地复现这些参数让我们的请求看起来和浏览器发起的别无二致。请求频率控制这是体现“道德”和“可持续性”的关键。在循环请求分页数据时务必在每次请求之间加入随机延时例如使用time.sleep(random.uniform(1, 3))。猛烈、不间断的请求不仅会对B站服务器造成压力也极易触发IP限制或验证码。控制频率细水长流是爬虫能长期稳定运行的前提。异常处理与重试网络请求充满不确定性。代码中必须用try...except块包裹核心请求逻辑对连接超时、状态码异常如403、429等情况进行捕获。对于临时性错误可以实现简单的重试机制例如重试2-3次增强脚本的健壮性。3. 脚本核心细节与实操拆解3.1 环境准备与依赖安装确保你的电脑上安装了Python 3.6或更高版本。打开命令行终端CMD或Terminal使用pip安装以下必需的库pip install requests pandas如果安装速度慢可以考虑使用国内的镜像源例如清华源pip install requests pandas -i https://pypi.tuna.tsinghua.edu.cn/simple安装完成后可以创建一个新的Python脚本文件比如命名为bilibili_keyword_crawler.py。3.2 核心函数构建请求与解析数据脚本的核心是一个函数它负责发送请求、接收响应并提取数据。下面我拆解这个函数的关键部分。首先我们需要构建一个看起来像来自浏览器的请求头。你可以打开B站按F12进入开发者工具在任意一个请求的Headers标签页中找到Request Headers部分将其复制出来。import requests import json import pandas as pd import time import random def fetch_videos_by_keyword(keyword, page1, page_size20): 根据关键词抓取B站视频信息 :param keyword: 搜索关键词 :param page: 页码从1开始 :param page_size: 每页数量建议不超过50 :return: 包含视频信息的字典列表如果失败返回None # 1. 定义API URL (注意此URL为示例实际需从浏览器网络请求中获取最新有效地址) url https://api.bilibili.com/x/web-interface/search/type # 2. 准备请求参数 params { search_type: video, keyword: keyword, page: page, page_size: page_size, # 可能还有其他必要参数如‘order’排序、‘duration’时长等需根据实际情况补充 } # 3. 准备请求头这是反爬的关键 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, Referer: https://search.bilibili.com/, Accept: application/json, text/plain, */*, Accept-Language: zh-CN,zh;q0.9,en;q0.8, # 注意如果遇到问题可能需要添加‘Cookie’但初始尝试建议不带因为Cookie涉及登录状态且会过期。 } # 4. 发送GET请求 try: # 添加随机延时避免请求过快 time.sleep(random.uniform(1, 2)) response requests.get(url, paramsparams, headersheaders, timeout10) response.raise_for_status() # 如果状态码不是200抛出HTTPError异常 # 5. 解析JSON响应 data_json response.json() # 6. 检查API返回状态码 if data_json.get(code) ! 0: print(fAPI接口返回错误: {data_json.get(message)}) return None # 7. 提取视频列表 video_list data_json.get(data, {}).get(result, []) if not video_list: print(f关键词 {keyword} 第{page}页未找到视频。) return None parsed_videos [] for item in video_list: # 根据实际API返回的字段名进行提取这里字段名是示例必须与实际响应匹配 video_info { aid: item.get(aid), # 视频AV号 bvid: item.get(bvid), # 视频BV号 标题: item.get(title), UP主: item.get(author), 播放量: item.get(play), 弹幕量: item.get(video_review), 收藏量: item.get(favorites, 0), # 有些API可能不返回给默认值 投币数: item.get(coins, 0), 分享数: item.get(share, 0), 发布时间: item.get(pubdate), # 通常是时间戳 视频时长: item.get(duration), # 格式可能是 mm:ss 视频描述: item.get(description, )[:100], # 取前100字符 视频链接: fhttps://www.bilibili.com/video/{item.get(bvid)} } parsed_videos.append(video_info) return parsed_videos except requests.exceptions.RequestException as e: print(f网络请求失败: {e}) return None except json.JSONDecodeError as e: print(fJSON解析失败: {e}) return None注意上面的url和params中的字段名如‘result’以及video_info中提取的字段如‘play’,‘author’都是示例。B站的API接口和返回数据结构可能会发生变化。你必须亲自打开浏览器开发者工具搜索一个关键词找到真实的请求URL和响应数据格式然后据此修改代码中的字段名。这是爬虫工作最核心的一步无法绕过。3.3 分页抓取与数据整合单个API请求通常只返回一页数据比如20条。要抓取更多我们需要循环请求多个页码。def crawl_multiple_pages(keyword, max_pages5): 抓取多页搜索结果 :param keyword: 搜索关键词 :param max_pages: 最大抓取页数 :return: 所有视频的DataFrame all_videos [] for page_num in range(1, max_pages 1): print(f正在抓取关键词 {keyword} 第 {page_num} 页...) videos fetch_videos_by_keyword(keyword, pagepage_num) if videos is None: # 如果某一页失败或为空可能已无更多数据可以选择中断 print(f第 {page_num} 页抓取失败或为空停止抓取。) break all_videos.extend(videos) print(f第 {page_num} 页抓取成功获得 {len(videos)} 条数据。) # 每抓完一页等待稍长时间更友好 if page_num max_pages: time.sleep(random.uniform(2, 4)) if all_videos: # 使用Pandas创建DataFrame df pd.DataFrame(all_videos) return df else: print(未抓取到任何视频数据。) return pd.DataFrame() # 返回空DataFrame3.4 数据保存与导出抓取到的DataFrame可以方便地保存到文件。def save_to_file(df, keyword, formatcsv): 将DataFrame保存到文件 :param df: 包含视频数据的DataFrame :param keyword: 关键词用于生成文件名 :param format: 保存格式支持 csv, excel, json if df.empty: print(数据为空无需保存。) return # 清理文件名中的非法字符 safe_keyword .join([c for c in keyword if c.isalnum() or c in ( , _, -)]).rstrip() filename fbilibili_videos_{safe_keyword}_{int(time.time())} try: if format.lower() csv: filepath f{filename}.csv df.to_csv(filepath, indexFalse, encodingutf-8-sig) # utf-8-sig解决Excel打开中文乱码 print(f数据已保存到CSV文件: {filepath}) elif format.lower() excel: filepath f{filename}.xlsx df.to_excel(filepath, indexFalse) print(f数据已保存到Excel文件: {filepath}) elif format.lower() json: filepath f{filename}.json df.to_json(filepath, orientrecords, force_asciiFalse, indent2) print(f数据已保存到JSON文件: {filepath}) else: print(f不支持的格式: {format}) except Exception as e: print(f保存文件时出错: {e})3.5 主程序入口最后我们将所有功能串联起来形成一个完整的脚本。if __name__ __main__: # 用户输入关键词 search_keyword input(请输入要搜索的B站视频关键词: ).strip() if not search_keyword: search_keyword Python编程 # 默认关键词 # 设置抓取页数 pages_to_crawl 5 print(f开始抓取关键词 {search_keyword} 的前 {pages_to_crawl} 页数据...) # 执行抓取 video_df crawl_multiple_pages(search_keyword, max_pagespages_to_crawl) if not video_df.empty: print(f\n抓取完成共获得 {len(video_df)} 条视频数据。) # 显示前几行 print(video_df.head()) # 保存数据 save_format input(\n请选择保存格式 (csv/excel/json 默认csv): ).strip().lower() if save_format not in [csv, excel, json]: save_format csv save_to_file(video_df, search_keyword, formatsave_format) else: print(抓取失败未获得数据。)现在你只需要运行python bilibili_keyword_crawler.py按照提示输入关键词脚本就会自动抓取数据并保存到本地文件。4. 常见问题排查与实战技巧4.1 请求失败状态码403/412等这是最常见的问题意味着你的请求被服务器识别为非正常访问。检查请求头确保User-Agent是当前有效的、来自浏览器的字符串。Referer字段是否正确设置为B站搜索页的URL。有时可能需要添加Origin头。检查参数确认API的URL和所有参数特别是那些看起来像加密或动态生成的参数都与浏览器中捕获的一致。B站可能会在参数中加入sign之类的签名。尝试添加Cookie如果简单的请求头伪装无效可能需要携带登录后的Cookie。在浏览器已登录B站的状态下从开发者工具中复制Cookie字符串到请求头中。但请注意Cookie涉及个人账户安全且会过期公开分享的脚本一般不包含此部分。这是进阶操作需自行承担风险。降低请求频率立即停止脚本增加time.sleep的随机等待时间间隔比如调到3-5秒过一段时间再试。你可能触发了临时的频率限制。4.2 返回数据为空或字段对不上这通常是因为API接口或返回数据结构已经更新而你还在使用旧的解析逻辑。重新抓包这是唯一的解决方案。再次打开浏览器开发者工具清空网络记录进行一次新的搜索。找到正确的搜索API请求仔细查看其Query String ParametersURL参数和Response响应内容。更新代码根据新抓包得到的信息修改脚本中的url、params字典以及fetch_videos_by_keyword函数里解析item的字段名。务必保持完全一致。4.3 数据清洗与去重API返回的数据可能包含一些HTML标签如标题里的em高亮标签或者存在重复视频由于B站排序机制。清洗标题可以使用简单的字符串替换或正则表达式移除HTML标签。import re raw_title item.get(title, ) clean_title re.sub(r[^], , raw_title) # 移除类似emxxx/em的标签基于bvid或aid去重在将数据存入all_videos列表前可以检查该视频的ID是否已经存在避免重复。seen_ids set() for item in video_list: bvid item.get(bvid) if bvid and bvid not in seen_ids: seen_ids.add(bvid) # ... 解析并添加到列表4.4 提升抓取效率与稳定性使用Session对象requests.Session()可以复用TCP连接并在会话级别保持一些头信息能小幅提升连续请求的效率。session requests.Session() session.headers.update(headers) # 设置会话级headers # 然后在循环中使用 session.get(...)实现简单的代理池如果抓取量非常大可以考虑使用代理IP来分散请求防止单个IP被封锁。但这会引入代理IP稳定性和可用性的新问题对于一般规模的抓取并非必需。结构化日志将print语句替换为logging模块可以输出不同级别INFO, WARNING, ERROR的日志到文件方便后期排查问题。增量抓取如果你需要定期监控某个关键词可以设计脚本只抓取上次运行之后新发布的视频。这需要将已抓取的视频ID持久化存储如存入文件或数据库并在每次抓取时进行比对。这个脚本提供了一个坚实可靠的起点。网络爬虫的本质是与目标网站进行“博弈”其核心技能不仅仅是写代码更是观察抓包、分析数据结构和适应反爬策略的能力。希望这个“可直接运行”的脚本能成为你探索B站数据海洋的一把趁手工具同时也理解其背后的原理和边界。