JB的Python之旅-爬蟲篇-新浪微博內容爬取

前言

記得半個月以前的一晚,媳婦跟jb說,你看,蒼老師發了條微博,內容爲69,後來微博官方關閉了該條微博的評論功能~ css

雖然不知道69是什麼意義,可是看評論,總感受是要開車了~html

據說,蒼老師是90後啓蒙的一代,雖然沒有經歷過,但依然心存敬佩,因而乎,就像把蒼老師的微博內容都爬出來,看看老師都發了些什麼~

PC找內容

打開連接:
https://weibo.com/u/1739928273?refer_flag=1001030101_&is_all=1#_rnd1530254292380
打開後就是蒼老師的微博連接,能夠看到,下面就是蒼老師發佈的微博拉,而內容就是咱們想要的東西~json

像微博這種大廠,想都不用想就知道解析html獲取數據這條路是行不通的,那咱們F12 刷新下網頁,看看請求? segmentfault

嗯,好多js跟css,那咱們一個一個看,看看能不能找到有用的信息;

(5分鐘過去了)尼瑪,怎麼一條微博動態都沒看到,怎麼獲取?api

嘗試屢次,依然找不到解決方案,欲想放棄,此時,三三同窗說,用wap版,微博有接口獲取!!!瀏覽器

轉戰手機版

就這樣,轉戰手機版,手機版蒼老師連接以下:https://m.weibo.cn/u/1739928273;
老規矩,F12刷新網頁,而後把請求一條一條過,結果發現一個玩意:app

比起PC版,手機版終於看到有點相似數據的東西了,那咱們打開第一條看看~函數

這裏面的text不就是跟蒼老師的第一條微博是同樣的嗎?get~這就是咱們須要的東西啦~

那咱們點擊headers,把request url拿出來分析下: 優化

https://m.weibo.cn/api/container/getIndex?type=uid&value=1739928273&containerid=1076031739928273
複製代碼

解析這這個url,這url帶有3個參數:ui

type=uid
value=1739928273
containerid=1076031739928273
複製代碼

這3個參數,惟一能肯定的就是value,爲何這麼說?回頭看看蒼老師手機版的連接:https://m.weibo.cn/u/1739928273,由此得知,1739928273就是蒼老師微博的ID,不信, 你隨便改下試試,可能會跳到其餘老師那呢~

這不,簡單把最後2位73改爲12,就變成另外一位美女了~

貌似跑題了,咳咳,剛剛說到哪~

嗯,知道這幾個參數,沒啥特別的,那咱們試試滑動下屏幕,往下拉,拉取更多的數據,最後使用上面的方式,獲取url:

https://m.weibo.cn/api/container/getIndex?type=uid&value=1739928273&containerid=1076031739928273&page=3
複製代碼

與上面的url不一樣的是,這裏多了個參數page=3,不用想都知道,這是表明第三頁的意思了~
結果分析,若是是第二頁,page=2,第一頁的話,是不攜帶page參數,可是嘗試把page=0跟page=1兩個狀況,返回的數據跟不攜帶page參數是一直的,因此後面就把首頁當作是page=1的狀況處理~

ok,如今知道了數據在哪裏,翻頁怎麼弄,那咱們就看看請求頭把~

咦, Provisional headers are shown這是什麼,其餘請求內容沒看到?
網上找了說,是這麼解釋: 請求的資源可能會被(擴展/或其餘什麼機制)屏蔽掉。
更詳細的信息的話,請看:https://segmentfault.com/q/1010000000364871/a-1020000000429614

這個東西對於咱們有影響嗎?暫時看是沒有的,從上圖就能看到請求的內容跟攜帶的參數~

那咱們打開看看,下面這條連接是什麼內容?

https://m.weibo.cn/api/container/getIndex?type=uid&value=1739928273&containerid=1076031739928273
複製代碼

嗯,打開以後,是這樣的,wtf??這是什麼??

此時,趕忙看看蒼老師的內容:

內容的對應字段是text,那咱們copy去到剛剛那個頁面搜索下:

都變成了\u6211\u81ea\u5df這種玩意了~

這個問題,以前在寫urllib的時候也說明過:

URL只容許部分ASCLL(數字字母和部分符號),其餘的字符(包括漢字)是不符合URL的標準,
因此URL須要對這些字符進行URL編碼,URL編碼的方式是把須要編碼的字符轉化爲 %xx 的形式。
一般 URL 編碼是基於 UTF-8 的,函數說明也說起到給予UTF-8進行encode~
複製代碼

Ok,那就說,提取text的內容就好啦~那咱們先寫個請求吧

# -*- coding:utf-8 -*-
import requests

url = "https://m.weibo.cn/api/container/getIndex"
#請求的url

headers = {
    "Host": "m.weibo.cn",
    "Referer": "https://m.weibo.cn/u/1739928273",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36",
    "Accept":'application/json, text/plain, */*',
    "X-Requested-With":"XMLHttpRequest",
}
#請求頭

params = {
          "type": "uid",
          "value": "1739928273",
          "containerid": "1076031739928273",
          "page": "1"}
#請求攜帶的參數


res = requests.get(url,headers=headers,params=params).content
print(res)
複製代碼

執行後,獲得的結果是這樣的:

覈對了下,跟網頁訪問是同樣的~

https://m.weibo.cn/api/container/getIndex?type=uid&value=1739928273&containerid=1076031739928273&page=1
複製代碼

接下來能夠幹嗎?就能夠寫正則來匹配啦,先找data,而後找cards,而後再獲取每一個cards下面的text;

若是真的如上面說的,立刻寫噼裏啪啦寫正則,就有點衝動了,看下返回結果的格式,感受是否是像json?

"ok":1,"data":{"cardlistInfo":{"containerid":"1076031739928273","v_p":
複製代碼

沒錯,這就是json,那咱們就能夠換一種方式處理~

res = requests.get(url,headers=headers,params=params)
cards  = res.json().get("data").get("cards")
#獲取carads下的全部項
複製代碼

獲取的就是下面這個截圖的全部cards項:

那咱們看看cards裏面的內容,這是第一個:

這是第二個:

嗯,有發現不一樣了嗎?對的,就是有多了個關注XXX的一項,可是這一項,可是這一項不是咱們要的,那怎麼搞?
逐個逐個分析,會發現,正常的數據都有這麼一項:

那咱們就拿這項作判斷吧,先判斷這項是否等於9,而後再獲取mblog,再獲取text裏面的內容,因而乎就有了下面的代碼:

for card in cards:
    if card.get("card_type") == 9:
        text = card.get("mblog").get("text")
        print(text)
複製代碼

輸出的結果以下:

嗯,內容都獲取到了,可是有奇怪的東西進來了~

回頭看了下,並非代碼的錯,並且由於發佈的內容有帶圖片或者表情~

這種狀況過濾掉就行了~只須要文字~

pattern = re.compile(r"(.*?)<span.*>(.*?)")
text = re.sub(pattern,"",text)
複製代碼

從上面能夠看到問題例子以下,那咱們只須要把裏面的內容都幹掉就行了~

啊啊啊啊啊啊啊啊啊
<span class = "url-icon"><img alt = [允悲] src = "https://user-gold-cdn.xitu.io/2018/6/29/1644b1bf47628785?w=32&h=32&f=png&s=2591" style = "width:1em; height:1em;"/></span> 
複製代碼

結果以下:

這裏有個不解之謎,就是會看到,會有換行,緣由是這樣的:

這尼瑪,竟然有個換行符??那咱們把獲取到的text打印如下~

我去,這個換行符已經換行了,沒辦法匹配啊~原本還把想把\n換行符先幹掉了,這個就是這個換行符的來源,怎麼辦?

以前在介紹urllib的時候說起有,urllib有一個quote的方法,函數說明說起到給予UTF-8進行encode;

import urllib
kw = urllib.request.quote("很緊張啊啊啊啊↵<s")
print(kw)
複製代碼

輸出的內容長這樣的:

%E5%BE%88%E7%B4%A7%E5%BC%A0%E5%95%8A%E5%95%8A%E5%95%8A%E5%95%8A%E2%86%B5%3Cs
複製代碼

那咱們把中文都去掉,只留↵看看?

%E2%86%B5
複製代碼

獲得的結果就是這樣的,OK,那假如咱們把上面這串結果匹配成空格,是否是就能解決問題?

但實際嘗試了下,是不行的,那咱們就把數據打出來:

啊啊啊啊啊啊啊啊啊
<s
複製代碼

最後會發現%0A纔是那個回車符

%8A%0A%3Cspan
複製代碼

去掉以後,會發現字符的確不見了,並且的的確不會換行了,問題解決;

kw = urllib.request.quote(text)
    old_kw = re.sub("%0A","",kw)
    new_kw = urllib.request.unquote(old_kw)
複製代碼

回到正題,按照上面的代碼爬下來的東西,好像沒啥問題,但認真一看,咦~
這裏誰說老師下垂了?

對應的微博內容:

這裏,很明顯是以前的正則有問題,那咱們從新折騰下正則,此處過程就不說了,很痛苦。。最後改爲這樣:

pattern = re.compile(r"<.*?>")
複製代碼

意思就是把<>符號內的內容都去掉,得出的結果:

//@李太白的表哥:老師…你好像下垂了……
查看圖片
複製代碼

這裏能夠看到,查看圖片也是多餘的,那咱們也去掉,包括有一些是轉發微博的,也都幹掉吧,就成這樣了~

pattern = re.compile(r"<.*?>|轉發微博|查看圖片")
複製代碼

執行下,結果是這樣了,看上去很好:

//@李太白的表哥:老師…你好像下垂了……
複製代碼

ok,這個是一個頁面的內容抓取,總體代碼以下:

# -*- coding:utf-8 -*-
import requests
import re
import urllib


url = "https://m.weibo.cn/api/container/getIndex"
#請求的url

headers = {
    "Host": "m.weibo.cn",
    "Referer": "https://m.weibo.cn/u/1739928273",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36",
    "Accept":'application/json, text/plain, */*',
    "X-Requested-With":"XMLHttpRequest",
}
#請求頭

params = {
          "type": "uid",
          "value": "1739928273",
          "containerid": "1076031739928273",
          "page": "1"}
#請求攜帶的參數

res = requests.get(url,headers=headers,params=params)
cards  = res.json().get("data").get("cards")
#獲取carads下的全部項

for card in cards:
    if card.get("card_type") == 9:
        text = card.get("mblog").get("text")
        # kw = urllib.request.quote(text)
        # old_kw = re.sub("%0A","",kw)
        # new_kw = urllib.request.unquote(old_kw)
        # %0A  這串數字對應的就是這個回車字符
        pattern = re.compile(r"<.*?>|轉發微博|查看圖片")
        #這裏就是把<>符號內的都匹配出來
        text = re.sub(pattern,"",text)
        print(text)
複製代碼

其餘優化

既然一頁搞定了,那咱們要爬多頁,怎麼破?這個很簡單啦,直接改page參數就好了
另外還遇到一個問題:

假如咱們設定爬取1000頁,可是實際上,用戶可能只有100頁的數據,那腳本仍是會一直爬取的~
處理方案,加多一個參數,統計上一次的長度,若是相同,則認爲沒有新數據,暫停腳本處理

數據這多了,還發現這種東西~固然,也是正則兼容下就好了~

最終代碼

結果上面的處理,縫縫補補,最終代碼以下:

# -*- coding:utf-8 -*-
import requests
import re
import urllib
import codecs


url = "https://m.weibo.cn/api/container/getIndex"
#請求的url

headers = {
    "Host": "m.weibo.cn",
    "Referer": "https://m.weibo.cn/u/1761379670",
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36",
}
#請求頭

params = {
          "type": "uid",
          "value": "{uid}",
          "containerid": "{containerid}",
          "page":"{page}"}
#請求攜帶的參數


def get_Data(uid="3303658163", containerid="1005053303658163"):
    total = 3000  #打算爬取的頁數,好比100頁
    content = []  #存放獲取到的微博正文內容
    page =1  #頁碼,從第一頁開始算
    last_length = 0 #上一個的內容長度,用於對比上一次的整體內容長度跟此次是否一致,若是一致,則認爲沒有新數據,中止腳本處理
    for i in range(total):
        params["page"] = str(page)
        params['uid'] = uid
        params['containerid'] = str(containerid)
        res = requests.get(url, headers=headers, params=params)
        print(res.json().get("data"))
        cards = res.json().get("data").get("cards")
        # 獲取carads下的全部項


        for card in cards:
            if card.get("card_type") == 9:
                text = card.get("mblog").get("text")
                kw = urllib.request.quote(text)
                old_kw = re.sub("%0A","",kw)
                new_text = urllib.request.unquote(old_kw)
                # %0A  這串數字對應的就是這個回車字符
                pattern = re.compile(r"<.*?>|轉發微博|查看圖片|查看動圖|&gt;")
                #這裏就是把<>符號內的都匹配出來,正則規則
                text = re.sub(pattern,"",new_text)
                content.append(text)
        page +=1
        if (len(content) == last_length):
            print("已經獲取不到更多內容,腳本暫停處理")
            break
        else:
            last_length = len(content)
            print("抓取第{page}頁,目前總共抓取了 {count} 條微博".format(page=page, count=len(content)))
            with codecs.open('jb.txt', 'w', encoding='utf-8') as f:
                f.write("\n".join(content))


if __name__ == '__main__':
    get_Data("1761379670", "1005051761379670")
複製代碼

功能介紹的話,一路看下來就很明朗了,一句話就是,解析json而已;

可能有同窗問,上面的代碼如何使用?直接copy出來執行便可,若是想爬某人的信息,好比吉澤明步:

https://m.weibo.cn/u/2360092592?uid=2360092592&luicode=10000011&lfid=100103
type%3D1%26q%3D%E5%90%89%E6%B3%BD%E6%98%8E%E6%AD%A5
複製代碼

打開她的手機版微博主頁

而後把瀏覽器的F12,從新刷新下網頁,搜索get關鍵詞,從而得到value跟containerid,直接填寫到get_Data方法裏面便可~

最後的輸出結果以下:

說明

該腳本可能依賴於網頁結果,一旦網頁結構發生變化,該腳本即不適用,請了解~
嘗試過20個左右的用戶,都可數據,如遇到問題,請留言告知,謝謝~

感謝

本文感謝三三同窗的極力支持,不然如研究PC版,估計就涼了~

小結

本文主要解析怎麼爬取手機版的微博內容,主要原理是解析json,遇到有趣的問題有2個,第一是正則,想獲取什麼,把不須要的處理掉就行了,否則什麼都()去作,太麻煩了~第二,微博的換行符,一開始還想着\n匹配處理,結果發現不行,後來換個角度,弄成編碼的格式就發現問題了;

好了,本文到此,謝謝你們~

相關文章
相關標籤/搜索