前言
近期的一些微博爬蟲(chóng)內(nèi)容,本篇主要將怎么根據(jù)關(guān)鍵詞或指定用戶進(jìn)行博文爬取。
準(zhǔn)備寫(xiě)的內(nèi)容:
- 微博熱門(mén)內(nèi)容及榜單的博文爬取 微博爬蟲(chóng)系列之微博榜單博文爬取
- 定向關(guān)鍵詞及指定用戶博文爬取 微博爬蟲(chóng)系列之關(guān)鍵詞及指定用戶博文爬取
- 博文評(píng)論爬取 微博爬蟲(chóng)系列之博文評(píng)論爬取
- 微博用戶信息爬取
定向詞及指定用戶博文爬取方面,用的是微博網(wǎng)頁(yè)版(https://weibo.cn)。對(duì)于微博網(wǎng)頁(yè)版中相關(guān)博文的爬取,需要使用到cookies。這方面的爬取參考了github上的資源:
寫(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。
接下來(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ù)所在的地方:
這里可以看到,總頁(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)看
這里我們可以看到要的信息全在這里了,接著就按著控件抓信息。這里拿其中一個(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)就是博文的主體了:
博文方面的內(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的大神~