python数据分析|B站视频搜索结果爬虫入库

要求:在B站搜索多个关键字,并将搜索到的视频结果(包括视频名,视频连接,点击量,收藏量,弹幕数和长传时间)录入数据库

了解网页

爬取数据前,我们需要先了解网页结构,以B站首页为例https://www.bilibili.com/
,按快捷键【Ctrl+U】打开源码页面

认识网页结构

网页一般由三部分组成,分别是 HTML(超文本标记语言)、CSS(层叠样式表)和 JScript(活动脚本语言)。
HTML
HTML 是整个网页的结构,相当于整个网站的框架。带“<”、“>”符号的都是属于 HTML 的标签,并且标签都是成对出现的。

标签
<head>..</head> 包含了所有的头部标签元素。在 <head>元素中可以插入脚本(scripts), 样式文件(CSS),及各种 meta 信息。可以添加的元素标签为: <title>, <style>, <meta>, <link>, <script>, <noscript> 和 <base>。
<html>..</html> 表示标记中间的元素是网页
<body>..</body> 表示用户可见的内容
<div>..</div> 表示框架
<p>..</p> 表示段落
<li>..</li> 表示列表
<img>..</img> 表示图片
<h1>..</h1> 表示标题
<a href="">..</a> 表示超链接

CSS
CSS 表示样式,表示下面引用一个 CSS,在 CSS 中定义了外观。


JScript
JScript 表示功能。交互的内容和各种特效都在 JScript 中,JScript 描述了网站中的各种功能。

如果用人体来比喻,HTML 是人的骨架,并且定义了人的嘴巴、眼睛、耳朵等要长在哪里。CSS 是人的外观细节,如嘴巴长什么样子,眼睛是双眼皮还是单眼皮,是大眼睛还是小眼睛,皮肤是黑色的还是白色的等。JScript 表示人的技能,例如跳舞、唱歌或者演奏乐器等。

写一个简单的 HTML

打开一个记事本,输入下面的内容:

<head>
 <title> 这里是标题</title>
</head>
<body>
 <div style="color:#0000FF">
  <h1>这是被第一个div定义的标题1</h1>
  <p>这里是被第一个div定义第一个段落</p>
 </div>
 <div style="color:#00FF00">
   <ul>
   <li><a href="https://www.baidu.com/">列表内容1 百度链接</a></li>
   <li>列表内容2</li>
   </ul>
 </div>
</body>

输入代码后,保存记事本,然后修改文件名和后缀名为"HTML.html",运行该文件后的效果,如图。

使用 requests 库请求网站

若未安装 requests 库,可在终端中使用pip安装库:

pip install requests

爬虫的基本原理

网页请求的过程分为两个环节:
Request (请求):也就是向服务器发送访问请求。
Response(响应):服务器在接收到用户的请求后,会验证请求的有效性,然后向用户(客户端)发送响应的内容,客户端接收服务器响应的内容,将内容展示出来,就是我们所熟悉的网页请求

网页请求的方式也分为两种:
GET:最常见的方式,一般用于获取或者查询资源信息,也是大多数网站使用的方式,响应速度快。
POST:相比 GET 方式,多了以表单形式上传参数的功能,因此除查询信息外,还可以修改信息。

所以,在写爬虫前要先确定向谁发送请求,用什么方式发送。B站是使用get请求的,所以我们使用get方式抓取数据

获取B站视频搜索结果url列表

demand参数:表示搜索视频排序方式(watch:按播放量排序,dm:按弹幕量排序,time:按上传时间排序,collect:按收藏量排序)
kw参数:表示搜索的视频关键字列表

def bilibili_url (demand,kw):
    if demand == 'watch':
        url = 'https://search.bilibili.com/all?keyword={}&order=click&duration=0&tids_1=0'
    elif  demand == 'dm':
        url = 'https://search.bilibili.com/all?keyword={}&order=dm&duration=0&tids_1=0'
    elif demand == 'time':
        url = 'https://search.bilibili.com/all?keyword={}&order=pubdate&duration=0&tids_1=0'
    elif demand == 'collect':
        url = 'https://search.bilibili.com/all?keyword={}&order=stow&duration=0&tids_1=0'
    else :
        url = 'https://search.bilibili.com/all?keyword={}'
    url_list = []
    for i in kw:
        list_view = url.format(i)
        url_list.append(list_view)
    return url_list

信息爬取

def get_bilibili_play_info(url_list):
    info = []  #建立一个空列表存放信息
    seen = set()  #建立一个空集合,去除重复字典
    for j in url_list:
        for i in range(50):
            t=random.randint(0,9)  
            time.sleep(t)  #随机sleep0到9秒防反爬
            url=j + ('&page=%d' %(i+1))  #跳转页数,B站支持显示50页搜索信息
            print (url)
            headers = {'Accept': 'text/html, application/xhtml+xml, image/jxr, */*',
                       'Accept - Encoding':'gzip, deflate',
                       'Accept-Language':'zh-Hans-CN, zh-Hans; q=0.5',
                       'Connection':'Keep-Alive',
                       'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36 Edge/15.15063'}
            bz_data = requests.get(url,headers=headers) 
            #添加headers参数伪装浏览器反爬
            soup = BeautifulSoup(bz_data.text,'lxml')  #解析网页信息
            for link in soup.find_all(name='li',class_="video-item matrix"):
                #对每一个视频信息循环处理,link的信息如下图
                info_dic = {}
                for temp in link.find_all(name='a',class_="img-anchor"):
                    #提取a标签,temp信息如下图
                    info_dic['title'] = temp.get('title')  #视频名信息
                    info_dic['url'] = "http:%str" %temp.get('href') #视频url信息
                a = str(link.find('span',class_='so-icon watch-num'))  
                #a= '<span class="so-icon watch-num" title="观看"><i class="icon-playtime"></i>\n        1230.1万\n      </span>'
                if '万' in a:
                    watch_num=re.findall(r"\d+\.\d+", a)
                    watch_num[0]=(float(watch_num[0]))*10000
                    info_dic['watch_num']=int(watch_num[0])
                else :
                    watch_num=re.findall(r"\d+", a)
                    watch_num[0]=float(watch_num[0])
                    info_dic['watch_num']=int(watch_num[0])
                a = str(link.find('span',class_='so-icon hide'))  
                #'<span class="so-icon hide" title="弹幕"><i class="icon-subtitle"></i>\n        5.8万\n      </span>'
                if '万' in a:
                    hide=re.findall(r"\d+\.\d+", a)
                    hide[0]=(float(hide[0]))*10000
                    info_dic['dm']=int(hide[0])
                else :
                    hide=re.findall(r"\d+", a)
                    hide[0]=int(hide[0])
                    info_dic['dm']=int(hide[0])
                a = str(link.find('span',class_='so-icon time'))  
                #'<span class="so-icon time" title="上传时间"><i class="icon-date"></i>\n        2020-02-01\n      </span>'
                info_dic['upload_time']=re.findall(r"(\d{4}-\d{1,2}-\d{1,2})", a)[0]
                #如果需要提取为datatime格式,可以用下面这条
                #info_dic['上传时间']=datetime.date(*map(int, time_list[0].split('-')))
                t = tuple(info_dic.items())
                if t not in seen:
                    #筛选是否有重复的视频信息
                    seen.add(t)
                    info.append(info_dic)
    return info

其中,每个视频的网页解析信息link结果如图:

temp信息如图:

数据入库

参数:host,user,password,db为连接数据库的参数

def rq(host,user,password,db,table,info):
    cols = ", ".join('`{}`'.format(k) for k in info[0].keys())
    #提取列表中字典的键,格式为`key1`,`key2`.....
    val_list = []
    for i in info:
        #仅保留列表中所有字典的值,以元组格式重新存入新列表
        val_tup = tuple(i.values())
        val_list.append(val_tup)
    cols_val = '%s'
    for j in range(len(list(info[0].keys()))-1):
        #根据键个数添加%s
        cols_val = cols_val + ",%s" 
    sql="insert into %s(%s) value(%s)"
    res_sql = sql % (table,cols,cols_val)
    #res_sql ="insert into table(`key1`,`key2`...) value(%s,%s...)"
    db=pymysql.connect(host=host,user=user,password=password,db=db,charset='utf8')
    #连接数据库
    cursor = db.cursor()
    cursor.executemany(res_sql,val_list)  
    #对数据库进行批量操作,其中val_list必须为以元组组成的列表格式
    db.commit()  # 提交更新的数据到数据库
    cursor.fetchall() #查询处理后数据结果
    print ('入库完成!')
    # 关闭cursor游标
    cursor.close()
    #关闭服务器
    db.close()
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,723评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,003评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,512评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,825评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,874评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,841评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,812评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,582评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,033评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,309评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,450评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,158评论 5 341
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,789评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,409评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,609评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,440评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,357评论 2 352