
简介这是一份面向Python爬虫初学者与数据分析入门者的实战项目包围绕新浪微博数据抓取展开帮助读者掌握从HTTP请求、网页解析到数据落盘与反爬应对的完整链路。资源共77个文件以44个py源码为核心辅以14个md说明文档、11个html页面、2个yml配置、2个json样例及conf、txt等辅助文件压缩包约106KB结构紧凑、模块划分清晰。内容涵盖requests请求、BeautifulSoup解析、User-Agent与代理设置、验证码处理、异常捕获、CSV/JSON/SQLite存储以及Scrapy框架选型等关键知识点并附有配置样例与使用说明便于对照调试与二次开发。目前已有1187人学习下载适合希望以真实社交媒体场景练手、快速搭建可运行爬虫并理解合规抓取边界的读者参考。1. 从一份微博数据采集脚本说起它能帮你拿到什么如果你正在做舆情分析、社交网络研究或者只是想批量保存某个话题下的公开微博内容手动复制粘贴显然不现实。这份名为「新浪微博爬虫用python爬取新浪微博数据.zip」的资源核心就是一套用 Python 编写的微博数据采集脚本目标是把公开可见的微博正文、发布时间、点赞转发评论数、用户昵称与主页链接等字段按关键词或指定用户批量抓取下来最终落成 CSV 或 JSON 文件方便后续做词频统计、情感分析或可视化。它适合两类人一类是刚接触爬虫、想拿一个真实站点练手的新手另一类是做数据方向、需要快速拿到一批微博样本做实验的开发者。需要先说明的是这类脚本只能处理公开数据登录态、验证码、频率限制是绕不开的现实问题后面会专门讲怎么应对。2. 技术选型与目录结构requests lxml 的轻量组合2.1 为什么是 requests 而不是 Scrapy拿到这个包解压后你会发现它没有用 Scrapy 这种重型框架而是走requests lxml pandas的轻量路线。原因很直接微博的移动端接口返回的是 JSONPC 端搜索页返回的是 HTML两种数据结构混在一起用 Scrapy 的 Item Pipeline 反而绕。用 requests 手动控制请求头、Cookie 和重试逻辑调试时能一眼看清每一步拿到了什么。常见做法是把请求封装成一个函数统一处理 headers、超时和重试import requests import time import random HEADERS { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36, Referer: https://m.weibo.cn/, X-Requested-With: XMLHttpRequest, } def fetch(url, paramsNone, retry3): 带重试的 GET 请求失败后指数退避 for i in range(retry): try: resp requests.get(url, headersHEADERS, paramsparams, timeout10) if resp.status_code 200: return resp except requests.RequestException as e: print(f第{i1}次请求失败: {e}) time.sleep(2 ** i random.random()) # 退避 随机抖动 return None这里的retry控制最大重试次数2 ** i实现指数退避random.random()是防止多个请求同时重试触发风控。Referer必须带上否则移动端接口大概率返回 403。2.2 目录结构长什么样解压后的目录通常是这样组织的weibo_spider/ ├── config.py # Cookie、关键词、起始页码等配置 ├── spider.py # 主采集逻辑 ├── parser.py # HTML/JSON 解析函数 ├── storage.py # 写 CSV / JSON / SQLite ├── requirements.txt # 依赖清单 └── data/ # 输出目录config.py把所有可变参数集中管理改关键词、改页码、换 Cookie 只动一个文件。parser.py单独抽出来是因为微博页面结构经常变解析逻辑和请求逻辑分开后页面改版只需要改 parser不用动 spider。storage.py支持多种输出格式方便对接不同下游。2.3 依赖安装与配置项说明requirements.txt里一般就四五个包pip install requests lxml pandas openpyxlrequests发 HTTP 请求lxml解析 HTML比 BeautifulSoup 快pandas数据整理和导出 Excel/CSVopenpyxlpandas 写 xlsx 时的引擎config.py里几个关键配置需要你手动填配置项作用示例KEYWORD搜索关键词人工智能START_PAGE起始页码1END_PAGE结束页码50COOKIE登录后的 Cookie 字符串从浏览器开发者工具复制OUTPUT_FORMAT输出格式csv/jsonSLEEP_RANGE每次请求间隔秒数(2, 5)SLEEP_RANGE设成元组代码里用random.uniform(*SLEEP_RANGE)取随机值比固定 sleep 更不容易被识别。3. 核心采集链路从搜索接口到字段落库3.1 移动端搜索接口的请求构造微博移动端m.weibo.cn的搜索接口是这套脚本的主力数据源返回的是结构化 JSON比解析 PC 端 HTML 省事得多。核心请求长这样BASE_URL https://m.weibo.cn/api/container/getIndex def search_weibo(keyword, page): 按关键词搜索微博page 从 1 开始 params { containerid: f100103type1q{keyword}, page_type: searchall, page: page, } resp fetch(BASE_URL, paramsparams) if resp is None: return [] data resp.json() cards data.get(data, {}).get(cards, []) results [] for card in cards: # 搜索页里 card_type9 才是微博正文卡片 if card.get(card_type) 9: mblog card.get(mblog, {}) results.append(parse_mblog(mblog)) return resultscontainerid里的100103type1是搜索类型标识q后面跟关键词。page控制翻页一般到 50 页左右就会返回空数据这是接口的硬限制。card_type过滤很重要搜索页会混入广告卡、用户卡不过滤会拿到一堆脏数据。3.2 单条微博字段解析拿到mblog字典后需要把嵌套结构拍平import re def parse_mblog(mblog): 从 mblog 字典提取需要的字段 # 正文里的 HTML 标签要去掉 text re.sub(r[^], , mblog.get(text, )) return { mid: mblog.get(id), user_name: mblog.get(user, {}).get(screen_name), user_id: mblog.get(user, {}).get(id), content: text.strip(), created_at: mblog.get(created_at), reposts_count: mblog.get(reposts_count, 0), comments_count: mblog.get(comments_count, 0), attitudes_count: mblog.get(attitudes_count, 0), source: mblog.get(source), }text字段里带a标签和表情代码用正则去掉标签后还要处理\u200b这类零宽字符。created_at格式不统一有的是「刚刚」有的是「10分钟前」有的是完整日期入库前建议统一转成datetime。attitudes_count就是点赞数微博接口里叫「态度数」。3.3 翻页控制与去重写入翻页时最容易翻车的是重复数据——微博搜索接口在翻页时偶尔会返回上一页的内容。解决办法是用mid做去重import pandas as pd def crawl(keyword, start_page, end_page): seen set() all_items [] for page in range(start_page, end_page 1): items search_weibo(keyword, page) if not items: print(f第{page}页无数据提前结束) break for item in items: if item[mid] not in seen: seen.add(item[mid]) all_items.append(item) time.sleep(random.uniform(2, 5)) df pd.DataFrame(all_items) df.to_csv(fdata/{keyword}.csv, indexFalse, encodingutf-8-sig) return dfencodingutf-8-sig是为了 Excel 打开不乱码。seen集合在内存里维护数据量特别大时可以换成写 SQLite 用INSERT OR IGNORE。break逻辑是遇到空页就停避免无意义请求。4. 避坑与常见问题排查4.1 返回 403 或空数据现象请求返回 403或者 JSON 里ok字段为 0data为空。原因Cookie 过期、请求头缺失、或者 IP 被临时限制。解决重新从浏览器复制 Cookie确认Referer和User-Agent都带上。如果换了 Cookie 还不行把SLEEP_RANGE调大到(5, 10)等半小时再试。4.2 中文乱码现象CSV 用 Excel 打开全是乱码。原因to_csv默认用utf-8Excel 在中文系统下默认按 GBK 解析。解决写文件时加encodingutf-8-sig让 Excel 识别 BOM 头。如果已经写坏了用 pandas 读进来重新导出一次。4.3 翻到十几页就没数据了现象前 10 页正常第 15 页开始返回空。原因微博搜索接口对未登录用户有深度限制通常 10-20 页封顶。解决带上登录 Cookie 能翻到 50 页左右。如果还不够换关键词拆分时间段比如把「人工智能」拆成「人工智能 2024」「人工智能 2023」分别抓。4.4 字段缺失或为 None现象部分微博的user_name是 None或者content为空。原因转发的微博、被删除的微博、或者广告卡片混进来了。解决在parse_mblog里加过滤user字段不存在就跳过content为空也跳过。不要试图补全脏数据入库比少数据更麻烦。4.5 程序跑一半卡死现象脚本运行几分钟后不动了也不报错。原因requests.get没设timeout遇到慢响应会一直等。解决所有请求都加timeout10并在外层包try/except。fetch函数里的重试逻辑就是干这个的。5. 二次开发从能跑到好用5.1 加一个 SQLite 存储层CSV 适合小批量数据上十万条后每次读写都慢。我一般会加一个 SQLite 层用INSERT OR IGNORE天然去重import sqlite3 def init_db(pathdata/weibo.db): conn sqlite3.connect(path) conn.execute( CREATE TABLE IF NOT EXISTS weibo ( mid TEXT PRIMARY KEY, user_name TEXT, content TEXT, created_at TEXT, reposts_count INTEGER, comments_count INTEGER, attitudes_count INTEGER ) ) return conn def save_to_db(conn, items): conn.executemany( INSERT OR IGNORE INTO weibo VALUES (?,?,?,?,?,?,?), [(i[mid], i[user_name], i[content], i[created_at], i[reposts_count], i[comments_count], i[attitudes_count]) for i in items] ) conn.commit()mid做主键重复数据自动忽略。executemany比逐条execute快一个数量级。后续做分析直接pd.read_sql就行。5.2 把解析规则抽成配置微博页面结构会变把 CSS 选择器和 JSON 路径抽到config.py里改版时只改配置不改代码PARSE_RULES { search_card_type: 9, content_path: [mblog, text], user_path: [mblog, user, screen_name], time_path: [mblog, created_at], }然后在parser.py里写一个通用的get_by_path(dict_obj, path_list)函数按路径取值。这样即使接口字段名变了改一行配置就能恢复。5.3 验证改造是否生效改完存储层后跑一个最小验证抓 3 页数据检查 SQLite 里的行数是否等于去重后的mid数量再随机抽 5 条和网页上对比字段是否一致。我习惯在crawl函数末尾加一句print(f入库 {len(all_items)} 条去重后 {len(seen)} 条)两个数字对不上就说明去重逻辑有问题。5.4 一个容易忽略的细节created_at字段在移动端接口里可能是「刚刚」「10分钟前」「今天 14:30」这种相对时间。如果要做时间序列分析必须在入库前统一转成绝对时间。我一般写一个parse_time函数用datetime.now()做基准往前推。这个转换一旦漏掉后面做趋势图时全是坑。从那以后我每次改完采集脚本都强制先用 3 页数据跑一遍完整链路——请求、解析、去重、入库、导出确认每个环节的输出都对得上再放开页码跑全量。希望帮到你。本文还有配套的精品资源点击获取