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’分别为评论和热评数据。
实现逻辑
-
通过控制page参数获取每页微博的cards数据,其中包含各条微博的详细信息;
-
开始遍历每一页微博页,同时遍历每一页的每一个微博,期间进行如下操作:
-
判断是否为长微博,如不是获取文本信息,否则进入详细微博内容请求,获取文本信息,将文本信息写入txt文档;
-
判断微博是否带有图片,如有通过请求获取图片地址,遍历地址,将其链接写入txt文档,将图片保存到本地,如无图片结束;
-
通过微博评论请求,获取评论数据列表,遍历列表获得该微博下每一条评论并保存到txt文档中相应微博内容下; …… 直到遍历完每一条微博。
爬取过程
爬取结果
文件夹中为对应微博图片,txt文档中为爬取的微博文本、评论内容。
以爬取“博物杂志”第3条微博为例,原博内容如下:
Txt文本中微博文本和评论如下:
文件夹中对应图片如下:
相对来说可以比较方便地进行检索和查阅。
代码实现
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 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
# -*- 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,需要爬取微博页数,微博本地保存路径 |