1. 程式人生 > 其它 >微博話題下的資料爬取

微博話題下的資料爬取

技術標籤:python資料爬取

1、前言

新浪微博中,一個話題下各個媒體或使用者發表在平臺發表的資訊是輿情研究的一個很重要的資料來源,這裡記錄一下一個話題下資料的爬取方式,以“#美國疫情#”話題為例。

2、話題下資料爬取

首先參考這篇文章,分析話題下資料爬取的結構,然後仿照示例得到如下程式碼:

import requests
from urllib.parse import urlencode
from pyquery import PyQuery as pq
import time
import xlwt

#設定代理等(新浪微博的資料是用ajax非同步下拉載入的,network->xhr)
host = 'm.weibo.cn'
base_url = 'https://%s/api/container/getIndex?' % host
user_agent = 'Mozilla/5.0 (Linux; Android 5.0; SM-G900P Build/LRX21T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Mobile Safari/537.36'

#設定請求頭
headers = {
    'Host': host,
    'Referer': 'https://m.weibo.cn/search?containerid=231522type%3D1%26q%3D%23%E7%BE%8E%E5%9B%BD%E7%96%AB%E6%83%85%23',
    'User-Agent': user_agent
}

# 按頁數抓取資料
def get_single_page(page):
    #請求引數
    params = {
        'containerid': '231522type=1&q=#美國疫情#',
        'page_type': 'searchall',
        'page': page
    }
    url = base_url + urlencode(params)
    try:
        response = requests.get(url, headers=headers)
        if response.status_code == 200:
            return response.json()
    except requests.ConnectionError as e:
        print('抓取錯誤', e.args)

# 解析頁面返回的json資料
global count
count = 0
def parse_page(json):
    global count
    items = json.get('data').get('cards')
    for item in items:
        item = item.get('mblog')
        if item:
            data = {
                'id': item.get('id'),
                'created': item.get('created_at'),
                'text': pq(item.get("text")).text(),  # 僅提取內容中的文字
            }
            yield data
            count +=1


if __name__ == '__main__':
    workbook = xlwt.Workbook(encoding='utf-8')# 建立一個表格
    worksheet = workbook.add_sheet('美國疫情')
    for page in range(1, 200):  # 瀑布流下拉式,載入200次
        json = get_single_page(page)
        results = parse_page(json)
        tmp_list = []
        print(count)
        for result in results: #需要存入的欄位
            worksheet.write(count, 0, label=result.get('created').strip('\n'))
            worksheet.write(count, 1, label=result.get('text').strip('\n'))

        time.sleep(1) #爬取時間間隔
        workbook.save('conv.xls')

3、展開全文解決方法

僅用以上方法爬取存在一個弊端:如果文章過長,介面上就會出現“展開全文”字樣,用此方法無法爬取到長文字。

觀察欄位,存在isLongText布林型欄位:

“展開原文”部分實際上是一個連結,組成方式為“https://m.weibo.cn/status/”+id,這個id是被爬取的文章對應的id,因此需要在爬取之前判斷是否為長文字,如果是,則需要根據響應的id跳轉到對應的頁面再次爬取,完善部分程式碼片段:

#長文字爬取程式碼段
def getLongText(lid): #lid為長文字對應的id
    # 長文字請求頭
    headers_longtext = {
        'Host': host,
        'Referer': 'https://m.weibo.cn/status/' +lid,
        'User-Agent': user_agent
    }
    params = {
        'id' : lid
    }
    url = 'https://m.weibo.cn/statuses/extend?' +urlencode(params)
    try:
        response = requests.get(url, headers=headers_longtext)
        if response.status_code == 200: #資料返回成功
            jsondata = response.json()
            tmp = jsondata.get('data')
            return pq(tmp.get("longTextContent")).text() #解析返回結構,獲取長文字對應內容
    except requests.ConnectionError as e:
        print('抓取錯誤', e.args)


# 解析頁面返回的json資料
global count
count = 0

'''
修改後的頁面爬取解析函式
'''
def parse_page(json):
    global count
    items = json.get('data').get('cards')
    for item in items:
        item = item.get('mblog')
        if item:
            if item.get('isLongText') is False: #不是長文字
                data = {
                    'id': item.get('id'),
                    'name': item.get('user').get('screen_name'),
                    'created': item.get('created_at'),
                    'text': pq(item.get("text")).text(),  # 僅提取內容中的文字
                    'attitudes': item.get('attitudes_count'),
                    'comments': item.get('comments_count'),
                    'reposts': item.get('reposts_count')
                }
            else: #長文字涉及文字的展開
                tmp = getLongText(item.get('id')) #呼叫函式
                data = {
                    'id': item.get('id'),
                    'name': item.get('user').get('screen_name'),
                    'created': item.get('created_at'),
                    'text': tmp,  # 僅提取內容中的文字
                    'attitudes': item.get('attitudes_count'),
                    'comments': item.get('comments_count'),
                    'reposts': item.get('reposts_count')
                }

            yield data
            count +=1

4、總結

話題下資料爬取相對簡單,且不涉及使用者登入,但資料爬取間隔過快,或者爬取次數過多,ip會被封掉,另外資料爬取工具也有很多,比如爬山虎不一定非要自己程式設計實現。