html收集用户信息的标签,Python实现抖音网页端用户页面信息爬取

抖音的用户信息页的网址有3种形式,分别是:

https://v.douyin.com/GW5S6D/

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

https://www.douyin.com/share/user/88445518961

链接1是从客户端分享的短链接,在浏览器地址栏输入后重定向至链接2的形式。链接2和链接3很明显地把用户的UID显示出来,这个用户的UID为88445518961。

ce10d281382366bf061c1d1664f9de0d.png

8f9e1f631a57d2330c21ea951f942a88.png

目标:抓取用户名、保存用户头像、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.  抓取用户名

e87f48c5525dcbbd3ba2ce34a2536fa4.png

只要查找class="nickname"属性的标签

,标签内的内容即用户名

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

2. 保存头像

2747eb14011e2424b0b0e39b32f1017c.png

红色框内src的属性值就是头像的下载链接。同样通过find函数查找class="avatar"的标签

1275325161628778496.htm里另一属性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

fe1ac89fe3628e80c2f2ff5f15865fa4.png

UID的数据在“关注”按钮位置

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

4. 抖音ID

0486015f8e401d6e0be25d8831ae38e6.png

右边红色框内是用户的抖音id。抖音对数字进行一些处理,这是一种反爬虫的应对措施,使我们不能轻易获得数据。每个黑色方框代表一个数字,其实这些数字由特殊的字体控制着,一组Unicode码对应一个数字。

8ac746a62c8ba7b49902ec7fb2cd37d9.png

依次选中Network、Font,刷新网页,iconfont_9eb9a50.woff字体文件出现了。选中iconfont_9eb9a50.woff,出现下图的下载链接。把文件下载下来分析数字和Unicode码的映射关系

c3cc15927cbb0583e886214f4822d771.png

网上提到百度字体编辑器是一个非常好用的在线编辑器。这个例子里使用font creator查看

00d770706f75e83641e1e114b2219128.png

每一个数字由3个十六进制代码编码,具体查看网页源码的时候发现,每一次加载网页一个数字只由四位十六进制代码指定,如下图

0181bc92eea455dfcf2b5e0b2588d699.png

所以只需要找出所有的数字的映射关系就可以轻松地解码数字,利用词典能方便地实现。

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. 签名

f8f3d61974204ff88944640462d24f87.png

因为签名中的数字没有经过处理,所以直接获得即可。

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爬取抖音分享页用户信息的代码基本完成,但是在测试过程中发现一个错误。

d070e0e32280fb0d08abcb59bc6383a8.png

出现这个错误的位置是签名,原因是一些用户的个性签名很有个性,带有特殊符号,Tk不支持显示这种符号。

c6f42b8b9ffbae05c5447a49ce82e47f.png

一个不完美的方法是不管该符号,保留其他符号显示。 将用户名和签名等可能含有特殊符号的信息进行修改

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)

结果

2ed888c457c8b1f018177308fdeb53e1.png

不过,这个程序意义不大,除非可以高效获得大量用户UID,或者只是为了提取几个用户的数据变化。