import bs4 import json import time import pymysql import requests from bs4 import BeautifulSoup from selenium import webdriver from selenium.webdriver.chrome.options import Options def getPage(mid, n, href): headers = { 'User-Agent': 'Mozilla/5.0', 'Cookie': "", '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'), ) proxy = ["116.117.134.134", "112.80.248.73", "47.99.209.194", "1.181.48.68", "60.255.151.81", "202.108.22.5", "223.104.38.117"] i = 0 while True: print(i) if i < len(proxy): proxies = { 'https://': proxy[i] } response = requests.get('https://api.bilibili.com/x/relation/followers', proxies=proxies, headers=headers, params=params) if response.status_code == 200: break i = i + 1 if i + 1 == len(proxy): print("IP 全部失效") break return response def getUserDetails(mid): cookies = {'domain': '/', 'expires': 'false', 'httpOnly': 'false', 'name': 'buvid3', 'path': 'Fri, 29 Jan 2021 08:50:10 GMT', 'value': '7A29BBDE-VA94D-4F66-QC63-D9CB8568D84331045infoc,bilibili.com'} 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'), ) proxy = ["112.95.18.193", "112.80.248.73", "47.99.209.194", "1.181.48.68", "60.255.151.81", "202.108.22.5", "223.104.38.117"] i = 0 while True: print(i) if i < len(proxy): proxies = { 'https://': proxy[i] } response = requests.get('https://api.bilibili.com/x/space/acc/info', proxies=proxies, headers=headers, cookies=cookies, params=params) if response.status_code == 200: break i = i + 1 if i + 1 == len(proxy): print("IP 全部失效") break return response def getUpInfoBySelenium(href, mid): chrome_options = Options() chrome_options.add_argument('--headless') browser = webdriver.Chrome(executable_path="C:\Program Files (x86)\Google\Chrome\Application\chromedriver.exe") browser.get(href) try: html = browser.execute_script("return document.documentElement.outerHTML") soup = BeautifulSoup(browser.page_source, 'html.parser') focus = soup.find('p', 'n-data-v space-attention').text # 关注数 fans = soup.find('p', 'n-data-v space-fans').text # 粉丝数 print("关注数" + str(focus), "粉丝数" + str(fans)) finally: browser.close() def viplevel(vip): if vip == 0: vipname = '非会员' elif vip == 1: vipname = '会员' else: vipname = '大会员' return vipname def createDb(): #-------------------------------------------------------------------------------------------------- db = pymysql.connect(host='localhost', user='root', password='admin', port=3306) cursor = db.cursor(); sql = 'CREATE DATABASE bilibili' cursor.execute(sql) cursor.close() #-------------------------------------------------------------------------------------------------- db = pymysql.connect(host='localhost', user='root', password='admin', port=3306, db='bilibili') cursor = db.cursor() sql = 'CREATE TABLE IF NOT EXISTS up (id int(11) NOT NULL AUTO_INCREMENT, ' \ 'up_id VARCHAR(255) NOT NULL,up_name VARCHAR(255) NOT NULL, ' \ 'sex VARCHAR(10) NOT NULL, birthday VARCHAR(255),' \ 'focus VARCHAR(255),fans VARCHAR(255),area VARCHAR(255),' \ 'praise VARCHAR(255),view VARCHAR(255),' \ 'sign VARCHAR(255) NOT NULL,title VARCHAR(255) NOT NULL,' \ 'PRIMARY KEY (id,up_id))' cursor.execute(sql) db.close() #--------------------------------------------------------------------------------------------------- db = pymysql.connect(host='localhost', user='root', password='admin', port=3306, db='bilibili') cursor = db.cursor() sql = 'CREATE TABLE IF NOT EXISTS fans (id int(11) NOT NULL AUTO_INCREMENT,' \ 'up_id VARCHAR(255) NOT NULL,fans_id VARCHAR(255) NOT NULL,' \ 'fans_name VARCHAR(255) NOT NULL, sex VARCHAR(10) NOT NULL,' \ 'fans_level VARCHAR(10) NOT NULL,viplevel VARCHAR(255) NOT NULL,' \ 'time VARCHAR(255) NOT NULL,' \ 'PRIMARY KEY (id))' cursor.execute(sql) db.close() def insertUp(mid, name, sex, sign, birthday, title): db = pymysql.connect(host='localhost', user='root', password='admin', port=3306, db='bilibili') cursor = db.cursor() sql = 'INSERT INTO up(up_id,up_name,sex,sign,birthday,title) values(%s,%s,%s,%s,%s,%s)' val = (mid, name, sex, sign, birthday, title) try : cursor.execute (sql, val) db.commit() except: db. rollback () db.close() def insertFans(up_mid, fans_mid, time, uname, viplevel, sex, level): db = pymysql.connect(host='localhost', user='root', password='admin', port=3306, db='bilibili') cursor = db.cursor() sql = 'INSERT INTO fans(up_id,fans_id,fans_name,sex,fans_level,viplevel,time) values(%s,%s,%s,%s,%s,%s,%s)' val = (up_mid, fans_mid, uname, sex, level, viplevel, time) try: cursor.execute(sql, val) db.commit() except: db.rollback() db.close() if __name__ == '__main__': up_id = ["546195", "9824766", "777536", "321173469", "517327498", "122879", "20165629", "14110780", "62540916", "19577966"] for i in range(len(up_id)): href = "https://space.bilibili.com/" + str(up_id[i]) + "/video" up = getUserDetails(up_id[i]) #获取up主个人信息(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'] print("up主uid:"+str(up_mid), "用户名:"+name, "性别:"+sex, "留言:"+sign, "生日:"+birthday, "称号:"+title) # ------------------------------------------------ # print("开始 selenium") getUpInfoBySelenium(href, str(up_mid)) # 打印粉丝数 print("结束 selenium") # ------------------------------------------------ # print("粉丝数据:", end='') for j in range(1, 5): print("j:" + j) r = getPage(up_id[i], j, href) 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)) time.sleep(5) # 防止封ip
本文初始定义了几个UP主的id号,因为这几个UP主的粉丝量较大,故易获取粉丝信息
本文通过构造UP主的空间信息,获取粉丝量,并访问粉丝信息,通过python的pymsql库链接本地mysql进行数据存储,其中的SQL代码已经内嵌进去。对于B站高频访问会有IP限制,所以本文也采用了代理IP池的方法,不过本文并没有进一步通过构建时时IP池进行刷新IP,这一点受制于有效IP过少的限制,所以仅使用了几个IP进行替换。
另,B站的UP主粉丝信息并不能持续翻页,这点受制于B站网站的用户信息限制或本博主技术不到位,无法突破。故每个UP主的粉丝信息只能爬取几十页的粉丝目录。