微博爬虫系列之关键词及指定用户博文爬取

前言

近期的一些微博爬虫内容,本篇主要将怎么根据关键词或指定用户进行博文爬取。

准备写的内容:

定向词及指定用户博文爬取方面,用的是微博网页版(https://weibo.cn)。对于微博网页版中相关博文的爬取,需要使用到cookies。这方面的爬取参考了github上的资源:

https://github.com/nghuyong/WeiboSpider

写的时候发现网页版的关键词检索接口已经不见了···可能是微博删除了网页版的接口吧···之后再看看怎么在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

image

接下来将第一页的内容爬下来看看长什么样。

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'))

这里要查看具体要定位到哪里,可以在网页上的源码进行定位,比如我们要定位到页数,找到页数所在的地方:

image

这里可以看到,总页数放在源码的这个地方:
在这里插入图片描述

在源码搜索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]')

选择其中一个博文来看


image

这里我们可以看到要的信息全在这里了,接着就按着控件抓信息。这里拿其中一个节点为例。

首先获取微博的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())

接下来就是博文的主体了:


image

博文方面的内容提取基本就是从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的大神~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容