天天看點

Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)

(已失效)

抖音的使用者資訊頁的網址有3種形式,分别是:

  1.  https://v.douyin.com/GW5S6D/
  2. https://www.iesdouyin.com/share/user/88445518961?sec_uid=MS4wLjABAAAAWxLpO0Q437qGFpnEKBIIaU5-xOj2yAhH3MNJi-AUY04&timestamp=1582709424&utm_source=copy&utm_campaign=client_share&utm_medium=android&share_app_name=douyin
  3. https://www.douyin.com/share/user/88445518961

連結1是從用戶端分享的短連結,在浏覽器位址欄輸入後重定向至連結2的形式。連結2和連結3很明顯地把使用者的UID顯示出來,這個使用者的UID為88445518961。

Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)
Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)

 目标:抓取使用者名、儲存使用者頭像、UID、抖音ID、簽名、關注數、粉絲數、贊數、作品數、喜歡數

0. 擷取HTML

使用requests獲得響應并利用BeautifulSoup來查找目标值,代碼如下

import requests
from bs4 import BeautifulSoup as bs

def getHtml(url):
    header = {"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60"}
    #try: 不必處理異常
    response = requests.get(url=url,  headers=header)
    return bs(response.text, 'lxml') # lxml需要安裝,可以選用html.parser
    #except: 不必處理異常
    #    print('網址錯誤')
    #    quit()

url = 'https://www.iesdouyin.com/share/user/88445518961?sec_uid=MS4wLjABAAAAWxLpO0Q437qGFpnEKBIIaU5-xOj2yAhH3MNJi-AUY04&timestamp=1582709424&utm_source=copy&utm_campaign=client_share&utm_medium=android&share_app_name=douyin'
soup = getHtml(url)
           

1.  抓取使用者名

Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)

隻要查找class="nickname"屬性的标簽<p>,标簽内的内容即使用者名

nickname = soup.find(name='p', attrs={'class': 'nickname'}).string
           

2. 儲存頭像

Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)

紅色框内src的屬性值就是頭像的下載下傳連結。同樣通過find函數查找class="avatar"的标簽<img>裡另一屬性src的值,下載下傳圖像後,檔案儲存在avatar目錄下,檔案名為nickname.jpeg。定義一個儲存頭像的函數

import os
def getAvatar(soup, nickname):
    avatarAddr = soup.find(name='img', attrs={'class': 'avatar'})['src']
    avatar = requests.get(avatarAddr)
    if not os.path.exists('avatar'): # 判斷目錄是否存在,如果不存在,則建立
        os.makedirs('avatar')
    try:
        with open('.\\avatar\\'+nickname+'.jpeg', 'wb') as img:
            img.write(avatar.content)
        print('頭像儲存成功 avatar\\'+nickname+'.jpeg')
    except:
        print('頭像儲存失敗')
           

tag[attr]傳回的是tag标簽内屬性為attr的值 

3. UID

Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)

UID的資料在“關注”按鈕位置

uid = soup.find(name='span', attrs={'class': 'focus-btn go-author'})['data-id']
           

 4. 抖音ID

Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)

右邊紅色框内是使用者的抖音id。抖音對數字進行一些處理,這是一種反爬蟲的應對措施,使我們不能輕易獲得資料。每個黑色方框代表一個數字,其實這些數字由特殊的字型控制着,一組Unicode碼對應一個數字。

Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)

依次選中Network、Font,重新整理網頁,iconfont_9eb9a50.woff字型檔案出現了。選中iconfont_9eb9a50.woff,出現下圖的下載下傳連結。把檔案下載下傳下來分析數字和Unicode碼的映射關系

Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)

 網上提到百度字型編輯器是一個非常好用的線上編輯器。這個例子裡使用font creator檢視

Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)

每一個數字由3個十六進制代碼編碼,具體檢視網頁源碼的時候發現,每一次加載網頁一個數字隻由四位十六進制代碼指定,如下圖

Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)

是以隻需要找出所有的數字的映射關系就可以輕松地解碼數字,利用詞典能友善地實作。

codeMap = {
            '\ue603': '0', '\ue60d': '0', '\ue616': '0',
            '\ue602': '1', '\ue60E': '1', '\ue618': '1',
            '\ue605': '2', '\ue610': '2', '\ue617': '2',
            '\ue604': '3', '\ue611': '3', '\ue61a': '3',
            '\ue606': '4', '\ue60c': '4', '\ue619': '4',
            '\ue607': '5', '\ue60f': '5', '\ue61b': '5',
            '\ue608': '6', '\ue612': '6', '\ue61f': '6',
            '\ue60a': '7', '\ue613': '7', '\ue61c': '7',
            '\ue60b': '8', '\ue614': '8', '\ue61d': '8',
            '\ue609': '9', '\ue615': '9', '\ue61e': '9'
          }
           

 定義一個函數實作把經過“加密”的字元串轉化成普通的字元串

def code2commStr(code):
    retStr = ''
    for c in code:
        if c in codeMap:
            retStr += codeMap[c]
        else:
            retStr += c
    return retStr
           

 再定義一個函數獲得含有這類數字的資訊

def getInfo(soup, tag, attr):
    element = soup.find(name=tag, attrs={'class': attr})
    return code2commStr(element.text.split())
           

find函數的傳回對象的string和text屬性有所不同。當find傳回的标簽含有子标簽時,string屬性的值為None,text屬性的值為純文字(包含空格)。把Unicode純文字以空格為間隔分割每一個字元,使用code2commStr函數轉化。為了隻提取ID而不要其他文字,把shortid以中文冒号(“:”)為分割,隻取後半部分。

shortid = getInfo(soup, 'p', 'shortid').split(':')[-1]
           

5. 簽名 

Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)

因為簽名中的數字沒有經過處理,是以直接獲得即可。

signature = soup.find(name='p', attrs={'class': 'signature'}).string
           

6. 關注數、粉絲數、贊數、作品數、喜歡數

這5個資料都含有經過處理的數字,和獲得抖音ID的方法相同,利用getInfo函數提取

focus = getInfo(soup, tag='span', attr='focus block')[:-2] # 關注
follower = getInfo(soup, tag='span', attr='follower block')[:-2] # 粉絲
likedNum = getInfo(soup, tag='span', attr='liked-num block')[:-1] # 贊
work = getInfo(soup, tag='div', attr='user-tab active tab get-list')[2:] # 作品
like = getInfo(soup, tag='div', attr='like-tab tab get-list')[2:] # 喜歡
           

為了提取數字本身,丢掉前(後)幾個字元(“關注”、“粉絲”、“贊”、“作品”、“喜歡”)。

至此,利用Python爬取抖音分享頁使用者資訊的代碼基本完成,但是在測試過程中發現一個錯誤。

Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)

出現這個錯誤的位置是簽名,原因是一些使用者的個性簽名很有個性,帶有特殊符号,Tk不支援顯示這種符号。

Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)

一個不完美的方法是不管該符号,保留其他符号顯示。 将使用者名和簽名等可能含有特殊符号的資訊進行修改

import sys
nonBmpMap = dict.fromkeys(range(0x10000, sys.maxunicode + 1), 0xfffd)
nickname = soup.find(name='p', attrs={'class': 'nickname'}).string.translate(nonBmpMap)
signature = soup.find(name='p', attrs={'class': 'signature'}).string.translate(nonBmpMap)
           

 完整代碼如下:

import requests
from bs4 import BeautifulSoup as bs
import os
import sys

codeMap = {
            '\ue603': '0', '\ue60d': '0', '\ue616': '0',
            '\ue602': '1', '\ue60E': '1', '\ue618': '1',
            '\ue605': '2', '\ue610': '2', '\ue617': '2',
            '\ue604': '3', '\ue611': '3', '\ue61a': '3',
            '\ue606': '4', '\ue60c': '4', '\ue619': '4',
            '\ue607': '5', '\ue60f': '5', '\ue61b': '5',
            '\ue608': '6', '\ue612': '6', '\ue61f': '6',
            '\ue60a': '7', '\ue613': '7', '\ue61c': '7',
            '\ue60b': '8', '\ue614': '8', '\ue61d': '8',
            '\ue609': '9', '\ue615': '9', '\ue61e': '9'
          }

def code2commStr(code):
    retStr = ''
    for c in code:
        if c in codeMap:
            retStr += codeMap[c]
        else:
            retStr += c
    return retStr
    
def getHtml(url):
    header = {"user-agent": "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36 OPR/26.0.1656.60"}
    #try: 不必處理異常
    response = requests.get(url=url,  headers=header)
    return bs(response.text, 'lxml') # lxml需要安裝,可以選用html.parser
    #except:
    #    print('網址錯誤')
    #    quit()

def getAvatar(soup, nickname):
    avatarAddr = soup.find(name='img', attrs={'class': 'avatar'})['src']
    avatar = requests.get(avatarAddr)
    if not os.path.exists('avatar'):
        os.makedirs('avatar')
    try:
        with open('.\\avatar\\'+nickname+'.jpeg', 'wb') as img:
            img.write(avatar.content)
        print('頭像儲存成功 avatar\\'+nickname+'.jpeg')
    except:
        print('儲存頭像失敗')
def getInfo(soup, tag, attr):
    element = soup.find(name=tag, attrs={'class': attr})
    return code2commStr(element.text.split())

url = 'https://www.iesdouyin.com/share/user/88445518961?sec_uid=MS4wLjABAAAAWxLpO0Q437qGFpnEKBIIaU5-xOj2yAhH3MNJi-AUY04&timestamp=1582709424&utm_source=copy&utm_campaign=client_share&utm_medium=android&share_app_name=douyin'

soup = getHtml(url)

nonBmpMap = dict.fromkeys(range(0x10000, sys.maxunicode + 1), 0xfffd)

nickname = soup.find(name='p', attrs={'class': 'nickname'}).string.translate(nonBmpMap)
getAvatar(soup, nickname)
shortid = getInfo(soup, 'p', 'shortid')
uid = soup.find(name='span', attrs={'class': 'focus-btn go-author'})['data-id']
signature = soup.find(name='p', attrs={'class': 'signature'}).string.translate(nonBmpMap)
focus = getInfo(soup, tag='span', attr='focus block')[:-2]
follower = getInfo(soup, tag='span', attr='follower block')[:-2]
likedNum = getInfo(soup, tag='span', attr='liked-num block')[:-1]
work = getInfo(soup, tag='div', attr='user-tab active tab get-list')[2:]
like = getInfo(soup, tag='div', attr='like-tab tab get-list')[2:]
print('使用者名:', nickname)
print('抖音ID:', shortid)
print('UID:', uid)
print('簽名:', signature)
print('關注數:', focus)
print('粉絲數:', follower)
print('贊數:', likedNum)
print('作品:', work)
print('喜歡:', like)
           

結果

Python實作抖音網頁端使用者頁面資訊爬取(已失效)(已失效)

不過,這個程式意義不大,除非可以高效獲得大量使用者UID,或者隻是為了提取幾個使用者的資料變化。