爬取b站最火up主及其粉丝信息
-
编译环境:python3、Firefox Developer Edition
-
使用技术:bs4、json、selenium、pymysql
输出预览:
代码:链接: https://pan.baidu.com/s/1SSqMrMr_IVni-Tb6Yx40vQ 提取码: hnu6 使用前请修改数据库密码
配套视频教程:https://www.acfun.cn/v/ac14821412,b站不让发只能发a站
实现思路:
查看up主榜单
https://www.kanbilibili.com/rank/ups/fans
审查元素,可以发现up主的信息都是封装在a标签下的,a的href对应up主的空间
可以通过
a = soup.find('div', 'ups-list').find_all('a', limit=3)
查询up空间的url,limit来判断想要查询up的个数
想要进入up的空间,我们只需要将a中href用 ‘https:’ + href+’/video’ 拼接即可
查看up主空间
我们选定某个up主的空间 https://space.bilibili.com/326499679/video
我想要爬取这两块的内容,但是如果直接爬取,返回的数字都是0,我们需要用selenium先进行动态渲染再爬取数据
html = driver.execute_script("return document.documentElement.outerHTML") # 必须执行js
time.sleep(2)
一定要先休眠两秒等待js运行结束,不然爬到的数据依旧是0
使用渲染库动态爬取关注数、粉丝数、获赞数等
可以看到要爬取的粉丝数、播放数等都在p标签中,我们可以通过id或者class进行爬取
但是获赞数与播放数id、class都是一样的,我们只能用父节点下第几个元素的形式才能获取到
focus = soup.find('p', 'n-data-v space-attention').text # 关注数
fans = soup.find('p', 'n-data-v space-fans').text # 粉丝数
div = soup.find('div', 'n-statistics')
praise = div.contents[2].find('p', 'n-data-v').text # 获赞数
view = div.contents[3].find('p', 'n-data-v').text # 播放数
接下来爬分区:
可以看到分区信息都是在 div 下的 a 标签中,我不需要全部的分区,所有class=“active"我不需要,只要爬取class=”"的,返回的数据是 动画40,我把它拆分成动画,40的形式存入字典中
div = soup.find('div', id='submit-video-type-filter') #分区
a = div.find_all('a', attrs={'class': ''})
dict = {}
for each in a:lstrip = each.text.lstrip()dict[lstrip[0:2]] = int(lstrip[2:])
maxArea = max(zip(dict.values(), dict.keys()))
最后一句是库中方法,目的是返回数值最大的那个键,也就是投稿最多的分区
爬取up的个人信息
找到这个uri
里面返回的就是up主的信息
将curl转换成Python requests
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0','Accept': 'application/json, text/plain, */*','Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2','Origin': 'https://space.bilibili.com','Connection': 'keep-alive','Referer': 'https://space.bilibili.com/546195/fans/fans','Cache-Control': 'max-age=0',
}
params = (('mid', str(mid)),('jsonp', 'jsonp'),
)
response = requests.get('https://api.bilibili.com/x/space/acc/info', headers=headers, params=params)
str(mid))代表up主uid,通过以下方法获取json中的数据
json_obj = json.loads(up.text)up_mid = json_obj['data']['mid']name = json_obj['data']['name']sex = json_obj['data']['sex']sign = json_obj['data']['sign']level = json_obj['data']['level']birthday = json_obj['data']['birthday']title = json_obj['data']['official']['title']
爬取up主粉丝信息
该uri中返回了粉丝的json数据
只要构造请求头即可
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0','Accept': '*/*','Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2','Connection': 'keep-alive','Referer': href+'/fans/fans',}params = (('vmid', str(mid)),('pn', str(n)),('ps', '50'),('order', 'desc'),)response = requests.get('https://api.bilibili.com/x/relation/followers', headers=headers, params=params)
其中pn代表分页数,ps代表一页多少人,这些都可用通过python来修改
*注:每次请求该uri时必须先休眠5s左右,否则会封ip
json_obj = json.loads(r.text); #返回json格式for entry in json_obj['data']['list']:fans_mid = entry['mid']mtime = entry['mtime']uname = entry['uname']vip = entry['vip']['vipType']fansDetails = getUserDetails(fans_mid)json_obj = json.loads(fansDetails.text)sex = json_obj['data']['sex']level = json_obj['data']['level']print("uid:" + str(fans_mid), "关注时间:"+ time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(mtime)),"用户名:" + uname, "vip等级:" + viplevel(vip), "性别:"+sex, "账户等级:"+str(level))insertFans(str(up_mid),str(fans_mid), time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(mtime)),uname,viplevel(vip),sex,str(level))time.sleep(5) # 防止封ip