Python实现单博主微博文本、图片及热评爬取

文章简介

经常刷微博的同学肯定会关注一些有比较意思的博主,看看他们发的文字、图片、视频和底下评论,但时间一长,可能因为各种各样的原因,等你想去翻看某个博主的某条微博时,发现它已经被删除了,更夸张的是发现该博主已经被封号。那么如果你有很感兴趣的博主,不妨定期将Ta的微博保存,这样即使明天微博服务器全炸了,你也不用担心找不到那些微博了。(自己的微博也同理哦。)

看网上一些微博爬虫,都是针对很早之前的微博版本,而且爬取内容不全面,比如长微博不能完整爬取、图片没有爬取或没有分类,已经不适用于对当下版本微博内容的完整爬取了。

本例主要基于Python3.6.2版本,能够实现对于单博主微博内容的完整爬取、编号整理和本地保存。

环境介绍

Python3.6.2/Windows-7-64位/微博移动端

实现目标

将微博上你感兴趣的博主微博(全部或过滤非原创等)内容获取,包括微博文本、图片和热评,文本和热评按编号存入txt文件中,图片按编号存入指定路径文件夹中。这样一来方便对你关注的微博信息进行定期保存以及后期的检索查阅,二来将这些数据获取后也可以对博主微博、评论等信息进行进一步的数据分析。

本例中获取数据保存在本地文件系统,如爬取数据量大,可考虑使用MongoDB等数据库,方便数据存储和检索查阅。

准备工作

一般来说同一网站,PC站的信息较为全面,但不易爬取,而移动端则相对来说比较简单,因此本例中选取移动端站点m.weibo.com作为入口来进行爬取。

进入所要爬取的博主的主页,以我关注的“博物杂志”为例,发现其主页url为:https://m.weibo.cn/u/1195054531?uid=1195054531&luicode=10000011&lfid=100103type%3D1%26q%3D%E5%8D%9A%E7%89%A9%E6%9D%82%E5%BF%97 

其中1195054531这段数字就是我们要找的uid,然后打开在浏览器中输入url: m.weibo.cn/u/1195054531 再次进入相同主页,这时候按F12打开谷歌开发者工具,点“Network”,因为移动端站点加载方式为异步加载,我们主要关注XHR下请求,点“XHR”,按F5刷新重新发送请求。这时候发现浏览器已经发送两个请求,第一个请求主要是为了获取一些和博主相关的介绍信息,而第二个请求就是为了获取第一页所有微博的信息,我们重点关注第二个请求。

点“Headers”,可以发现Request URL 、Cookie、Referer等我们需要的信息(Cookie信息这里采用手动获取方式,有效时间为几个小时不等,过期后需要手动重新获取一下), 其中Request URL为 https://m.weibo.cn/api/container/getIndex?type=uid&value=1195054531&containerid=1076031195054531 

后面通过观察发现,在这个url末尾加上&page=页数可以控制想要爬取的微博页数。 点“Preview”:

观察返回的json数据,cards下就是一条条微博的信息card。 点开mblog,可以获取详细的微博相关内容:

我们主要需要以下数据: ‘id’:微博编号 ‘text’:微博文本 ‘islongText’:判断该条微博是否为长微博 ‘bmiddle_pic’:判断该微博是否带有图片

点开某条具体微博,来到微博完整内容和评论页面,同理通过观察“Network中请求相关信息,可以发现该页面url为: https://m.weibo.cn/api/comments/show?id=3900009063730800&page=1 其中id后面数字即为我们前面获取的微博编号,page参数可控制微博页数,请求返回json格式数据如下:

其中’data’和’hotdata’分别为评论和热评数据。

实现逻辑

  1. 通过控制page参数获取每页微博的cards数据,其中包含各条微博的详细信息;
  2. 开始遍历每一页微博页,同时遍历每一页的每一个微博,期间进行如下操作:
  3. 判断是否为长微博,如不是获取文本信息,否则进入详细微博内容请求,获取文本信息,将文本信息写入txt文档;
  4. 判断微博是否带有图片,如有通过请求获取图片地址,遍历地址,将其链接写入txt文档,将图片保存到本地,如无图片结束;
  5. 通过微博评论请求,获取评论数据列表,遍历列表获得该微博下每一条评论并保存到txt文档中相应微博内容下; …… 直到遍历完每一条微博。

爬取过程

爬取结果

文件夹中为对应微博图片,txt文档中为爬取的微博文本、评论内容。

以爬取“博物杂志”第3条微博为例,原博内容如下:

Txt文本中微博文本和评论如下:

文件夹中对应图片如下:

相对来说可以比较方便地进行检索和查阅。   

代码实现

# -*- coding:utf-8 -*- ''' Created on 2018年3月9日 @author: ora_jason ''' from lxmlimport html import requests import json import re import os import time import urllib.request class CrawlWeibo: # 获取指定博主的所有微博cards的list defgetCards(self, id, page): # id(字符串类型):博主的用户id;page(整型):微博翻页参数 ii = 0 list_cards = [] while ii < page: ii = ii + 1 print('正在爬取第%d页cards' % ii) url = 'https://m.weibo.cn/api/container/getIndex?type=uid&value=' + id + '&containerid=107603' + id + '&page=' + str( ii) response = requests.get(url, headers=headers) ob_json = json.loads(response.text) # ob_json为dict类型 list_cards.append(ob_json['data']['cards']) # ob_json['data']['cards']为list类型 time.sleep(2) print('暂停2秒') # 爬完一页所有微博的cards后 停顿两秒 return list_cards# 返回所有页的cards # 获取某条微博的热门评论或评论的list defgetComments(self, id, page): # id(字符串类型):某条微博的id;page(整型):评论翻页参数 url = 'https://m.weibo.cn/api/comments/show?id=' + id + '&page=' + str(page) response = requests.get(url, headers=headers) ob_json = json.loads(response.text) list_comments = [] if 'data' in ob_json: if 'hot_data' in ob_json['data']: list_comments = ob_json['data']['hot_data'] else: list_comments = ob_json['data']['data'] return list_comments# 返回某条微博下评论 defgetAll(self, id, page, path): # id为博主uid,page为爬取页数,path为保存路径 list_cards = self.getCards(id, page) print('爬取页数为:' + str(len(list_cards)) + '\n' + 30 * '-') count_weibo = 1 page_weibo = 1 # 遍历当页所有微博,保存内容,并根据id查找输出热门评论 for cards in list_cards: for card in cards: if card['card_type'] == 9: # 过滤出微博 #if card['card_type'] == 9 and 'raw_text' not in card['mblog']: # 过滤出原创微博 print('正在爬取第' + str(page_weibo) + '页 第' + str(count_weibo) + '条card') mid = card['mblog']['id'] created_at = card['mblog']['created_at'] # 获取保存文本信息 if card['mblog']['isLongText'] == 'false': text = card['mblog']['text'] else: url = 'https://m.weibo.cn/statuses/extend?id=' + mid response = requests.get(url, headers=headers) ob_json = json.loads(response.text) # ob_json为dict类型 text = ob_json['data']['longTextContent'] tree = html.fromstring(text) text = tree.xpath('string(.)') # 用string函数过滤掉多余标签 # 输出微博文本 with open(path + 'weibo_crawl.txt', 'a', encoding='utf-8') as ff: ff.write('第' + str(count_weibo) + '条\n' + '*** 发布于 ' + created_at + ' ***' + '\n') ff.write(text + '\n') # 获取保存图片 if 'bmiddle_pic' in card['mblog']: image_path = path + str(count_weibo) # if os.path.exists(image_path) is False: os.mkdir(image_path) url_extend = 'https://m.weibo.cn/status/' + mid # 单条微博url res = requests.get(url_extend, headers=headers).text # str类型 imgurl_weibo = re.findall('https://.*large.*.jpg', res) # 用正则匹配到图片url x = 1 for iin range(len(imgurl_weibo)): temp = image_path + '/' + str(x) + '.jpg' # 将图片url添加到微博文本中 with open(path + 'weibo_crawl.txt', 'a', encoding='utf-8') as ff: ff.write('微博图片链接:' + imgurl_weibo[i] + '\n') print('正在下载该条微博 第%s张图片' % x) try: urllib.request.urlretrieve(urllib.request.urlopen(imgurl_weibo[i]).geturl(), temp) except: print("该图片下载失败:%s" % imgurl_weibo) x += 1 with open(path + 'weibo_crawl.txt', 'a', encoding='utf-8') as ff: ff.write(78 * '-' + '评论' + '>' + 78 * '-' + '\n') else: with open(path + 'weibo_crawl.txt', 'a', encoding='utf-8') as ff: ff.write(78 * '-' + '评论' + '>' + 78 * '-' + '\n') count_weibo = count_weibo + 1 # 根据微博id获取热门评论,并输出 list_comments = self.getComments(mid, 1) # 评论只需要访问第一页 print('正在爬取该条微博 评论') count_hotcomments = 1 for comment in list_comments: # like_counts = comment['like_counts'] # 点赞数 text = comment['text'] # 评论内容 tree = html.fromstring(text) text = tree.xpath('string(.)') # 用string函数过滤掉多余标签 name_user = comment['user']['screen_name'] # 评论者的用户名 # 输出评论数据 if count_hotcomments<len(list_comments): with open(path + 'weibo_crawl.txt', 'a', encoding='utf-8') as ff: result = str(count_hotcomments) + ': #' + name_user + '#' ff.write(result + '\n') ff.write(text + '\n\n') else: with open(path + 'weibo_crawl.txt', 'a', encoding='utf-8') as ff: result = str(count_hotcomments) + ': #' + name_user + '#' ff.write(result + '\n') ff.write(text + '\n') count_hotcomments = count_hotcomments + 1 with open(path + 'weibo_crawl.txt', 'a', encoding='utf-8') as ff: ff.write(78 * '-' + '<' + '评论' + 78 * '-' + '\n\n\n\n') time.sleep(2) print('暂停2秒\n') # 爬完一条微博的所有内容后 停顿两秒 page_weibo = page_weibo + 1 # 请求头,爬取新博主需更新Cookie和Referer headers = { 'Cookie': '_T_WM=5a5b9ae925e458f93279d6708b159927; ALF=1523107054; SCF=Ativ2ybI8StjZccoSRca_uyzfWFIcM45JHEaLQ_tD8ksmi6-whOM5Pl1p8Vz4EziyMQe5QgrSlo8RY9Nd3NiFO8.; SUB=_2A253pUizDeRhGeRG61EV9S_NwzuIHXVVZmj7rDV6PUJbktANLXD4kW1NTeA_GStZpY6CFmR1PzgN50YL186u9HbC; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9Wh2V9E8BT6Glu5-SxvF-MwO5JpX5K-hUgL.FozReheXSK2p1hM2dJLoI7D29PyXUGxXUsHE; SUHB=02M_R-ArnK_FEZ; WEIBOCN_FROM=1110006030; M_WEIBOCN_PARAMS=featurecode%3D20000320%26oid%3D3900009063730800%26luicode%3D10000011%26lfid%3D1076031195054531%26fid%3D1005051195054531%26uicode%3D10000011', 'Host': 'm.weibo.cn', # 'qq': 'MQQBrowser/26 Mozilla/5.0 (Linux; U; Android 2.3.7; zh-cn; MB200 Build/GRJ22; CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1', 'Referer': 'https://m.weibo.cn/u/1195054531', 'Upgrade-Insecure-Requests': '1', 'User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36', } crawl_weibo = CrawlWeibo() # 实例化爬虫类并调用成员方法进行输出 crawl_weibo.getAll('1195054531', 2, 'D:/weibo/') # 输入需要爬取用户uid,需要爬取微博页数,微博本地保存路径

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143

# -*- coding:utf-8 -*-'''Created on 2018年3月9日 @author: ora_jason'''from lxmlimport htmlimport requestsimport jsonimport reimport osimport timeimport urllib.request  class CrawlWeibo:# 获取指定博主的所有微博cards的listdefgetCards(self, id, page):  # id(字符串类型):博主的用户id;page(整型):微博翻页参数ii = 0list_cards = []while ii < page:            ii = ii + 1print('正在爬取第%d页cards' % ii)url = 'https://m.weibo.cn/api/container/getIndex?type=uid&value=' + id + '&containerid=107603' + id + '&page=' + str(                ii)            response = requests.get(url, headers=headers)ob_json = json.loads(response.text)  # ob_json为dict类型 list_cards.append(ob_json['data']['cards'])  # ob_json['data']['cards']为list类型time.sleep(2)print('暂停2秒')  # 爬完一页所有微博的cards后 停顿两秒return list_cards# 返回所有页的cards     # 获取某条微博的热门评论或评论的listdefgetComments(self, id, page):  # id(字符串类型):某条微博的id;page(整型):评论翻页参数url = 'https://m.weibo.cn/api/comments/show?id=' + id + '&page=' + str(page)        response = requests.get(url, headers=headers)ob_json = json.loads(response.text) list_comments = []if 'data' in ob_json:if 'hot_data' in ob_json['data']:list_comments = ob_json['data']['hot_data']else:list_comments = ob_json['data']['data']return list_comments# 返回某条微博下评论 defgetAll(self, id, page, path):  # id为博主uid,page为爬取页数,path为保存路径list_cards = self.getCards(id, page)print('爬取页数为:' + str(len(list_cards)) + '\n' + 30 * '-')count_weibo = 1page_weibo = 1# 遍历当页所有微博,保存内容,并根据id查找输出热门评论for cards in list_cards:for card in cards:if card['card_type'] == 9:  # 过滤出微博#if card['card_type'] == 9 and 'raw_text' not in card['mblog']:  # 过滤出原创微博print('正在爬取第' + str(page_weibo) + '页 第' + str(count_weibo) + '条card')                    mid = card['mblog']['id']created_at = card['mblog']['created_at']# 获取保存文本信息if card['mblog']['isLongText'] == 'false':                        text = card['mblog']['text']else:url = 'https://m.weibo.cn/statuses/extend?id=' + mid                        response = requests.get(url, headers=headers)ob_json = json.loads(response.text)  # ob_json为dict类型text = ob_json['data']['longTextContent']                    tree = html.fromstring(text)                    text = tree.xpath('string(.)')  # 用string函数过滤掉多余标签# 输出微博文本with open(path + 'weibo_crawl.txt', 'a', encoding='utf-8') as ff:ff.write('第' + str(count_weibo) + '条\n' + '***  发布于  ' + created_at + '  ***' + '\n')ff.write(text + '\n') # 获取保存图片if 'bmiddle_pic' in card['mblog']:image_path = path + str(count_weibo)# if os.path.exists(image_path) is False:os.mkdir(image_path)url_extend = 'https://m.weibo.cn/status/' + mid  # 单条微博urlres = requests.get(url_extend, headers=headers).text  # str类型imgurl_weibo = re.findall('https://.*large.*.jpg', res)  # 用正则匹配到图片urlx = 1for iin range(len(imgurl_weibo)):                            temp = image_path + '/' + str(x) + '.jpg'# 将图片url添加到微博文本中with open(path + 'weibo_crawl.txt', 'a', encoding='utf-8') as ff:ff.write('微博图片链接:' + imgurl_weibo[i] + '\n')print('正在下载该条微博 第%s张图片' % x)try:                                urllib.request.urlretrieve(urllib.request.urlopen(imgurl_weibo[i]).geturl(), temp)except:print("该图片下载失败:%s" % imgurl_weibo)                            x += 1with open(path + 'weibo_crawl.txt', 'a', encoding='utf-8') as ff:ff.write(78 * '-' + '评论' + '>' + 78 * '-' + '\n')else:with open(path + 'weibo_crawl.txt', 'a', encoding='utf-8') as ff:ff.write(78 * '-' + '评论' + '>' + 78 * '-' + '\n')count_weibo = count_weibo + 1 # 根据微博id获取热门评论,并输出list_comments = self.getComments(mid, 1)  # 评论只需要访问第一页print('正在爬取该条微博 评论')count_hotcomments = 1for comment in list_comments:# like_counts = comment['like_counts']  # 点赞数text = comment['text']  # 评论内容tree = html.fromstring(text)                        text = tree.xpath('string(.)')  # 用string函数过滤掉多余标签name_user = comment['user']['screen_name']  # 评论者的用户名# 输出评论数据if count_hotcomments<len(list_comments):with open(path + 'weibo_crawl.txt', 'a', encoding='utf-8') as ff:                                result = str(count_hotcomments) + ': #' + name_user + '#'ff.write(result + '\n')ff.write(text + '\n\n')else:with open(path + 'weibo_crawl.txt', 'a', encoding='utf-8') as ff:                                result = str(count_hotcomments) + ': #' + name_user + '#'ff.write(result + '\n')ff.write(text + '\n')count_hotcomments = count_hotcomments + 1with open(path + 'weibo_crawl.txt', 'a', encoding='utf-8') as ff:ff.write(78 * '-' + '<' + '评论' + 78 * '-' + '\n\n\n\n')time.sleep(2)print('暂停2秒\n')  # 爬完一条微博的所有内容后 停顿两秒page_weibo = page_weibo + 1  # 请求头,爬取新博主需更新Cookie和Refererheaders = {'Cookie': '_T_WM=5a5b9ae925e458f93279d6708b159927; ALF=1523107054; SCF=Ativ2ybI8StjZccoSRca_uyzfWFIcM45JHEaLQ_tD8ksmi6-whOM5Pl1p8Vz4EziyMQe5QgrSlo8RY9Nd3NiFO8.; SUB=_2A253pUizDeRhGeRG61EV9S_NwzuIHXVVZmj7rDV6PUJbktANLXD4kW1NTeA_GStZpY6CFmR1PzgN50YL186u9HbC; SUBP=0033WrSXqPxfM725Ws9jqgMF55529P9D9Wh2V9E8BT6Glu5-SxvF-MwO5JpX5K-hUgL.FozReheXSK2p1hM2dJLoI7D29PyXUGxXUsHE; SUHB=02M_R-ArnK_FEZ; WEIBOCN_FROM=1110006030; M_WEIBOCN_PARAMS=featurecode%3D20000320%26oid%3D3900009063730800%26luicode%3D10000011%26lfid%3D1076031195054531%26fid%3D1005051195054531%26uicode%3D10000011','Host': 'm.weibo.cn',# 'qq': 'MQQBrowser/26 Mozilla/5.0 (Linux; U; Android 2.3.7; zh-cn; MB200 Build/GRJ22; CyanogenMod-7) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1','Referer': 'https://m.weibo.cn/u/1195054531','Upgrade-Insecure-Requests': '1','User-Agent': 'Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36',} crawl_weibo = CrawlWeibo()  # 实例化爬虫类并调用成员方法进行输出crawl_weibo.getAll('1195054531', 2, 'D:/weibo/')  # 输入需要爬取用户uid,需要爬取微博页数,微博本地保存路径

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Python中文社区

Python 3.6实现单博主微博文本、图片及热评爬取

文章简介 经常刷微博的同学肯定会关注一些有比较意思的博主,看看他们发的文字、图片、视频和底下评论,但时间一长,可能因为各种各样的原因,等你想去翻看某个博主的某...

5916
来自专栏Kubernetes

Mesos+Marathon对比Kubernetes

本文是我之前在技术选型时给团队做的一次分享内容,做了一次相对全面的关于Kubernetes 1.2 和 Mesos 0.28 + Marathon 1.2的对比...

4238
来自专栏阮一峰的网络日志

网站开发人员应该知道的61件事

通常情况下,你需要把所有人的发言从头到尾读一遍。但是,Stack Overflow有一个很贴心的设计,它允许在问题下方开设一个wiki区,让所有人共同编辑一个最...

1614
来自专栏WeaponZhi

使用Python快速获取公众号文章定制电子书(一)

因为工作原因,小之停更了一段时间,发生了很多事,不过从今天开始,我将会满血复活。这篇文章将分享一个我最近写的 Python 相关的小 demo 。爬取某个公众号...

1344
来自专栏喔家ArchiSelf

老码农眼中的存储

存储,是我们码农每天都要打交道的事情,而当我们面对RAID,SAN,对象存储,分布式数据库等技术的时候,又往往似是而非,存储成了我们熟悉的陌生人。

1653
来自专栏IT派

Python库大全,建议收藏留用!

学Python,想必大家都是从爬虫开始的吧。毕竟网上类似的资源很丰富,开源项目也非常多。

1762
来自专栏吴裕超

防运营商劫持代码

运营商是指那些提供宽带服务的ISP,包括三大运营商中国电信、中国移动、中国联通,还有一些小运营商,比如长城宽带、歌华有线宽带。运营商提供最最基础的网络服务,掌握...

3873
来自专栏欧阳大哥的轮子

论MVVM伪框架结构和MVC中M的实现机制

一直都有人撰文吹捧MVVM应用开发框架,文章把MVVM说的天花乱坠并且批评包括iOS和android所用的MVC经典框架。这篇文章就是想给那些捧臭脚的人们泼泼冷...

933
来自专栏ytkah

保存一下dedecms数据库表和字段说明,方便日后查询

玩dedecms有一段时间,对它的字段不是很了解,在此做个记录,方便日后查询 dede数据库字段说明: dede_addonarticle 附加文章...

2704
来自专栏容器云生态

基于软件的方式实现RAID(冗余磁盘阵列)技术

        在摘要部分已经对raid进行了简单的介绍,而在实际生产中主要用的便是软件RAID和硬件RAID,同时由于硬件RAID的价钱比较贵,因而,越来越多...

3066

扫码关注云+社区

领取腾讯云代金券