前言
近期的一些微博爬虫内容,本篇主要将怎么根据关键词或指定用户进行博文爬取。
准备写的内容:
- 微博热门内容及榜单的博文爬取 微博爬虫系列之微博榜单博文爬取
- 定向关键词及指定用户博文爬取 微博爬虫系列之关键词及指定用户博文爬取
- 博文评论爬取 微博爬虫系列之博文评论爬取
- 微博用户信息爬取
定向词及指定用户博文爬取方面,用的是微博网页版(https://weibo.cn)。对于微博网页版中相关博文的爬取,需要使用到cookies
。这方面的爬取参考了github上的资源:
写的时候发现网页版的关键词检索接口已经不见了···可能是微博删除了网页版的接口吧···之后再看看怎么在pc端爬取。
这里先介绍怎么指定用户进行博文爬取吧···
指定用户博文爬取
指定用户的时候,需要有用户的用户id。通常用户id是一串数字,也有一些账号更改后是字符串,举个例子,何同学跟央视新闻的微博:
老师好我叫何同学:https://weibo.cn/6529876887
央视新闻:https://weibo.cn/cctvxinwen
在这里何同学的uid = 6529876887
,央视新闻的uid = cctvxinwen
。当然也可以获取到央视新闻以数字存储的id,之后再讲怎么获取,这里直接放出来就是uid = 2656274875
,点击可以发现确实是央视新闻的微博https://weibo.cn/2656274875
这个问题在爬取用户博文方面没有影响,不过在爬取用户信息时会有影响,后面写用户信息爬取再说这个情况怎么解决。
下面以央视新闻为例看看怎么爬用户的博文。
获取用户发布的所有博文网页
点击进入央视新闻的微博,可以看到这个账号发布了很多很多微博,在网页版观看就会显示很多页,那么要爬取的时候就要先获取他的页数。
当点击第二页时,会发现url
会变成https://weibo.cn/cctvxinwen?page=2。也就是说这个翻页是以page
这个字段进行翻页的,这就好办很多了。
将page
改成1
可以发现网页跳转到所有博文的第1页,接下来那我们就先获取到所有的页面url。
首先进入https://weibo.cn/cctvxinwen?page=1,打开开发者模式,在文件中找到自己的cookies
。
接下来将第一页的内容爬下来看看长什么样。
import random
import requests
import re
from lxml import etree
import time
headers = {
"cookie": cookies,
"user-agent": get_random_ua(),
}
user_id = 'cctvxinwen'
url_format = "https://weibo.cn/{}?page={}"
urls = []
url = url_format.format(user_id, 1)
resp = requests.get(url, headers=headers)
在网页开发者模式下,点开文件可以发现没有json格式的数据输出。因此这里不能直接通过解析json数据获取到页面数据。
这里就需要查看网页返回的文本信息了。这里再定位具体信息时,我用的是lxml
库里的etree
方法。
text = resp.text
tree_node = etree.HTML(resp.text.encode('utf-8'))
这里要查看具体要定位到哪里,可以在网页上的源码进行定位,比如我们要定位到页数,找到页数所在的地方:
这里可以看到,总页数放在源码的这个地方:
在源码搜索
mp
,发现这个控件就一个,因此我们就可以直接定位到页数,并通过节点的get
方法获得属性value
,接着就可以把所有页面的url
都拿到啦:
page_node = tree_node.xpath('//input[@name="mp"]')[0]
page = int(page_node.get('value'))
urls.append(url)
for i in range(2, page):
url = url_format.format(user_id, i) # 第一页的url
urls.append(url)
print('获取用户{}的url完毕,共{}页'.format(user_id, page))
获取用户博文
拿到用户的所有博文网页后,就可以进行博文的爬取了。这里每一页的数据是一样的,所以直接用第一页为例就可以了。同样的将页面数据爬下来:
url = 'https://weibo.cn/cctvxinwen?page=1'
resp = requests.get(url, headers=headers)
tree_node = etree.HTML(resp.text.encode('utf-8'))
还是在网页上看,定位到某一条博文,可以看到源码是这样子的:
可以看到第1页这里展示了11条博文(这个不一定),每条博文放在div class="c" id=""
的控件里,这里的id是对应的博文id,于是我们就可以拿到博文的控件:
tweet_nodes = tree_node.xpath('//div[@class="c" and @id]')
选择其中一个博文来看
这里我们可以看到要的信息全在这里了,接着就按着控件抓信息。这里拿其中一个节点为例。
首先获取微博的url以及微博id,这个从上面看到,可以从点赞、转发、评论处的链接获取,这里选择最简单的转发链接,对应的点赞数、转发数、评论数也可以顺便爬下来了:
tweet_node = tweet_nodes[0]
tweet_data = {}
tweet_repost_url = tweet_node.xpath('.//a[contains(text(),"转发[")]/@href')[0]
user_tweet_id = re.search(r'/repost/(.*?)\?uid=(\d+)', tweet_repost_url)
tweet_data['user_id'] = user_tweet_id.group(2) # 用户id
tweet_data['weibo_id'] = user_tweet_id.group(1) # 微博id
like_num = tweet_node.xpath('.//a[contains(text(),"赞[")]/text()')[-1]
tweet_data['like_num'] = int(re.search('\d+', like_num).group()) # 点赞数
repost_num = tweet_node.xpath('.//a[contains(text(),"转发[")]/text()')[-1]
tweet_data['repost_num'] = int(re.search('\d+', repost_num).group()) # 转发数
comment_num = tweet_node.xpath(
'.//a[contains(text(),"评论[") and not(contains(text(),"原文"))]/text()')[-1]
tweet_data['comment_num'] = int(re.search('\d+', comment_num).group()) # 评论数
接下来看下微博的创建时间,这里我们看到还有微博的来源,有一些可能会没有这个信息:
create_time_info_node = tweet_node.xpath('.//span[@class="ct"]')[-1]
create_time_info = create_time_info_node.xpath('string(.)')
if "来自" in create_time_info:
tweet_data['created_at'] = time_fix(create_time_info.split('来自')[0].strip()) # 微博发表时间
tweet_data['tool'] = create_time_info.split('来自')[1].strip() # 发布微博的工具
else:
tweet_data['created_at'] = time_fix(create_time_info.strip())
接下来就是博文的主体了:
博文方面的内容提取基本就是从github上搬过来的,对内容部分字符串进行了一些匹配清洗:
keyword_re = re.compile('<span class="kt">|</span>|原图|<!-- 是否进行翻译 -->|<span class="cmt">|\[组图共.+张\]')
emoji_re = re.compile('<img alt="|" src="//h5\.sinaimg(.*?)/>')
white_space_re = re.compile('<br />')
div_re = re.compile('</div>|<div>')
image_re = re.compile('<img(.*?)/>')
url_re = re.compile('<a href=(.*?)>|</a>')
def extract_weibo_content(weibo_html):
s = weibo_html
if 'class="ctt">' in s:
s = s.split('class="ctt">', maxsplit=1)[1]
s = emoji_re.sub('', s)
s = url_re.sub('', s)
s = div_re.sub('', s)
s = image_re.sub('', s)
if '<span class="ct">' in s:
s = s.split('<span class="ct">')[0]
splits = s.split('赞[')
if len(splits) == 2:
s = splits[0]
if len(splits) == 3:
origin_text = splits[0]
try:
retweet_text = splits[1].split('转发理由:')[1]
except:
retweet_text = ''
s = origin_text + '转发理由:' + retweet_text
s = white_space_re.sub(' ', s)
s = keyword_re.sub('', s)
s = s.replace('\xa0', '')
s = s.strip(':')
s = s.strip()
return s
tweet_html = etree.tostr
ing(tweet_node, encoding='unicode')
tweet_data['content'] = extract_weibo_content(tweet_html) # 微博内容
上面是比较简单的博文情况,有一些可能有图片、视频、转发等情况,这里直接放GitHub的做法,具体爬取方式是一样的,定位控件,找信息:
#### 图或视频
images = tweet_node.xpath('.//img[@alt="图片"]/@src')
if images:
tweet_data['image_url'] = images # 图片
for img in images:
fileName = '{}_{}_{}'.format(tweet_data['user_id'], user_name, tweet_data['weibo_id'])
try:
savePics(img, user_name, path = 'D:/课题工作/data/black/userContentPic')
except:
pass
videos = tweet_node.xpath('.//a[contains(@href,"https://m.weibo.cn/s/video/show?object_id=")]/@href')
if videos:
tweet_data['video_url'] = videos # 视频
#### 转发类的原始微博
repost_node = tweet_node.xpath('.//a[contains(text(),"原文评论[")]/@href')
if repost_node:
tweet_data['origin_weibo'] = repost_node[0] # 原始微博,只有转发的微博才有这个字段
#### 获取微博内容
all_content_link = tweet_node.xpath('.//a[text()="全文" and contains(@href,"ckAll=1")]')
if all_content_link:
all_content_url = base_url + all_content_link[0].xpath('./@href')[0]
count = 0
while count < 10:
try:
tmp_res = requests.get(all_content_url, headers=headers)
tmp_tree_node = etree.HTML(tmp_res.text.encode('utf-8'))
content_node = tmp_tree_node.xpath('//*[@id="M_"]/div[1]')[0]
tweet_html = etree.tostring(content_node, encoding='unicode')
tweet_data['content'] = extract_weibo_content(tweet_html)
break
except:
count += 1
if count == 10:
tweet_html = etree.tostring(tweet_node, encoding='unicode')
tweet_data['content'] = extract_weibo_content(tweet_html) # 微博内容
time.sleep(5)
else:
tweet_html = etree.tostring(tweet_node, encoding='unicode')
tweet_data['content'] = extract_weibo_content(tweet_html) # 微博内容
这篇又结束了!
到这里,指定用户的博文爬取就结束了,主要还是参考了GitHub的大神~