本系列文章旨在分享python学习的心得,所涉及的视频和up主皆是值得与大家分享的,如有冒犯,望请谅解。
本期任务:使用Python下载B站大佬投稿页所有视频
【Python】第3节:Beautifulsoup+requests+selenium获取视频url
【Python】第4节:常见问题的解决(2020年3月23日哔哩哔哩将稿件的「av 号」变更为「BV 号」)
完整GitHub代码:BilibiliDownloader
上一节,我们实现了用multiprocessing实现多进程调用you-get下载哔哩哔哩技术视频,解决了CPU和带宽利用率低的问题。
本节我们将介绍使用Beautifulsoup+requests+selenium,实现简单的爬虫,获取大牛技术视频url。
任务描述
本节我们将以CodeSheep(博主最喜欢的Java up主!!!)的投稿页的79个视频url为目标进行爬取。
难点:网页解析,动态加载(反爬机制)
完整代码
import os
import time
import requests
from bs4 import BeautifulSoup
from selenium import webdriver
# 给定哔哩哔哩up主投稿页链接,爬取所有视频链接
class RequestsSpider:
def __init__(self, pages, **kwargs):
self.depth = pages # CodeSheep 投稿页功79个视频,分成3页
if kwargs.get('selenium'):
self.browser = self.selenium()
def selenium(self):
executable_path = r"输入chromedriver.exe的存放路径"
chrome_options = webdriver.ChromeOptions()
"""后台运行Chromedriver"""
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
browser = webdriver.Chrome(executable_path=executable_path, chrome_options=chrome_options)
"""全屏显示"""
browser.maximize_window()
time.sleep(5)
return browser
def downloader1(self, url):
“”“
需动态加载时, 使用selenium加载完整页面再返回
”“”
self.browser.get(url)
self.browser.implicitly_wait(20)
time.sleep(5)
return self.browser.page_source
def downloader(self, url):
“”“
无需动态加载时,直接使用requests.get()获取页面
”“”
try:
r = requests.get(url)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
def parse(self, info, html, **kwargs):
"""
网页解析: BeautifulSoup + re
"""
soup = BeautifulSoup(html, "html.parser")
nodes = soup.find("ul", {"class": "clearfix cube-list"}).find_all("li")
aids = [node.get("data-aid") for node in nodes]
info.extend(aids)
def spider(self):
"""
初始url与调度
"""
info = []
start_url = "https://space.bilibili.com/384068749/video?tid=0&page=" # 投稿页的url
for i in range(self.depth):
print("正在爬取第{0}页".format(i + 1))
try:
url = start_url + str(i + 1)
html = self.downloader1(url)
self.parse(info, html)
except Exception as e:
print(e)
return info
if __name__ == "__main__":
client = RequestsSpider(pages=3, selenium=True)
urls = client.spider()
client.browser.close()
urls = ["https://www.bilibili.com/video/av" + item for item in urls]
print(urls)
核心部分讲解
注意:
- 因为需要使用selenium进行动态加载,需要pip安装selenium(第三方库)
- 本任务使用的是chrome驱动(还有Firfox、Edge等),需要自行下载与本机Chrome版本号相一致的驱动,版本一致!版本一致!版本一致!
- 驱动下载传送门:Chrome驱动
RequestsSpider类:
实例化的时候,需要申明下载页数(pages),是否启用selenium进行动态加载(开启时爬虫效率一般远低于未开启状态)
def __init__(self, pages, **kwargs):
self.depth = pages # CodeSheep 投稿页功79个视频,分成3页
if kwargs.get('selenium'):
self.browser = self.selenium()
selenium基本配置:什么驱动存放位置、后台静默运行、全屏显示等
def selenium(self):
executable_path = r"输入chromedriver.exe的存放路径"
chrome_options = webdriver.ChromeOptions()
"""后台运行Chromedriver"""
chrome_options.add_argument('--headless')
chrome_options.add_argument('--no-sandbox')
browser = webdriver.Chrome(executable_path=executable_path, chrome_options=chrome_options)
"""全屏显示"""
browser.maximize_window()
time.sleep(5)
return browser
使用selenium动态加载网页内容
def downloader1(self, url):
“”“
需动态加载时, 使用selenium加载完整页面再返回
”“”
self.browser.get(url)
self.browser.implicitly_wait(20)
time.sleep(5)
return self.browser.page_source
使用request.get()快速加载网页
def downloader(self, url):
“”“
无需动态加载时,直接使用requests.get()获取页面
”“”
try:
r = requests.get(url)
r.raise_for_status()
r.encoding = r.apparent_encoding
return r.text
except:
return ""
网页解析:
chrome浏览器,点击F12打开网页源代码,通过简单分析,可以发现,
我们需要的内容位于li标签内data-aid的属性值:如图中的"95417273", 其中li位于class为clearfix cube-list的ul标签下。
将selenium加载的页面用BeautifulSoup熬成一锅"soup",
按照上面的分析,找到class为"clearfix cube-list"的ul标签下的所有li标签;
将每个li标签中的"data-aid"对应的值保存到aids中
拓展info即可完成当前页面的解析。
def parse(self, info, html, **kwargs):
"""
网页解析: BeautifulSoup + re
"""
soup = BeautifulSoup(html, "html.parser")
nodes = soup.find("ul", {"class": "clearfix cube-list"}).find_all("li")
aids = [node.get("data-aid") for node in nodes]
info.extend(aids)
爬虫主逻辑:分析投稿页不同页面的url,总结url规律;循环调用download1动态加载,并调用parse解析网页内容,结果更新到info中
def spider(self):
"""
初始url与调度
"""
info = []
start_url = "https://space.bilibili.com/384068749/video?tid=0&page=" # 投稿页的url
for i in range(self.depth):
print("正在爬取第{0}页".format(i + 1))
try:
url = start_url + str(i + 1)
html = self.downloader1(url)
self.parse(info, html)
except Exception as e:
print(e)
return info
效果
接下来,便可以使用前两节的方法进行视频下载了。
温馨提醒:下载过程固然能学到一些东西,静心感悟大佬想传达的意义才是核心,不要满足于当搬运工,共勉。
下期预告
【Python】整理matplotlib绘图库,用新的方式解读pyplot绘图过程