微博爬蟲(chóng)系列之關(guān)鍵詞及指定用戶博文爬取

前言

近期的一些微博爬蟲(chóng)內(nèi)容,本篇主要將怎么根據(jù)關(guān)鍵詞或指定用戶進(jìn)行博文爬取。

準(zhǔn)備寫(xiě)的內(nèi)容:

定向詞及指定用戶博文爬取方面,用的是微博網(wǎng)頁(yè)版(https://weibo.cn)。對(duì)于微博網(wǎng)頁(yè)版中相關(guān)博文的爬取,需要使用到cookies。這方面的爬取參考了github上的資源:

https://github.com/nghuyong/WeiboSpider

寫(xiě)的時(shí)候發(fā)現(xiàn)網(wǎng)頁(yè)版的關(guān)鍵詞檢索接口已經(jīng)不見(jiàn)了···可能是微博刪除了網(wǎng)頁(yè)版的接口吧···之后再看看怎么在pc端爬取。

這里先介紹怎么指定用戶進(jìn)行博文爬取吧···

指定用戶博文爬取

指定用戶的時(shí)候,需要有用戶的用戶id。通常用戶id是一串?dāng)?shù)字,也有一些賬號(hào)更改后是字符串,舉個(gè)例子,何同學(xué)跟央視新聞的微博:

老師好我叫何同學(xué):https://weibo.cn/6529876887
央視新聞:https://weibo.cn/cctvxinwen

在這里何同學(xué)的uid = 6529876887,央視新聞的uid = cctvxinwen。當(dāng)然也可以獲取到央視新聞以數(shù)字存儲(chǔ)的id,之后再講怎么獲取,這里直接放出來(lái)就是uid = 2656274875,點(diǎn)擊可以發(fā)現(xiàn)確實(shí)是央視新聞的微博https://weibo.cn/2656274875

這個(gè)問(wèn)題在爬取用戶博文方面沒(méi)有影響,不過(guò)在爬取用戶信息時(shí)會(huì)有影響,后面寫(xiě)用戶信息爬取再說(shuō)這個(gè)情況怎么解決。

下面以央視新聞為例看看怎么爬用戶的博文。

獲取用戶發(fā)布的所有博文網(wǎng)頁(yè)

點(diǎn)擊進(jìn)入央視新聞的微博,可以看到這個(gè)賬號(hào)發(fā)布了很多很多微博,在網(wǎng)頁(yè)版觀看就會(huì)顯示很多頁(yè),那么要爬取的時(shí)候就要先獲取他的頁(yè)數(shù)。

當(dāng)點(diǎn)擊第二頁(yè)時(shí),會(huì)發(fā)現(xiàn)url會(huì)變成https://weibo.cn/cctvxinwen?page=2。也就是說(shuō)這個(gè)翻頁(yè)是以page這個(gè)字段進(jìn)行翻頁(yè)的,這就好辦很多了。

page改成1可以發(fā)現(xiàn)網(wǎng)頁(yè)跳轉(zhuǎn)到所有博文的第1頁(yè),接下來(lái)那我們就先獲取到所有的頁(yè)面url。

首先進(jìn)入https://weibo.cn/cctvxinwen?page=1,打開(kāi)開(kāi)發(fā)者模式,在文件中找到自己的cookies。

image

接下來(lái)將第一頁(yè)的內(nèi)容爬下來(lái)看看長(zhǎng)什么樣。

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)

在網(wǎng)頁(yè)開(kāi)發(fā)者模式下,點(diǎn)開(kāi)文件可以發(fā)現(xiàn)沒(méi)有json格式的數(shù)據(jù)輸出。因此這里不能直接通過(guò)解析json數(shù)據(jù)獲取到頁(yè)面數(shù)據(jù)。

這里就需要查看網(wǎng)頁(yè)返回的文本信息了。這里再定位具體信息時(shí),我用的是lxml庫(kù)里的etree方法。

text = resp.text
tree_node = etree.HTML(resp.text.encode('utf-8'))

這里要查看具體要定位到哪里,可以在網(wǎng)頁(yè)上的源碼進(jìn)行定位,比如我們要定位到頁(yè)數(shù),找到頁(yè)數(shù)所在的地方:

image

這里可以看到,總頁(yè)數(shù)放在源碼的這個(gè)地方:
在這里插入圖片描述

在源碼搜索mp,發(fā)現(xiàn)這個(gè)控件就一個(gè),因此我們就可以直接定位到頁(yè)數(shù),并通過(guò)節(jié)點(diǎn)的get方法獲得屬性value,接著就可以把所有頁(yè)面的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) # 第一頁(yè)的url
    urls.append(url)

print('獲取用戶{}的url完畢,共{}頁(yè)'.format(user_id, page))

獲取用戶博文

拿到用戶的所有博文網(wǎng)頁(yè)后,就可以進(jìn)行博文的爬取了。這里每一頁(yè)的數(shù)據(jù)是一樣的,所以直接用第一頁(yè)為例就可以了。同樣的將頁(yè)面數(shù)據(jù)爬下來(lái):

url = 'https://weibo.cn/cctvxinwen?page=1'
resp = requests.get(url, headers=headers)
tree_node = etree.HTML(resp.text.encode('utf-8'))

還是在網(wǎng)頁(yè)上看,定位到某一條博文,可以看到源碼是這樣子的:


在這里插入圖片描述

可以看到第1頁(yè)這里展示了11條博文(這個(gè)不一定),每條博文放在div class="c" id=""的控件里,這里的id是對(duì)應(yīng)的博文id,于是我們就可以拿到博文的控件:

tweet_nodes = tree_node.xpath('//div[@class="c" and @id]')

選擇其中一個(gè)博文來(lái)看


image

這里我們可以看到要的信息全在這里了,接著就按著控件抓信息。這里拿其中一個(gè)節(jié)點(diǎn)為例。

首先獲取微博的url以及微博id,這個(gè)從上面看到,可以從點(diǎn)贊、轉(zhuǎn)發(fā)、評(píng)論處的鏈接獲取,這里選擇最簡(jiǎn)單的轉(zhuǎn)發(fā)鏈接,對(duì)應(yīng)的點(diǎn)贊數(shù)、轉(zhuǎn)發(fā)數(shù)、評(píng)論數(shù)也可以順便爬下來(lái)了:

tweet_node = tweet_nodes[0]
tweet_data = {}

tweet_repost_url = tweet_node.xpath('.//a[contains(text(),"轉(zhuǎn)發(fā)[")]/@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()) # 點(diǎn)贊數(shù)

repost_num = tweet_node.xpath('.//a[contains(text(),"轉(zhuǎn)發(fā)[")]/text()')[-1]
tweet_data['repost_num'] = int(re.search('\d+', repost_num).group()) # 轉(zhuǎn)發(fā)數(shù)

comment_num = tweet_node.xpath(
    './/a[contains(text(),"評(píng)論[") and not(contains(text(),"原文"))]/text()')[-1]
tweet_data['comment_num'] = int(re.search('\d+', comment_num).group()) # 評(píng)論數(shù)

接下來(lái)看下微博的創(chuàng)建時(shí)間,這里我們看到還有微博的來(lái)源,有一些可能會(huì)沒(méi)有這個(gè)信息:

create_time_info_node = tweet_node.xpath('.//span[@class="ct"]')[-1]
create_time_info = create_time_info_node.xpath('string(.)')
if "來(lái)自" in create_time_info:
    tweet_data['created_at'] = time_fix(create_time_info.split('來(lái)自')[0].strip()) # 微博發(fā)表時(shí)間
    tweet_data['tool'] = create_time_info.split('來(lái)自')[1].strip() # 發(fā)布微博的工具
else:
    tweet_data['created_at'] = time_fix(create_time_info.strip())

接下來(lái)就是博文的主體了:


image

博文方面的內(nèi)容提取基本就是從github上搬過(guò)來(lái)的,對(duì)內(nèi)容部分字符串進(jìn)行了一些匹配清洗:

keyword_re = re.compile('<span class="kt">|</span>|原圖|<!-- 是否進(jìn)行翻譯 -->|<span class="cmt">|\[組圖共.+張\]')
emoji_re = re.compile('<img alt="|" src="http://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('轉(zhuǎn)發(fā)理由:')[1]
        except:
            retweet_text = ''
        s = origin_text + '轉(zhuǎn)發(fā)理由:' + 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) # 微博內(nèi)容

上面是比較簡(jiǎn)單的博文情況,有一些可能有圖片、視頻、轉(zhuǎn)發(fā)等情況,這里直接放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 # 視頻

#### 轉(zhuǎn)發(fā)類的原始微博
repost_node = tweet_node.xpath('.//a[contains(text(),"原文評(píng)論[")]/@href')
if repost_node:
    tweet_data['origin_weibo'] = repost_node[0] # 原始微博,只有轉(zhuǎn)發(fā)的微博才有這個(gè)字段

#### 獲取微博內(nèi)容
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) # 微博內(nèi)容
            time.sleep(5)
else:
    tweet_html = etree.tostring(tweet_node, encoding='unicode')
    tweet_data['content'] = extract_weibo_content(tweet_html) # 微博內(nèi)容

這篇又結(jié)束了!

到這里,指定用戶的博文爬取就結(jié)束了,主要還是參考了GitHub的大神~

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容