(1)chrome浏览器 下载地址:https://www.google.cn/chrome/,用于分析网结构。
jsonhandle 用于分析json格式化数据,下载地址:http://jsonhandle.sinaapp.com/
我用的版本是这个。
(2)python3.7 下载地址:https://www.python.org/,用于编写爬虫代码。
(3)mysql-8.0.22 下载与安装详见:
https://www.cnblogs.com/winton-nfs/p/11524007.html
用于存储数据,Navicat 15 for MySQL 下载地址:https://wws.lanzous.com/iUOl7ipybib
,数据库可视化工具,用于查看,管理mysql数据库。
从第三部分开始,就是我的主要思路了
首先打开一个用户的主页(我这次要爬取回形针的视频信息/doge),切换到 投稿视频 分栏
发现一共有168个视频,满满的干货有木有。此时,鼠标右击/ctrl+u查看网页源代码。
发现并没有我们需要的视频相关数据,初步断定,视频数据的加载方式为ajax异步加载,此时回到视频页,点击下一页,发现url在变化,但是源代码依然没有视频数据,右击点检查/F12 打开开发者工具,切换到network选项卡,刷新网页。
发现就在刷新这片刻,发起了91个请求,其中左侧为请求的网址,这么多数据,哪一个才是我们需要的呢?不慌,直接ctrl+f搜索即可。
搜索到了一个结果,右侧查看到response为json格式,查看请求头。请求地址为:
https://api.bilibili.com/x/space/arc/search?mid=258150656&ps=30&tid=0&pn=1&keyword=&order=pubdate&jsonp=jsonp
目测此链接为一个搜索接口,使用Josnhandle打开它发现存在我们需要的数据!
由此,可判断网页数据为ajax异步加载之后渲染出来的,那我们怎么构造这个网址呢?
在之前,我们分析了网页加载方式为异步加载,得到了接口地址,发现此接口需要以下参数:
ps:这里说一下我是怎么判断参数是否需要传递,我们拿到接口url时,以“?”分割此url,?前面的协议,路径是一定有的,?后面的部分为参数部分,他们的结构为key=value,参数和参数之间以&符号相连,而这些参数中,有的是可有可无的,所以我们可以通过适当删减url参数部分,精简url地址,详解参考
详解URL的组成
设想一下我们将数据入库时,需要选择数据表,可以简单定义为一些变量,但是为了便于区分,选择以用户名为数据表名来区分数据存储。
由于在刚才的视频接口地址不能直接拿到用户名,就退回来到用户主页,这是我们想到了网页源代码中存在用户名,那就简单了,直接请求用户主页,再提取他的名字即可。
但是,我选择了另外一种方法,就是查找用户名所在的接口,ctrl+f搜索一下
搜索到了三个结果,经过分析发现第一个为我们所需要的,地址为:
https://api.bilibili.com/x/space/acc/info?mid=258150656&jsonp=jsonp
仅需传输一个mid(用户id)即可,而mid在主页就能拿到。既然拿到了所有参数就可以开始撰写爬虫了。
import requests
import json
import pymysql
import time
import re
class Bilibili_User_Videos(object):
#初始化,要先创建一个数据库,之后的操作都在这个数据库中进行
def __init__(self):
user='root'
password='root'
self.conn=pymysql.connect('localhost',user=user,password=password,port=3306,database='BiliBili',charset="utf8")
self.cursor=self.conn.cursor()
self.headers={
'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36'}
#获取用户名,以这个用户名作为数据表名
def get_tablename(self):
base_url = f'https://api.bilibili.com/x/space/acc/info?mid={key}&jsonp=jsonp'
r = requests.get(base_url)
_json_data = json.loads(r.text)
table_name = _json_data.get('data').get('name')
return table_name
#程序解析主程序
def parse(self,tablename):
sql2 = f"""CREATE TABLE IF NOT EXISTS {tablename} (
id int(5) PRIMARY KEY ,video_title VARCHAR(100),video_avid VARCHAR(20),video_bvid VARCHAR(20),video_play_num INT(10),video_comment_num VARCHAR(10),video_danmu_num VARCHAR(10),video_created DATE,video_length VARCHAR (10),video_description VARCHAR(1000),video_link VARCHAR (50)
) DEFAULT CHARSET utf8 COLLATE utf8_general_ci;"""
self.cursor.execute(sql2)
n=1
id_num=1
while True:
url = f'https://api.bilibili.com/x/space/arc/search?mid={key}&ps=30&tid=0&pn={n}&keyword=&order=pubdate&jsonp=jsonp'
r=requests.get(url,headers=self.headers)
_json=json.loads(r.text)
v_list=_json.get('data').get('list').get('vlist')
# if n==1:#在数据抽取第一次将视频数量保存下来
# v_count = jsonpath.jsonpath(_json, '$.data.list..count')#因为分类数量不定,所以使用jsonpath去模糊匹配
n+=1
if len(v_list)!=0:#程序终止标志
for video in v_list:
video_title=video.get('title')#标题
video_aid=video.get('aid')#av号
video_bvid=video.get('bvid')#BV号
video_play_num=video.get('play')#播放数量
video_comment_num=video.get('comment')#当前评论数量
video_danmu_num=video.get('video_review')#弹幕数量
video_created_=video.get('created')#
timeArray = time.localtime(video_created_)
video_created = time.strftime("%Y-%m-%d %H:%M:%S", timeArray)#时间字符串格式化
video_length=video.get('length')#视频长度
video_description=video.get('description')#视频描述
video_link='https://www.bilibili.com/video/'+video_bvid#链接地址:固定开头+BV号
sql=f'insert into {tablename} values(%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)'
self.cursor.execute(sql,(id_num,video_title,video_aid,video_bvid,video_play_num,video_comment_num,video_danmu_num,video_created,video_length,video_description,video_link))
self.conn.commit()
id_num+=1
print(video_title,video_aid,video_created,video_length,video_link)
else:
print(f'\033[35;46m----------------------------爬取了{id_num-1}个视频----------------------------\033[0m')
self.cursor.close()
self.conn.close()
break
def main():
bilibili_user_video_parse=Bilibili_User_Videos()
table_name=bilibili_user_video_parse.get_tablename()
bilibili_user_video_parse.parse(table_name)
if __name__ == '__main__':
while True:
key=input('请输入用户id:')
#做一个简单的判断
judge=re.match('\d+',key)
if judge:
main()
break
else:
print('id输入有误!')
使用navicate for MySQL 数据库可视化工具查看我们得到的数据。
可以看到,一共168条数据,全部保存到了MySQL中,并且数据表名为用户名。