第一次自己尝试写爬虫,尝试爬取一个发了1w+图片的up的所有图片,由于他一直在更新,所以这个版本的代码去掉了自动翻页功能,最好扫码先录之后再继续程序,不然容易报错,B站会一直弹出登录界面。登录后,手动跳转的需要爬取的页面,就可以继续程序。(需要安装edge浏览器的驱动)
# bilibili单页下载(手动跳转并且不关闭页面,跳转下一页继续,遇到多图的时候跳过第一张然后下载)
import os
import time
import pyautogui
import requests
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver import ActionChains, Keys
import winsound
class Bilibili:
def __init__(self, Vol):
self.Vol = Vol
def mainrun(self):
# 创建 WebDriver 对象
wd = webdriver.Edge()
# 创建右键动作链接
actions = ActionChains(wd)
# 隐式等待时间
wd.implicitly_wait(3)
# 调用WebDriver 对象的get方法 可以让浏览器打开指定网址
wd.get(self.Vol)
# 保存主页面
mainWindow = wd.current_window_handle
# 获取图集总页数(返回maxPage)
maxPage = self.find_max_page(wd)
print('最大页数=', maxPage)
print('请验证登录,并移动至需要下载的页面')
# 先验证登录后继续
manual_input = True
while manual_input:
k = input()
if k == '0':
print("下载完成!")
break
# 点击图片
self.open_photos_in_new_window(wd, actions)
print('窗口打开完毕,是否开始下载?')
# 下载图片
self.open_and_download(wd)
# 关闭已下载的页面
self.close_window(wd, mainWindow)
print('下载完成!请手动切换到下一页,任意键继续...')
self.Sound_beep()
# 获取图集总页数函数
def find_max_page(self, wd):
Total_page_num = wd.find_element(By.CSS_SELECTOR, '.be-pager>li:nth-last-of-type(2)>a')
maxPage = int(Total_page_num.text)
return maxPage
# 点击打开所有图片函数
def open_photos_in_new_window(self, wd, actions):
# 选择图片
photos = wd.find_elements(By.CSS_SELECTOR, '.album-list__content a:nth-child(1)')
print('本页共有', len(photos), '张图片')
# 依次选择所有图片,从第一张开始,在while循环中,当a走到len(photos)+1时跳出循环
a = 0
while a < len(photos):
# 判断有没有产生弹窗,若有,则会点击关闭,并跳出本次循环,a就不能加一(为了提高运算速度可省略,这里不等待,摆烂
wd.implicitly_wait(0.1)
self.is_pop(wd)
# 在新标签页打开图片,b为第几张图片
photo = photos[a]
wd.implicitly_wait(3)
actions.key_down(Keys.CONTROL).click(photo).key_up(Keys.CONTROL).perform()
windows = wd.window_handles
print('共打开', len(windows) - 1, '个页面')
a += 1
# 下载图片URL函数
def downlaod_URL(self, wd):
# 使用get_attribute()方法获取对应属性的属性值,src属性值就是图片地址。
url = wd.find_element(By.CSS_SELECTOR, '.bili-gallery__content>img').get_attribute('src')
print(url)
# 获取图片信息以重命名图片
photo_Name = self.renamed_photo(wd, url)
# 通过requests发送一个get请求到图片地址,返回的响应就是图片内容
r = requests.get(url) # 将获取到的图片二进制流写入本地文件
# 对于图片类型的通过r.content方式访问响应内容,将响应内容写入baidu.png中
# 将path设置为当前工作目录
os.chdir('D:\\下载站\\新建图集')
print('当前下载路径:', os.getcwd())
# 写入图片
with open(photo_Name, 'wb') as f:
f.write(r.content)
# 打开大图并下载函数
def open_and_download(self, wd):
# 获取所有打开的页面
windows = wd.window_handles
print('窗口总数', len(windows))
for window_num in range(1, len(windows)): # 跳过主页面
print('正在下载第', window_num, '个页面')
wd.implicitly_wait(0.5)
# 将页面切换到新打开的动态
wd.switch_to.window(windows[window_num])
wd.implicitly_wait(3)
# 点击图片
wd.find_element(By.CSS_SELECTOR, '.bili-album__preview__picture>div').click()
# 如果发现裂图,则跳过本次循环,用continue,跳出所有循环break
is_ignor = self.ignored_next(wd)
if is_ignor == 1:
continue
# 点击查看大图
time.sleep(0.5)
wd.find_element(By.CSS_SELECTOR, "[class='bili-album__watch__control__option full-screen']").click()
# 如果有下一页点击下一页
if not wd.find_elements(By.CSS_SELECTOR, '.bili-gallery__pagination__total'):
print('【只有一张图】')
self.downlaod_URL(wd)
else:
gallery_nums = int(
wd.find_element(By.CSS_SELECTOR, '.bili-gallery__pagination__total').text.replace('/ ', ''))
print('【多图共', gallery_nums, '张】')
i = 1
while i < gallery_nums:
# 先翻一页跳过第一章
wd.find_element(By.CSS_SELECTOR, '.bili-gallery__nav__next').click()
self.downlaod_URL(wd)
# 循环加1
i += 1
time.sleep(3)
# 获取并重命名函数
def renamed_photo(self, wd, url):
# 获取图片时间
photo_time = wd.find_element(By.CSS_SELECTOR, '[data-module="time"]').text
photo_time1 = photo_time[0:13] + '-' + photo_time[14:16]
# 获取图片名称(并剔除无效字符)
photo_man = wd.find_element(By.CSS_SELECTOR, '.bili-rich-text__content>span').text
photo_man = photo_man[0:25]
photo_man = photo_man.replace(':', '-')
photo_man = photo_man.replace('\n', '')
photo_man = photo_man.replace(':', '')
photo_man = photo_man.replace('/', '')
photo_man = photo_man.replace('**', '')
photo_man = photo_man.replace('|', '')
url = url.replace('https://', '')
url = url.replace('/', '-')
# 重命名图片
photo_Name = "[" + photo_time1 + "]" + photo_man + url
print(photo_Name)
return photo_Name
# 切换和关闭窗口函数(下载完一页后将其关闭)
def close_window(self, wd, mainWindow):
windows = wd.window_handles
# wd.switch_to.window(windows[1])
for handle in windows:
wd.switch_to.window(handle)
if '动态' in wd.title:
wd.close()
wd.switch_to.window(mainWindow)
# 判断弹窗函数
def is_pop(self, wd):
# 搜寻广告元素
ads = wd.find_elements(By.CSS_SELECTOR, '.bili-mini .bili-mini-close')
# 用复数形式找不到也不至于报错
for ad in ads:
if ad:
print('找到弹窗')
# 找到弹窗后点击关闭
ad.click()
# 裂图跳过函数
def ignored_next(self, wd):
is_ignors = wd.find_elements(By.CSS_SELECTOR,
'.bili-album__error')
for is_ignor in is_ignors:
# 如果找到了这个元素,则返回1
if is_ignor:
is_ignor = 1
print('【这张图裂了。。。。。。】')
return is_ignor
#发出提示音
def Sound_beep(self):
duration = 1000 # millisecond
freq = 440 # Hz
winsound.Beep(freq, duration)
# 创建一个实例(测试代码),调用mainrun
Bilibili(Vol='https://space.bilibili.com/1581895468/album').mainrun()