写在前面
本文是微博超级爬虫系列 2022 最新最全指南。
微博超级爬虫系列的仓库地址在 https://github.com/Python3Spiders/WeiboSuperSpider
之前的连载全部在公众号,但是由于爬虫时常修改,公众号发文只能修改一次,故开此篇,保持更新!
相关开源代码仅为相关数据研究用,不做任何加速,没有 ip 代理,请勿用作其他用途,由此产生的法律风险本人概不承担。
相关未开源代码均为 pyd 或者 pyc 文件(不了解 pyd 或者 pyc 文件的读者 点击这里,pyd 只兼容 win 系统,pyc 则可以兼容 win/mac/linux ,也没有任何加速或 ip 代理。
运行本项目前,请确保已经关闭了 vpn 或者加速器,或者代理模式设置成 PAC 模式。
运行本项目的任何 py 文件,由于相关语法限制,python 版本大于等于 3.6 即可,32 bit or 64 bit 均可。
运行本项目的任何 pyd or pyc 文件,由于特有文件限制,请确保 python 环境是 python3.6.6 64 bit。
只推荐 Pycharm IDE 运行本项目。
还有一点,需要注意的是,微博的唯一标识 id 有两种形式。纯数字和数字+字母形式,这两者可以相互转化。转化的代码在文末,点我直达,遇到相关问题时查阅即可。
更新日志
2022-08-29
1、微博话题爬虫新增用户认证类型字段:verify_typ,取值范围为:没有认证、黄 V 认证、红 V 认证、蓝 V 认证。
2、修复微博话题爬虫 IndexError 等错误,提升稳定性。
2023-02-11
1、微博用户爬虫 解决无法抓取自己微博和抓不全的 bug。
2、微博用户爬虫 新增图片相册下载功能。
2023-03-05
1、新增开源 评论微博爬虫 可实现某微博账号评论、转发和点赞过的微博信息。
单功能微博爬虫
只需要抓取一个用户的所有微博或文章,一个关键词或者话题的特定时间段的微博,一个位置签到的最新微博,特定微博的转发,评论,点赞等功能之一的读者,可以只参考此部分。
用户抓取系列
用户微博爬虫
见名知意,抓取一个用户的所有微博,针对 weibo.cn 站点,代码地址在 WeiboUserScrapy.py
运行代码需要安装的库罗列如下:
pip install requests
pip install lxml
pip install 成功了还报错 module not found?点击这里
csv 结果文件,保存在代码目录下的 user 文件夹中,保存字段格式如下:
字段名 |
解释 |
wid |
数字 + 字母格式的微博唯一标识,可与纯数字形式 id 互转, |
publish_time |
发布时间 |
content |
内容 |
image_urls |
图片链接,以英文空格分隔多个图片链接 |
weibo_link |
微博链接 |
forward_num |
转发数 |
comment_num |
评论数 |
like_num |
点赞数 |
is_origin |
是否是原创微博 |
origin_img_urls |
被转发微博图片链接 |
微博文章爬虫
在微博上发布的内容有的短文本+图片(也就是微博),还有文章等形式,微博文章爬虫爬取用户的所有文章。有文章标题,id,内容,发布时间,阅读数,评论数,点赞数,图片链接等字段或信息。
针对 weibo.com 站点,代码地址在 WeiboComPostSpider.py
cookie 获取及更多信息可以参考:爬取微博用户所有文章的爬虫
用户信息爬虫
微博用户信息爬虫指的是,根据微博用户 id,抓取用户的阳光信用、性别、地区、学校、公司等信息。
针对 weibo.com 站点,代码全部开源在 WeiboSuperSpider 的 github 仓库地址,功能独立版文件夹下,取名 WeiboUserInfoSpider
拿到代码后需要填一下 headers 里面的 cookie,随便打开 weibo.com 站点里一个人的主页,比如
1
| https://weibo.com/u/1764201374
|
也可以是
这种微博用户自定义形式,然后 F12 开始找 info 或者 detail 这两个 path 之一,复制它们的 cookie 即可。
有关该爬虫更多信息可以参考:超级方便的微博用户信息爬虫
用户搜索爬虫
微博用户信息爬虫是根据微博用户 Uid 来抓取公开的用户微博信息,但是很多时候,我们可能只知道这个用户的微博名字,并不知道 Uid,用户搜索爬虫就是完成从微博用户名到 Uid 的转换。
针对 weibo.com 站点,代码地址在 SearchUser.py
cookie 获取及更多信息可以参考:微博搜索用户爬虫
话题关键词系列
首先必须搞清楚微博的关键词、话题、超话这三者的区别。
首先 #buyixiao# 这个就是话题, 而 buyixiao 是关键词;使用关键词可以同时搜到同名话题,话题却不能搜到同名关键词。
再者,在搜索时,关键词可能会被拆分,例如 北京暴雨 可能会搜索到包含 北京 或 暴雨 的微博,不仅可能没连在一起,还有可能只包含其中一个;而话题在搜索时不会被拆分~
话题和关键词区别明了,下面看 关键词话题 和 超话的区别 。
本爬虫只只能抓取关键词或话题(2022/03/08 更新,也支持抓取超话,只需要在 keyword 后面加个 超话 就能抓取超话微博了,超话后续有精力再开发)。抓取保存的结果 csv 文件字段格式如下:
字段名 |
解释 |
mid |
纯数字形式的微博唯一标识,可与字母+数字形式 id 互转 |
publish_time |
发布时间 |
user_name |
微博作者名 |
user_link |
微博作者链接 |
content |
内容 |
image_urls |
图片链接 |
weibo_link |
微博链接 |
forward_num |
转发数 |
comment_num |
评论数 |
like_num |
点赞数 |
针对 weibo.com 站点,pyd 或者 pyc 获取及相关配置过程在 新版话题关键词爬虫发布 。
链接里相关配置流程可能更改,但是公众号文章无法多次修改,以最后获取的百度网盘链接里文件为准。
在这里补充一点,如果需要设置多个关键词同时抓取,按照以下格式设置 keyword:
“keyword”: “a b c”,
实践证明,不需要空格也行,效果相同,爬的结果是多个关键词且的关系。
如果需要设置抓取 日本地震 或者 美国地震,可以将关键词设置为以下两种之一,结果是一样的:
“keyword”: “(日本 or 美国) and 地震”
或者
“keyword”: “日本地震 or 美国地震”
也可以参考视频教程:
转评赞系列
针对指定微博的转发、评论和点赞信息抓取。
评论爬虫
评论数据尤为重要,先来讲讲评论抓取。
评论数据的抓取难度较大,针对一条 100w+ 评论的抓取,通常分为三个量级难度的抓取:0.1w、1w、10w。0.1w 指只能爬到几千条,10w 指的是能爬到几十万条。
本爬虫 针对 weibo.com ,能够保存的字段信息如下:
字段名 |
字段值 |
parent_comment_id |
父评论 id,为空说明该评论就是根评论 |
comment_id |
评论 id |
comment_time |
评论时间 |
comment_user_name |
评论者昵称 |
comment_user_link |
评论者主页链接 |
comment_content |
评论内容 |
comment_like_num |
评论点赞数 |
child_comment_num |
子评论数,为 0 说明该评论可能就是子评论 |
抓取数据量在第二量级,单条100w+ 的评论能抓到 1w-10w,当然,如果评论总量只有 10w,抓不到 10w。pyd 或者 pyc 获取及相关配置过程在 新版评论爬虫发布
链接里相关配置流程可能更改,但是公众号文章无法多次修改,以最后获取的百度网盘链接里文件为准。
如果想要免费抓取最高量级的评论,可以参考 单机单账号抓取了单条微博的 100w+ 评论
转发爬虫
抓取指定微博的抓发信息,针对 weibo.com,抓取保存的字段如下:
字段名 |
解释 |
mid |
纯数字形式的微博唯一标识,可与字母+数字形式 id 互转 |
publish_time |
发布时间 |
user_name |
微博作者名 |
user_link |
微博作者链接 |
content |
内容 |
weibo_link |
微博链接 |
forward_num |
转发数 |
like_num |
点赞数 |
pyd 或者 pyc 获取及相关配置过程在 新版转发爬虫发布
链接里相关配置流程可能更改,但是公众号文章无法多次修改,以最后获取的百度网盘链接里文件为准。
可以参考视频教程:
点赞爬虫
看名字以为是手动点赞机器人,其实只是抓取点赞信息信息。
针对 m.weibo.cn,只能抓到最新的几千条点赞信息。
代码地址在:WeiboLikeSpider.py 。
详细信息及配置过程可以参考:新版点赞爬虫 。
位置签到系列
针对 weibo.com,微博位置签到爬虫,只能抓取到最新 1000 余条,比较稳定,不多赘述,直接查看:新版位置签到爬虫 。
2022 年 5 月更新,发布的位置签到爬虫 pyd 不稳定或者失效,需要抓取大量签到数据私聊合作。
超话系列
1、抓取超话微博,同话题关键词系列,比如抓取李健超话,只需要将 keyword 设置为 李健超话 即可,其余配置一样。
2、针对 weibo.com,下载超话相册和抓取活跃粉丝,比较稳定,不多赘述,直接查看:【开源】微博超话相册下载及超话活跃粉丝抓取 。
多功能集成爬虫
主要是针对上一步输出结果为微博信息,想要作为下一步或评论或转发输入的批量抓取,比如抓取一个微博话题下的所有评论,抓取一个用户发博的所有评论…以及其他 DIY 功能,FT 导向,本部分仍在活跃更新
抓取话题下的所有评论
不只是话题,关键词也行,要求是输入为话题关键词爬虫的结果文件,输出为很多评论文件,也可合并之。
在上文所述[新版微博评论爬虫]中,只是针对单条微博的,如果是很多很多个微博需要爬评论,难道需要一个个输入 mid 和 uid 吗?考虑到这个问题,我特意写了个脚本,后,需要获取该话题下所有微博的评论,我们可以使用如下的 py 脚本代码自动构建视频中抓取评论所需要的 json 配置文件。
配套视频如下:
构建评论批量抓取配置文件的脚本附在文末,点我直达
抓取用户发布微博的所有评论
可以直接参考视频:
批量抓取转评赞
待更新…
加字段
给一些 csv 加上学校,地区,性别等字段,总的来说还是需要上文 用户信息爬虫,待更新。
下载图片
就是说,爬虫结果文件里面的图片链接怎么下载到本地,代码贴在文末,点我直达
微博自助抓取网站
站点地址:https://weibo-crawl-visual.buyixiao.xyz/
具体介绍,见诸公众号文章合集:#微博自助抓取网站
数据分析及可视化相关
可定制、一站式微博分析及可视化平台:WB_VIS
中国-省-市可钻入钻出地图、世界地图、动态排序柱状图、桑基图、旭日图、关系图、树图、矩形树图、弦图等类型图表在线生成工具:https://tools.buyixiao.xyz/ ,可上传数据,在线编辑生成图表下载~
微博评论情感分析
pip install snownlp
;然后文末的代码随取随用:点我直达
LDA
代码附在文末,点我直达 , 自行百度相关包安装过程,可能比较麻烦
时间序列可视化
直达文末代码
QA
为什么不做一个系统
以前尝试做过一个 GUI 系统,所有功能点击即可,后来微博改版,流程全部失效了,维护成本巨大,所以只关注基本功能。
为什么有些 pyd/pyc 是付费的
博主大厂全职,维护这个项目三年多,至少花费了我几百个小时的业余时间,为此熬过不少夜,废过不少食。所有设置了一些付费文章,3~9 块不等,算是对项目持续维护的激励,不是为文件付费,知悉。
遇到错误怎么办
可以先查看下方的 常见错误汇总(已从 qq docs 迁移至本文),没有找到答案的可以在本文文末评论。
使用 pyc 时的 Python 版本必须是 Python3.6.6 64 bit
运行报错 ImportError: bad magic number
,切换到 Python 3.6.6 64 bit 即可,Python 可以多版本共存,无需卸载当前 Python,多版本时,请确保你当前 Pycharm 使用的是 3.6.6 64 bit,有关于此的任何疑问,可以查询此文:Python 多版本共存问题
没有关闭 VPN
运行报错 requests.exceptions.SSLExcption
,关闭 vpn 即可。
json 文件配置错误
如果运行报错类似 json.decoder.JSONDecodeError: Expecting ':' delimiter: line 3 column 19 (char 843)
,原因是 json 文件你修改时把冒号逗号引号之类的分隔符搞丢了。
如果复制 cookie 到 json 文件里时有一些双引号造成 loadConfig 错误,那是因为复制 cookie 的时候右键选择 复制 而不是 copy value。
Pycharm 看不到 pyc 文件
这不是问题,正常,pycharm 默认不会索引显示 pyc 文件。
运行 id 转化 Unable to locate a Java Runtime
这是因为 pyexecjs 库需要 jdk,如果你电脑之前没配置过 java 环境的话,google 百度搜索 jdk配置教程配置一个即可。
话题爬虫问题汇总
原始文档
问题陈列如下:
- topic_config.json 文件设置注意点,小时前不用补足 0,月份和天前需要补足 0,注意单双引号,invalid character等
- 为什么爬了,但是只能爬几十条?
- 爬取显示 data is none?
- bad magic number
- 缺少字段 only_origin
以上这些问题在 原始文档 中都有详细解释。
只想爬根评论
把 json 配置中的 child_max_page 设置为 0 即可。
评论总是爬不全
以微博 https://weibo.com/1934183965/LqvYeCdBu 为例子,显示有 3w 条,实际上只能抓到几百条,有以下几个原因:
1、这条微博可能开启了评论精选,博主只选出了几百条,不信的话,去 weibo.com 或者手机上手动浏览下,你会发现只能看到几百条,去 weibo.cn 可能一条都看不到。
2、微博数据水分,互联网大厂常规操作,在此不做过多解释。
转发总是爬不全
道理同上。
附录代码
微博两种 id 相互转化代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
|
import execjs jspython = '''str62keys = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; /** * 10进制值转换为62进制 * @param {String} int10 10进制值 * @return {String} 62进制值 */ function int10to62(int10) { var s62 = ''; var r = 0; while (int10 != 0) { r = int10 % 62; s62 = this.str62keys.charAt(r) + s62; int10 = Math.floor(int10 / 62); } return s62; } /** * 62进制值转换为10进制 * @param {String} str62 62进制值 * @return {String} 10进制值 */ function str62to10(str62) { var i10 = 0; for (var i = 0; i < str62.length; i++) { var n = str62.length - i - 1; var s = str62.substr(i, 1); // str62[i]; 字符串用数组方式获取,IE下不支持为“undefined” i10 += parseInt(str62keys.indexOf(s)) * Math.pow(62, n); } return i10; } /** * id转换为mid * @param {String} id 微博id,如 "201110410216293360" * @return {String} 微博mid,如 "wr4mOFqpbO" */ function id2mid(id) { if (typeof (id) != 'string') { return false; // id数值较大,必须为字符串! } var mid = ''; for (var i = id.length - 7; i > -7; i = i - 7) //从最后往前以7字节为一组读取mid { var offset1 = i < 0 ? 0 : i; var offset2 = i + 7; var num = id.substring(offset1, offset2); num = int10to62(num); mid = num + mid; } return mid; } /** * mid转换为id * @param {String} mid 微博mid,如 "wr4mOFqpbO" * @return {String} 微博id,如 "201110410216293360" */ function mid2id(mid) { var id = ''; for (var i = mid.length - 4; i > -4; i = i - 4) //从最后往前以4字节为一组读取mid字符 { var offset1 = i < 0 ? 0 : i; var len = i < 0 ? parseInt(mid.length % 4) : 4; var str = mid.substr(offset1, len); str = str62to10(str).toString(); if (offset1 > 0) //若不是第一组,则不足7位补0 { while (str.length < 7) { str = '0' + str; } } id = str + id; } return id; }''' ctx = execjs.compile(jspython)
def mid2id(mid): return ctx.call('mid2id', mid)
def id2mid(id): return ctx.call('id2mid', id)
if __name__ == '__main__': global mid mid = 'L8J4vC6m7' id = mid2id(mid) print(id) id = '4655725672138197' mid = id2mid(id) print(mid)
|
下载图片代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
|
import pandas as pd df = pd.read_csv('易烊千玺V公益 - 文本.csv')
import os image_folder = 'image' if not os.path.exists(image_folder): os.mkdir(image_folder)
import hashlib import requests
headers = { 'authority': 'weibo.com', 'x-requested-with': 'XMLHttpRequest', 'sec-ch-ua-mobile': '?0', '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', 'content-type': 'application/x-www-form-urlencoded', 'accept': '*/*', 'sec-fetch-site': 'same-origin', 'sec-fetch-mode': 'cors', 'sec-fetch-dest': 'empty', 'referer': 'https://weibo.com/1192329374/KnnG78Yf3?filter=hot&root_comment_id=0&type=comment', 'accept-language': 'zh-CN,zh;q=0.9,en-CN;q=0.8,en;q=0.7,es-MX;q=0.6,es;q=0.5', 'cookie': 'weibo.com 登录后随便哪一个接口的 cookie', }
for index, row in df.iterrows(): print(f'index: {index + 1}/{df.shape[0]}') image_urls = row['img_urls'] if not type(image_urls) is float and len(image_urls) > 0: if image_urls == '无': print('无') continue image_url_list = image_urls.split(' ') for image_url in image_url_list: print(image_url) if "?" in image_url: image_url = image_url[:image_url.rindex("?")] image_spilt = image_url.rsplit('.', 1) image_path = os.path.join(image_folder, '{}.{}'.format(hashlib.md5(image_spilt[0].encode('utf-8')).hexdigest(), image_spilt[1])) if os.path.exists(image_path): continue with open(image_path, 'wb') as fp: response = requests.get(url=image_url, headers=headers) fp.write(response.content)
|
生成批量抓取评论的 json 配置代码
注意这份代码需要根据 话题爬虫(或者用户爬虫)的结果 CSV 作为前置条件,体现在代码的 data_path 这个变量处,注意修改成你电脑上结果 CSV 文件的真实路径~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
|
import json import pandas as pd limit = 100000 from time import sleep config_path = 'topic_comment_config.json' with open(config_path, 'r', encoding='utf-8-sig') as f: config_json = json.loads(f.read()) data_path = f'./topic/{config_json["keyword"]}.csv' def drop_duplicate(path, col="mid"): df = pd.read_csv(path) df.drop_duplicates(keep='first', inplace=True, subset=[col]) df = df[-df[col].isin([col])] df.to_csv(path, encoding='utf-8-sig', index=False)
def main(): drop_duplicate(data_path)
df = pd.read_csv(data_path)
config_json['comments'].clear()
for index, row in df.iterrows(): print(f'{index + 1}/{df.shape[0]}') comment_num = row['comment_num'] weibo_link = row['weibo_link'] try: comment_num = int(comment_num) except: comment_num = 0 if comment_num <= 0: print(f'\n\n {weibo_link} 没有评论,不加入配置 json \n\n') continue if '?' in weibo_link: weibo_link = weibo_link[:weibo_link.index('?')] uid = weibo_link[weibo_link.index('com') + 4:weibo_link.rindex('/')] mid = weibo_link[weibo_link.rindex('/') + 1:] config_json['comments'].append({ 'index': f'N{len(config_json["comments"])}', 'mid': mid, 'uid': uid, 'limit': limit, 'user_name': row['user_name'] })
config_json['comments_pos'] = 0
print(f"\n\n\n 共计 {len(config_json['comments'])} 条微博加入评论抓取队列... \n\n\n") sleep(3)
with open(config_path, 'w', encoding='utf-8-sig') as f: f.write(json.dumps(config_json, indent=2, ensure_ascii=False))
if __name__ == '__main__': main()
|
上述代码所需 topic_comment_config.json 文件格式如下:
1 2 3 4 5 6 7 8
| { "cookie": "话题或评论的cookie", "keyword": "云南象2.6", "start_time": "2020-01-25-15", "end_time": "2020-01-25-17", "only_origin": false, "comments": [] }
|
评论情感分析代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
|
import pandas as pd
import re def filter_html(text): if len(text) == 0 or text == None: return text
content = re.sub("<[^>]*?>", "", text) return content
from snownlp import SnowNLP def sentiment_score(input_file, text_col = 'text'): df = pd.read_csv(input_file) sentiment_score_col = 'sentiment_score' is_scored_col = 'has_scored' df[is_scored_col] = [False for _ in range(df.shape[0])] for index, row in df.iterrows(): print(f'{index + 1}/{df.shape[0]}')
if row[is_scored_col] == True: continue
text = row[text_col] text = filter_html(text)
if len(text) == 0 or text == None: sentiment = -1 else: sentiment = SnowNLP(text).sentiments
df.loc[index, sentiment_score_col] = sentiment df.loc[index, is_scored_col] = True
df.to_csv(input_file, index=False, encoding='utf-8-sig') sentiment_score("待分析的.csv",text_col='文本所在 csv 的列名')
|
LDA 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
|
import pandas as pd
from sklearn.feature_extraction.text import CountVectorizer from sklearn.decomposition import LatentDirichletAllocation import pyLDAvis.sklearn
def doLDA(content, n_features=1000, n_topics=5, max_df=5, min_df=1): n_features = n_features
tf_vectorizer = CountVectorizer(strip_accents='unicode', max_features=n_features, stop_words='english', max_df=max_df, min_df=min_df) tf = tf_vectorizer.fit_transform(content)
n_topics = n_topics lda = LatentDirichletAllocation(n_components=n_topics, max_iter=50, learning_method='online', learning_offset=50., random_state=0) lda.fit(tf)
data = pyLDAvis.sklearn.prepare(lda, tf, tf_vectorizer)
print(data) pyLDAvis.show(data)
if __name__ == '__main__': df = pd.read_csv('comments.csv') doLDA(content=df['content'].values.tolist(),n_topics=2)
|
如果没有运行报错,但是出不来图,是因为需要挂 vpn。
时间序列可视化代码
可以使用配套的微博可视化网站:定制可视化
后话
本文首发: BuyiXiao’s Blog
链接地址:https://buyixiao.github.io/blog/weibo-super-spider.html
转载需注明来源。
如本项目对你有很多帮助,可以点击下方打赏赞助我持续维护。或者使用爱发电:https://afdian.net/@buyixiao