目录
一、mysql数据库的设置
1.进入数据库:mysql -u用户名 -p密码
2.查询数据库:show databases;
3.创建新的数据库:create database 数据库的名称 charset=编码格式(utf8mb4);
注意:编码格式我们一般使用utf8mb4,因为普通的utf8在保存表情时可能会出现乱码
4.使用(选择)数据库:use 数据库名称
5.创建数据表:
creat table 数据表名称(
id int auto_increment primary key,
名 类型 自动增长 主键
nick_name varchar(100) not null,
名 类型可变字符串(容量) 不能为空
comments varchar(5000) not null;
)
6.查看数据表的结构:desc 数据表名称;
7.python与mysql数据库进行交互
pymysql > python控制mysql数据库的第三方库
数据入库:以上节所讲的腾讯课堂评论爬取的案例为例
# 导入与mysql数据库进行交互的第三方库
from pymysql import *
import requests
import jsonpath
import json
from fake_useragent import FakeUserAgent
import time
if __name__ == '__main__':
# (1) 与MySQL数据库进行连接,会返回给我们一个对象
data_obj = connect(host='mysql数库的IP地址', user='mysql数据库的用户名', password='mysql数据库的密码', database='mysql数据库的名称',
port='mysql数据库的端口号,默认是3306', charset='编码格式')
# (2) 创建一个游标对象
mysql_ = data_obj.cursor()
for i in range(3):
# 1.url的确认
url_ = f'https://ke.qq.com/cgi-bin/comment_new/course_comment_list?cid=425893&count=10&page={i}&filter_rating=0&bkn=260593302&r=0.3510540542720364'
# 设置一个用户代理
headers_ = {
'User-Agent': FakeUserAgent().random,
'Cookie': 'RK=3PptZAXHNG; ptcz=0a8ee31077f15dbc8b652bcded52e5312ba6f1b50b3851fed841a642536aecb1; pgv_pvi=5472699392; pgv_pvid=8009771513; ts_uid=1386988748; ke_login_type=1; localInterest=[2032]; o_cookie=925290303; pac_uid=1_925290303; eas_sid=41q6x140H584g9Q6d4J7a3i2h1; sd_userid=2251615169031575; sd_cookie_crttime=1615169031575; tvfe_boss_uuid=7fff752b6f976d19; _pathcode=0.19297139008784892; tdw_auin_data=-; iswebp=1; tdw_first_visited=1; Hm_lvt_0c196c536f609d373a16d246a117fd44=1625421010; pgv_info=ssid=s9590420085; ts_refer=www.baidu.com/link; _qpsvr_localtk=0.27361930633804943; tdw_data_sessionid=162542108087646589378986; ts_last=ke.qq.com/course/425893; tdw_data_testid=; tdw_data_flowid=; uin=o0925290303; skey=@fqMi4eMJj; luin=o0925290303; lskey=00010000195df01b242ea1cb526d55f181749e7edaaae9577a5d84257c6935afb38c518e98e47db7e9a3bb5b; pt4_token=Zr4JcDZb5qs1YDNLXOGa4k6qHPTbAQe5rO6Gw*os93g_; p_skey=iBermNfAdmxG8oOK*bxmD7Zo88CLhPJ4An0rXaBMZFI_; p_lskey=000400009c4d0d1b69f4dc95614b9786a332a0f6020edd1812cd2f119238f14c961b449b5a595a48d4487f5b; auth_version=2.0; mix_login_mode=true; uid_type=0; uin=925290303; p_uin=925290303; p_luin=925290303; uid_uin=925290303; uid_origin_uid_type=0; tdw_data={"ver4":"www.baidu.com","ver5":"","ver6":"","refer":"www.baidu.com","from_channel":"","path":"ddddh-0.19297139008784892","auin":"-","uin":925290303,"real_uin":"25290303"}; sessionPath=1625421258802847841718; Hm_lpvt_0c196c536f609d373a16d246a117fd44=1625422801; tdw_data_new_2={"auin":"-","sourcetype":"","sourcefrom":"","uin":925290303,"visitor_id":"1765771903493909","sessionPath":"1625421258802847841718","ver9":925290303}; report_position_key={"url_position":"","url_module":"","url_page":"course"}',
'Referer': 'https://ke.qq.com/course/425893' # 从正规的页面跳转过来的
}
# 2.发送网络请求, 获取响应对象
response_ = requests.get(url_, headers=headers_)
data_ = response_.json() # 自动转换python格式的数据
# print(data_)
# 3.解析数据 > json类型的数据
# 昵称
name_list = jsonpath.jsonpath(data_, '$..nick_name')
# 评论
comment_list = jsonpath.jsonpath(data_, '$..first_comment')
# print(name_list)
# print(comment_list)
# 4.保存((3) 数据入库)
for i in range(len(name_list)):
# mysql_.execute('要执行的sql语句')
mysql_.execute("insert 表名(要插入数据的字段) values(0,('%s'),('%s'))" % (name_list[i], comment_list[i]))
# 降低请求频率,避免被反爬
time.sleep(1.5)
# (4) 需要提交才能生效(成功保存)
data_obj.commit()
# (5) 关闭数据表的连接
mysql_.close()
# (6) 关闭数据库的连接
data_obj.close()
二、B站视频获取案例
单个视频的爬取
B站视频抓包分析
此处我们以https://www.bilibili.com/video/BV12W411x7ac?from=search&seid=13954326685747091392为例:
1.在视频页面右键点击检查,点击network,然后刷新页面,再将network清空,点击视频的播放,发送请求,获取数据包(可以三通过数据包的大小来寻找视频的数据包,一般视频的数据包是最大的,判断找到的数据包是否正确,我们可以通过双击数据包名称,看看是否跳转到了视频播放页面或者进行了下载)
2.经过分析我们找到了两个比较像的数据包:
33327178_da2-1-30280.m4s?e=ig8euxZM2rNcNbdlhoNvNC8…eIWwlbW2eaKMfIWwJIgxMkJARkJwzaKLubg6sagIfbDbmBf==(1.0MB)
33327178_da2-1-30064.m4s?e=ig8euxZM2rNcNbdlhoNvNC8…zaWmRbgwMbgIPIW1pVl6fkJwpbKLuIlA4IgAuaJmlIWAJ0-n=(2.1MB)
我们简称上面两个数据包分别为:30280,30064
3.找到了相似的url,我们分别对他们发送请求:
import requests
from fake_useragent import FakeUserAgent
if __name__ == '__main__':
# 1.目标url的确认
url_30280 = 'https://5ov35z5j.v1d.szbdyd.com:9305/upos-sz-mirrorkodoo1.bilivideo.com/upgcxcode/78/71/33327178/33327178_da2-1-30280.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1625574509&gen=playurlv2&os=kodoo1bv&oi=994175558&trid=96852f1b7525438db742433f87117392u&platform=pc&upsig=020b6a19ca14b23680a33351361aea2e&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&mid=0&bvc=vod&orderid=0,3&agrr=0&logo=80000000&xyip=144.52.158.156&xyct=58&xycip=106.37.205.155&xyid=a8a99995-37c4-14e3-ea1b-beb5b43f3dfa&xypr=iKzlB-C40-t40Rzp0EXsB-mPiFbX2JVPIlheIWwlbW2eaKMfIWwJIgxMkJARkJwzaKLubg6sagIfbDbmBf=='
url_30064 = 'https://tgkzfdbq.v1d.szbdyd.com:9305/cn-bj-se-bcache-01.bilivideo.com/upgcxcode/78/71/33327178/33327178_da2-1-30064.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1625574509&gen=playurlv2&os=bcache&oi=994175558&trid=000096852f1b7525438db742433f87117392u&platform=pc&upsig=7940913cf0de38f8d0ef0c9ac6d65fec&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&cdnid=60001&mid=0&bvc=vod&orderid=0,3&agrr=0&logo=80000000&xycip=106.37.205.155&xyid=61483a03-1595-9ba8-29f5-45c5c1dd2e1e&xypr=iKzUryUzBDzP6EC42cUUrYzz2coRIRMzaWmRbgwMbgIPIW1pVl6fkJwpbKLuIlA4IgAuaJmlIWAJ0-n='
# 构造请求头
headers_ = {
'User-Agent': FakeUserAgent().random,
'Cookie': "_uuid=22033820-F579-BEBC-42CE-58C113E2551A56116infoc; buvid3=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; sid=8p6gkcm9; fingerprint=2446d5d91ca8dced483055bb370a3374; buvid_fp=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; buvid_fp_plain=743C4C8A-A496-4445-81C9-2F616EAC6EE895161infoc; CURRENT_FNVAL=80; blackside_state=1; rpdid=|(RY~~Y|Y~l0J'uY|m)lYYuJ; LIVE_BUVID=AUTO1816144322540275; CURRENT_QUALITY=64; PVID=1",
'Referer': 'https://www.bilibili.com/video/BV12W411x7ac?from=search&seid=13954326685747091392'
}
# 2.发送请求,获取响应
response_30280 = requests.get(url_30280, headers=headers_)
response_30064 = requests.get(url_30064, headers=headers_)
# 因为我们要找的是视频的数据包,数据包是我们找到疑似视频的,所以响应应该是字节型数据,用content
data_30280 = response_30280.content
data_30064 = response_30064.content
# 3.保存
with open('lemon_30280.mp4', 'wb') as f:
f.write(data_30280)
with open('lemon_30064.mp4', 'wb') as f:
f.write(data_30064)
4.下载成功后,我们分别打开两个文件,发现30280只有声音没有视频是个纯音频的文件,30064只有视频没有声音是个纯视频的文件
5.拿到了纯视频和纯音频,进行视频和音频合成(利用我们介绍过的ffmpeg)
import requests
from fake_useragent import FakeUserAgent
import os
if __name__ == '__main__':
# 1.目标url的确认
url_30280 = 'https://5ov35z5j.v1d.szbdyd.com:9305/upos-sz-mirrorkodoo1.bilivideo.com/upgcxcode/78/71/33327178/33327178_da2-1-30280.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1625574509&gen=playurlv2&os=kodoo1bv&oi=994175558&trid=96852f1b7525438db742433f87117392u&platform=pc&upsig=020b6a19ca14b23680a33351361aea2e&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&mid=0&bvc=vod&orderid=0,3&agrr=0&logo=80000000&xyip=144.52.158.156&xyct=58&xycip=106.37.205.155&xyid=a8a99995-37c4-14e3-ea1b-beb5b43f3dfa&xypr=iKzlB-C40-t40Rzp0EXsB-mPiFbX2JVPIlheIWwlbW2eaKMfIWwJIgxMkJARkJwzaKLubg6sagIfbDbmBf=='
url_30064 = 'https://tgkzfdbq.v1d.szbdyd.com:9305/cn-bj-se-bcache-01.bilivideo.com/upgcxcode/78/71/33327178/33327178_da2-1-30064.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1625574509&gen=playurlv2&os=bcache&oi=994175558&trid=000096852f1b7525438db742433f87117392u&platform=pc&upsig=7940913cf0de38f8d0ef0c9ac6d65fec&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&cdnid=60001&mid=0&bvc=vod&orderid=0,3&agrr=0&logo=80000000&xycip=106.37.205.155&xyid=61483a03-1595-9ba8-29f5-45c5c1dd2e1e&xypr=iKzUryUzBDzP6EC42cUUrYzz2coRIRMzaWmRbgwMbgIPIW1pVl6fkJwpbKLuIlA4IgAuaJmlIWAJ0-n='
# 构造请求头
headers_ = {
'User-Agent': FakeUserAgent().random,
'Cookie': "_uuid=22033820-F579-BEBC-42CE-58C113E2551A56116infoc; buvid3=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; sid=8p6gkcm9; fingerprint=2446d5d91ca8dced483055bb370a3374; buvid_fp=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; buvid_fp_plain=743C4C8A-A496-4445-81C9-2F616EAC6EE895161infoc; CURRENT_FNVAL=80; blackside_state=1; rpdid=|(RY~~Y|Y~l0J'uY|m)lYYuJ; LIVE_BUVID=AUTO1816144322540275; CURRENT_QUALITY=64; PVID=1",
'Referer': 'https://www.bilibili.com/video/BV12W411x7ac?from=search&seid=13954326685747091392'
}
# 2.发送请求,获取响应
response_30280 = requests.get(url_30280, headers=headers_)
response_30064 = requests.get(url_30064, headers=headers_)
# 因为我们要找的是视频的数据包,数据包是我们找到疑似视频的,所以响应应该是字节型数据,用content
data_30280 = response_30280.content
data_30064 = response_30064.content
# 3.保存
with open('lemon_30280.mp3', 'wb') as f:
f.write(data_30280)
with open('lemon_30064.mp4', 'wb') as f:
f.write(data_30064)
# 4.纯视频和纯音频的合成
os.system('ffmpeg -i lemon_30280.mp3 -i lemon_30064.mp4 -c copy lemon.mp4')
# 合成的固定命令 音频 视频 合成后的文件名
# 5.删除原来的纯音频和纯视频
os.remove('lemon_30280.mp3')
os.remove('lemon_30064.mp4')
6.我们此时拿到的视频名称是我们自己取的,这样就很不方便,为了解决这个问题,我们可以使用原来网站上视频的名称进行命名并且是否有办法不用抓包只需要提供视频链接就可以下载视频呢?
视频名称:鼠标右键选择检查,出来的界面右上角选择一个小箭头加一个方框的图片,然后点击页面中视频的名字,会自动跳转到视频名称对应在html数据中对应的位置(不好用)
利用xpath_helper找到视频名称对应的xpath语法,然后利用代码对其进行提取
//span[@class='tit tr-fix']/text()
我们发现上面的xpath语法虽然可以成功取到这个视频的名称但在别的视频中就不能去到了(不通用)
在此我们在介绍另外一种方法,复制视频的名称,在检查中的elements中按ctrl + F,粘贴视频名字,找到视频名字在html数据中所有对应的位置,如果想找到对于其他视频也能用的xpath语法,可以对找到的位置写出他们的xpath语法,然后一一实验。
这里我们找到的通用的语法是:
//title/text()
这个xpath语法唯一的缺点就是视频名称后面多了:_哔哩哔哩_bilibili,对此我们在取到后可以使用re模块的语法去掉:_哔哩哔哩_bilibili 或者 使用切片操作
视频链接:
https://5ov35z5j.v1d.szbdyd.com:9305/upos-sz-mirrorkodoo1.bilivideo.com/upgcxcode/78/71/33327178/33327178_da2-1-30280.m4s?e=ig8euxZM2rNcNbdlhoNvNC8BqJIzNbfqXBvEqxTEto8BTrNvN0GvT90W5JZMkX_YN0MvXg8gNEV4NC8xNEV4N03eN0B5tZlqNxTEto8BTrNvNeZVuJ10Kj_g2UB02J0mN0B5tZlqNCNEto8BTrNvNC7MTX502C8f2jmMQJ6mqF2fka1mqx6gqj0eN0B599M=&uipk=5&nbs=1&deadline=1625574509&gen=playurlv2&os=kodoo1bv&oi=994175558&trid=96852f1b7525438db742433f87117392u&platform=pc&upsig=020b6a19ca14b23680a33351361aea2e&uparams=e,uipk,nbs,deadline,gen,os,oi,trid,platform&mid=0&bvc=vod&orderid=0,3&agrr=0&logo=80000000&xyip=144.52.158.156&xyct=58&xycip=106.37.205.155&xyid=a8a99995-37c4-14e3-ea1b-beb5b43f3dfa&xypr=iKzlB-C40-t40Rzp0EXsB-mPiFbX2JVPIlheIWwlbW2eaKMfIWwJIgxMkJARkJwzaKLubg6sagIfbDbmBf==我们在以前介绍了?后面都是url携带的参数,有些是没用的,所以主要的url是https://5ov35z5j.v1d.szbdyd.com:9305/upos-sz-mirrorkodoo1.bilivideo.com/upgcxcode/78/71/33327178/33327178_da2-1-30280.m4s
前面介绍了通过抓包获取视频和音频的连接,那么我们在主页面html格式数据的响应中能否找到视频和音频数据包对应的url呢?我们将视频和音频主要的url(上面已经介绍)复制,在检查中点击右上角的三个点,点search进行全局搜索,粘贴我们复制的url,点击右边的refresh按键,点击下方出来的信息会自动跳转到对应文件中的位置,我们将鼠标放在出现的文件上面就会显示出该文件夹对应的url,发现:该文件夹的url就是主页的url,所以一番探寻,视频和音频的url都存在与主页html数据中。对找到的url的位置进行观察,他是存在于一个script标签当中,并且没有属性,同名兄弟节点过多导致父节点难找,此时我们可以文本内容去进行定位
//script[text(),"window.__playinfo__"]/text()
为什么没有结果呢?
需要做一个模糊查询,只要包含window.__playinfo__就定位
//script[contains(text(),"window.__playinfo__")]/text()
得到url所在的文本数据后使用正则进行url的提取即可
url_video = re.findall(r'"video":\[{"id":\d+,"baseUrl":"(.*?)"', url_str)[0]
url_audio = re.findall(r'"audio":\[{"id":\d+,"baseUrl":"(.*?)"', url_str)[0]
需要注意的是:id的值是不确定位数的数字,因为不是所有视频的清晰度都一样
最终代码:
import requests
from fake_useragent import FakeUserAgent
import os
from lxml import etree
import re
if __name__ == '__main__':
# 1.目标url的确认
url_ = input('请输入要下载b站视频的网址:')
# 构造请求头
headers_ = {
'User-Agent': FakeUserAgent().random,
'Cookie': "_uuid=22033820-F579-BEBC-42CE-58C113E2551A56116infoc; buvid3=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; sid=8p6gkcm9; fingerprint=2446d5d91ca8dced483055bb370a3374; buvid_fp=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; buvid_fp_plain=743C4C8A-A496-4445-81C9-2F616EAC6EE895161infoc; CURRENT_FNVAL=80; blackside_state=1; rpdid=|(RY~~Y|Y~l0J'uY|m)lYYuJ; LIVE_BUVID=AUTO1816144322540275; CURRENT_QUALITY=64; PVID=2",
}
# 2.对主页发送请求,获取响应
response_ = requests.get(url_, headers=headers_)
str_data = response_.text
# html格式数据的转换
html_obj = etree.HTML(str_data)
# 3.解析(获取视频名称和视频音频的url)
# 获取视频名称
title_ = html_obj.xpath('//title/text()')[0]
# print(title_) # 【unnatural×Lemon】如今你依旧是我的光_哔哩哔哩_bilibili
title_ = re.findall(r'(.*?)_哔哩哔哩_bilibili', title_)[0]
# print(title_) # 【unnatural×Lemon】如今你依旧是我的光
# 视频名字处理,文件命名中有些特殊符号不能出现,我们在B站爬到的视频名字可能有一些非法字符出现
title_ = re.sub(u"([^\u4e00-\u9fa5\u0030-\u0039\u0041-\u005a\u0061-\u007a])", "", title_)
# 获取纯视频和纯音频的url
url_str = html_obj.xpath('//script[contains(text(),"window.__playinfo__")]/text()')[0]
# print(url_str)
# 纯视频url的提取
url_video = re.findall(r'"video":\[{"id":\d+,"baseUrl":"(.*?)"', url_str)[0]
# print(url_video)
url_audio = re.findall(r'"audio":\[{"id":\d+,"baseUrl":"(.*?)"', url_str)[0]
# print(url_audio)
# 4.设置携带跳转信息的请求头
headers_ = {
'User-Agent': FakeUserAgent().random,
'Cookie': "_uuid=22033820-F579-BEBC-42CE-58C113E2551A56116infoc; buvid3=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; sid=8p6gkcm9; fingerprint=2446d5d91ca8dced483055bb370a3374; buvid_fp=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; buvid_fp_plain=743C4C8A-A496-4445-81C9-2F616EAC6EE895161infoc; CURRENT_FNVAL=80; blackside_state=1; rpdid=|(RY~~Y|Y~l0J'uY|m)lYYuJ; LIVE_BUVID=AUTO1816144322540275; CURRENT_QUALITY=64; PVID=4",
'Referer': url_
}
# 5.发送请求,获取纯视频和纯音频的响应
response_video = requests.get(url_video, headers=headers_)
data_video = response_video.content
response_audio = requests.get(url_audio, headers=headers_)
data_audio = response_audio.content
# 6.保存
with open(f'video.mp4', 'wb') as f:
f.write(data_video)
with open(f'audio.mp3', 'wb') as f:
f.write(data_audio)
# 7.合成纯视频和纯音频
os.system(f'ffmpeg -i "audio.mp3" -i "video.mp4" -c copy {title_}.mp4 -loglevel quiet')
# 8.删除纯视频和纯音频
os.remove('video.mp4')
os.remove('audio.mp3')
需要注意的是:
1.第二个请求头需要携带跳转信息,因为我们要对纯视频和纯音频的url发送请求,这个必须是从视频页面跳转过去的,所以referer 是 我们输入的url
2. *\/:?"<>|' # 这9个字符在Windows系统下是不可以出现在文件名中的,还有一些特殊字符都不能出现,我们在获取到视频名字后需要把这些字符去掉
3.在合成视频的命令结尾加入:-loglevel quiet 可以隐藏ffmpeg运行过程中红色的运行信息
4.输入网址后需要在按一下空格再按回车,不按空格按回车会打开该页面,不会运行程序
单页视频的爬取
我们已经可以成功下载单个视频,那如果我们有了一整页视频的url,是不是就可以下载一整页视频了呢?
我们可以在主页输入一个关键字,然后跳转到该关键字搜索出来的视频的第一页,然后对第一页的视频进行下载
b站主页的url:https://www.bilibili.com/
搜索lemon后的url:https://search.bilibili.com/all?keyword=lemon&from_source=webtop_search&spm_id_from=333.851
(&from_source=webtop_search&spm_id_from=333.851是没有用的参数,删除后依旧可以访问到原来的页面)
在这个页面中,我们随便点开一个视频,该视频的爬取就和我们上面介绍的一样,不过上面我们需要手动输入这个视频的url。现在我们要爬取一整页的视频,所以要输入一整页视频的url,不过不用那么麻烦,我们可以从 搜索lemon后的网页中提取到这些视频的url。
为实现需求:在主页输入一个关键字,然后跳转到该关键字搜索出来的视频的第一页,然后对第一页的视频进行下载
我们首先需要确定一个关键词,并且获得跳转后页面的url,经上面分析跳转后的页面的url就是:
https://search.bilibili.com/all?keyword=我们输入的关键词,得到首页url后对其发送请求,获取响应,方便我们从中提取20个视频的url
import requests
from fake_useragent import FakeUserAgent
import os
from lxml import etree
import re
if __name__ == '__main__':
# 确定关键字
kw_ = input('请输入要搜索视频的关键词:')
# 1.确定首页的url
url_ = f'https://search.bilibili.com/all?keyword={kw_}'
# 构造请求头
headers_ = {
'User-Agent': FakeUserAgent().random,
'Cookie': "_uuid=22033820-F579-BEBC-42CE-58C113E2551A56116infoc; buvid3=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; sid=8p6gkcm9; fingerprint=2446d5d91ca8dced483055bb370a3374; buvid_fp=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; buvid_fp_plain=743C4C8A-A496-4445-81C9-2F616EAC6EE895161infoc; CURRENT_FNVAL=80; blackside_state=1; rpdid=|(RY~~Y|Y~l0J'uY|m)lYYuJ; LIVE_BUVID=AUTO1816144322540275; CURRENT_QUALITY=64; arrange=matrix; PVID=5"
}
# 2.对首页发送请求,获取响应
response_ = requests.get(url_, headers=headers_)
str_data = response_.text
我们在首页右键选择检查,用左上角的小箭头和方框的按钮点击视频的名字,会自动跳转到有视频url的位置,然后使用xpath_helper进行调试,找到提取20个视频的xpath语法
//li[@class='video-item matrix']/a/@href
我们需要对20个url利用循环一一发送请求,但是需要注意提取到的url的前面缺少https:,所以我们要进行补全,剩下的操作与下载单个视频一致
import requests
from fake_useragent import FakeUserAgent
import os
from lxml import etree
import re
import time
if __name__ == '__main__':
# 确定关键字
kw_ = input('请输入要搜索视频的关键词:')
# 1.确定首页的url
url_ = f'https://search.bilibili.com/all?keyword={kw_}'
# 构造请求头
headers_ = {
'User-Agent': FakeUserAgent().random,
'Cookie': "_uuid=22033820-F579-BEBC-42CE-58C113E2551A56116infoc; buvid3=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; sid=8p6gkcm9; fingerprint=2446d5d91ca8dced483055bb370a3374; buvid_fp=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; buvid_fp_plain=743C4C8A-A496-4445-81C9-2F616EAC6EE895161infoc; CURRENT_FNVAL=80; blackside_state=1; rpdid=|(RY~~Y|Y~l0J'uY|m)lYYuJ; LIVE_BUVID=AUTO1816144322540275; CURRENT_QUALITY=64; arrange=matrix; PVID=5"
}
# 2.对首页发送请求,获取响应
response_ = requests.get(url_, headers=headers_)
str_data = response_.text
# 3.提取首页中所有视频的url(20个)
html_obj = etree.HTML(str_data)
url_list = html_obj.xpath("//li[@class='video-item matrix']/a/@href")
# 4.对20个url进行循环
for url_ in url_list:
# (1) 目标的确认url
url_ = 'https:' + url_
# 构造请求头
headers_ = {
'User-Agent': FakeUserAgent().random,
'Cookie': "_uuid=22033820-F579-BEBC-42CE-58C113E2551A56116infoc; buvid3=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; sid=8p6gkcm9; fingerprint=2446d5d91ca8dced483055bb370a3374; buvid_fp=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; buvid_fp_plain=743C4C8A-A496-4445-81C9-2F616EAC6EE895161infoc; CURRENT_FNVAL=80; blackside_state=1; rpdid=|(RY~~Y|Y~l0J'uY|m)lYYuJ; LIVE_BUVID=AUTO1816144322540275; CURRENT_QUALITY=64; PVID=2",
}
# (2).对视频主页发送请求,获取响应
response_ = requests.get(url_, headers=headers_)
str_data = response_.text
# html格式数据的转换
html_obj = etree.HTML(str_data)
# (3).解析(获取视频名称和视频音频的url)
# 获取视频名称
title_ = html_obj.xpath('//title/text()')[0]
# print(title_) # 【unnatural×Lemon】如今你依旧是我的光_哔哩哔哩_bilibili
title_ = re.findall(r'(.*?)_哔哩哔哩_bilibili', title_)[0]
# print(title_) # 【unnatural×Lemon】如今你依旧是我的光
# 视频名字处理,文件命名中有些特殊符号不能出现,我们在B站爬到的视频名字可能有一些非法字符出现
title_ = re.sub(u"([^\u4e00-\u9fa5\u0030-\u0039\u0041-\u005a\u0061-\u007a])", "", title_)
# 获取纯视频和纯音频的url
url_str = html_obj.xpath('//script[contains(text(),"window.__playinfo__")]/text()')[0]
# print(url_str)
# 纯视频url的提取
url_video = re.findall(r'"video":\[{"id":\d+,"baseUrl":"(.*?)"', url_str)[0]
# print(url_video)
url_audio = re.findall(r'"audio":\[{"id":\d+,"baseUrl":"(.*?)"', url_str)[0]
# print(url_audio)
# (4).设置携带跳转信息的请求头
headers_ = {
'User-Agent': FakeUserAgent().random,
'Cookie': "_uuid=22033820-F579-BEBC-42CE-58C113E2551A56116infoc; buvid3=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; sid=8p6gkcm9; fingerprint=2446d5d91ca8dced483055bb370a3374; buvid_fp=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; buvid_fp_plain=743C4C8A-A496-4445-81C9-2F616EAC6EE895161infoc; CURRENT_FNVAL=80; blackside_state=1; rpdid=|(RY~~Y|Y~l0J'uY|m)lYYuJ; LIVE_BUVID=AUTO1816144322540275; CURRENT_QUALITY=64; PVID=4",
'Referer': url_
}
# (5).发送请求,获取纯视频和纯音频的响应
response_video = requests.get(url_video, headers=headers_)
data_video = response_video.content
response_audio = requests.get(url_audio, headers=headers_)
data_audio = response_audio.content
# (6).保存
with open(f'video.mp4', 'wb') as f:
f.write(data_video)
with open(f'audio.mp3', 'wb') as f:
f.write(data_audio)
# (7).合成纯视频和纯音频
os.system(f'ffmpeg -i "audio.mp3" -i "video.mp4" -c copy {title_}.mp4 -loglevel quiet')
# (8).删除纯视频和纯音频
os.remove('video.mp4')
os.remove('audio.mp3')
# (9).降低请求频率,防止反爬
time.sleep(1)
多页视频的爬取
我们前面爬取了第一页的全部视频,那爬取多页的视频我们只需要找到每一页url的关系,然后利用循环即可。
第一页:https://search.bilibili.com/all?keyword=lemon
第二页:https://search.bilibili.com/all?keyword=lemon&from_source=webtop_search&spm_id_from=333.851&page=2
第三页:https://search.bilibili.com/all?keyword=lemon&from_source=webtop_search&spm_id_from=333.851&page=3
分析:
1.我们将第二页,第三页中&from_source=webtop_search&spm_id_from=333.851这一部分删除依旧可以访问到原来的页面
2.我们在第一页url的末尾加上&page=1依旧可以访问到原来的页面
综上:翻页的url可以是:https://search.bilibili.com/all?keyword=lemon&page=1
但是需要注意:B站视频的排序是实时变化的,有可能前面在第一页的视频后面就会到第二页,这样会导致我们下载视频的失败
解决方案:我们起初的想法是先对第一页发送请求,获取响应,解析出20个视频的url然后进行下载,再对第二页发送请求,获取响应,解析出20个视频的url然后下载,这样就会出现上面所说的问题;改变:我们先对每一页发送请求,获取响应,拿到每一页20个视频的url,最后在一起下载
import requests
from fake_useragent import FakeUserAgent
import os
from lxml import etree
import re
import time
if __name__ == '__main__':
# 确定关键字
kw_ = input('请输入要搜索视频的关键词:')
pages_ = int(input('请输入要下载视频的页数:'))
url_total = []
for page_ in range(pages_):
# 1.确定首页的url
url_ = f'https://search.bilibili.com/all?keyword={kw_}&page={page_ + 1}'
# 构造请求头
headers_ = {
'User-Agent': FakeUserAgent().random,
'Cookie': "_uuid=22033820-F579-BEBC-42CE-58C113E2551A56116infoc; buvid3=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; sid=8p6gkcm9; fingerprint=2446d5d91ca8dced483055bb370a3374; buvid_fp=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; buvid_fp_plain=743C4C8A-A496-4445-81C9-2F616EAC6EE895161infoc; CURRENT_FNVAL=80; blackside_state=1; rpdid=|(RY~~Y|Y~l0J'uY|m)lYYuJ; LIVE_BUVID=AUTO1816144322540275; CURRENT_QUALITY=64; arrange=matrix; PVID=5"
}
# 2.对首页发送请求,获取响应
response_ = requests.get(url_, headers=headers_)
str_data = response_.text
# 3.提取首页中所有视频的url(20个)
html_obj = etree.HTML(str_data)
url_list = html_obj.xpath("//li[@class='video-item matrix']/a/@href")
# 4.将视频url加入到总的url列表中,全部加入后统一下载
for url_ in url_list:
url_total.append(url_)
else:
print(f'第{page_ + 1}页加载成功!')
# 5.降低请求频率,防止反爬
time.sleep(2)
else:
print('全部视频url加载成功!')
# 6.对所有视频的url进行循环
for url_ in url_total:
# (1) 目标的确认url
url_ = 'https:' + url_
# 构造请求头
headers_ = {
'User-Agent': FakeUserAgent().random,
'Cookie': "_uuid=22033820-F579-BEBC-42CE-58C113E2551A56116infoc; buvid3=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; sid=8p6gkcm9; fingerprint=2446d5d91ca8dced483055bb370a3374; buvid_fp=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; buvid_fp_plain=743C4C8A-A496-4445-81C9-2F616EAC6EE895161infoc; CURRENT_FNVAL=80; blackside_state=1; rpdid=|(RY~~Y|Y~l0J'uY|m)lYYuJ; LIVE_BUVID=AUTO1816144322540275; CURRENT_QUALITY=64; PVID=2",
}
# (2).对视频主页发送请求,获取响应
response_ = requests.get(url_, headers=headers_)
str_data = response_.text
# html格式数据的转换
html_obj = etree.HTML(str_data)
# (3).解析(获取视频名称和视频音频的url)
# 获取视频名称
title_ = html_obj.xpath('//title/text()')[0]
# print(title_) # 【unnatural×Lemon】如今你依旧是我的光_哔哩哔哩_bilibili
title_ = re.findall(r'(.*?)_哔哩哔哩_bilibili', title_)[0]
# print(title_) # 【unnatural×Lemon】如今你依旧是我的光
# 视频名字处理,文件命名中有些特殊符号不能出现,我们在B站爬到的视频名字可能有一些非法字符出现
title_ = re.sub(u"([^\u4e00-\u9fa5\u0030-\u0039\u0041-\u005a\u0061-\u007a])", "", title_)
# 获取纯视频和纯音频的url
url_str = html_obj.xpath('//script[contains(text(),"window.__playinfo__")]/text()')[0]
# print(url_str)
# 纯视频url的提取
url_video = re.findall(r'"video":\[{"id":\d+,"baseUrl":"(.*?)"', url_str)[0]
# print(url_video)
url_audio = re.findall(r'"audio":\[{"id":\d+,"baseUrl":"(.*?)"', url_str)[0]
# print(url_audio)
# (4).设置携带跳转信息的请求头
headers_ = {
'User-Agent': FakeUserAgent().random,
'Cookie': "_uuid=22033820-F579-BEBC-42CE-58C113E2551A56116infoc; buvid3=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; sid=8p6gkcm9; fingerprint=2446d5d91ca8dced483055bb370a3374; buvid_fp=9C5D42BC-6379-43CB-9719-F75E09389E0D185002infoc; buvid_fp_plain=743C4C8A-A496-4445-81C9-2F616EAC6EE895161infoc; CURRENT_FNVAL=80; blackside_state=1; rpdid=|(RY~~Y|Y~l0J'uY|m)lYYuJ; LIVE_BUVID=AUTO1816144322540275; CURRENT_QUALITY=64; PVID=4",
'Referer': url_
}
# (5).发送请求,获取纯视频和纯音频的响应
response_video = requests.get(url_video, headers=headers_)
data_video = response_video.content
response_audio = requests.get(url_audio, headers=headers_)
data_audio = response_audio.content
# (6).保存
with open(f'video.mp4', 'wb') as f:
f.write(data_video)
with open(f'audio.mp3', 'wb') as f:
f.write(data_audio)
# (7).合成纯视频和纯音频
os.system(f'ffmpeg -i "audio.mp3" -i "video.mp4" -c copy {title_}.mp4 -loglevel quiet')
# (8).删除纯视频和纯音频
os.remove('video.mp4')
os.remove('audio.mp3')
print(f'{title_}.mp4下载成功!')
print('*' * 60)
# (9).降低请求频率,防止反爬
time.sleep(2)
else:
print(f'第{page_}页下载成功!')