0


【爬虫】Selenium爬取动态网页的base64图片

  • 文章简介 Selenium爬取动态网页的base64图片,并解决页面完整加载缓慢,base64字符串的获取和格式转码,一些页面不存在,部分照片无法加载等问题。后附源码。
  • 目录

1,需求

近日遇到需要爬取某网站的一些图片。图片所在页面为基础地址加上图片集的编号(类似:

www.XXX.com/img/001

,其为restful风格的网址,后面的数字为图片集的编号)。进入页面后,由动态加载网页,其技术以我现在的水平还无从得知。图片以base64风格嵌在img标签中。我的目标就是要爬取所有图集中的图片,将其保存为png或jpg格式。

2,环境和使用的技术

  • 使用的python版本是Python 3.7.0
  • 因为是动态加载的页面,在Chrome浏览器的network中抓包找不到相关加载base64的请求,目前我只知道可以使用Selenium
  • 获取加载完成后的网页源码后,使用bs4(BeautifulSoup)解析获取想要的base64字符串,当然也可以使用xpath等。

安装环境

如果未安装需要的库,可以直接进入CMD使用pip安装(PyCharm的话还要调整路径,VSCode可以直接安装,PyCharm可参考其他文章)

# 安装selenium
pip install selenium
# 安装bs4
pip install bs4

# 如果下载慢可以用国内镜像,只需要在上述命令后添加: -i 国内python镜像地址# 如:# 安装selenium
pip install selenium -i https://pypi.tuna.tsinghua.edu.cn/simple
# 安装bs4
pip install bs4 -i https://pypi.tuna.tsinghua.edu.cn/simple

3,难点

3.1,页面完整加载缓慢
  • 问题爬取的网站页面上个别部分加载比较慢,网页加载完成所需要的时间很长。页面加载过程线程阻塞,要页面停止加载才会执行后面的代码,每爬一个页面加载时间都很长,导致爬取速度很慢。
  • 解决方案虽然加载完成很慢,但是所需要的图片几秒钟就可以加载完成,所以我想着可不可以直接代码控制网页自动停止加载。- 首先我想到的是直接调用js的停止执行命令,但是发现线程阻塞,不能够使用'''这里只演示关键部分,表达思想,省略了一些代码和参数'''url = 'www.XXX.com/img/001'broswer = webdriver.Chrome()broswer.get(url) # 只有页面加载完成后,才能执行下面代码,也就是页面加载完成后,才能执行停止页面加载的代码...browser.execute_script('window.stop()')- 一通操作后,我在网上找到了让get不阻塞线程的方法,使得自己能够控制加载时间:from selenium.webdriver.common.desired_capabilities import DesiredCapabilitiesLOAD_TIME =3path ="C:\...\chrome.exe"options = Options()##### 设置get不阻塞线程 #####desired_capabilities = DesiredCapabilities.CHROMEdesired_capabilities["pageLoadStrategy"]="none"broswer = webdriver.Chrome(desired_capabilities=desired_capabilities)broswer.get(url)##### 设置睡眠时间,LOAD_TIME秒后执行停止加载的js命令 #####time.sleep(LOAD_TIME)broswer.execute_script("window.stop()")至此,加载完整页面缓慢的问题解决。
3.2,base64字符串的获取和格式转码
  • 问题1解析获取页面中的img标签的src属性值。src值其格式为:data:image/jpg;base64,base64字符串``````base64,后面的字符串才是要转码的内容,
  • 解决方案1直接使用python的str类split方法获取base64字符串:# img_src : 使用bs4获取的img标签的src属性值# base64str : 想要获取的base64字符串编码base64str = img_src.split('base64,')[1]
  • 问题2Base64编码需要转码为二进制编码后才能写为png或jpg文件,
  • 解决方案2使用base64类的转码函数转码:import base64# base64str 为获得的base64字符串imgdata = base64.b64decode(base64str)# imgdata 为转码后的二进制编码withopen(img_name +".jpg",'wb')as f: f.write(imgdata)
3.3,一些页面不存在
  • 问题测试过程中,发现一些图片编号的页面不存在,这样会导致获取页面标题失败,抛出异常,导致程序停止运行。
  • 解决方案通过if语句判断是否能够加载:title = soup.select('div[...] > div[class="title"]')iflen(title)==0:# 后续操作
3.4,部分照片无法加载
  • 问题一些照片无法加载,其对应的img标签src为加载时的标记图片的链接,无法正常解析,会导致抛出异常。
  • 解决方案尝试刷新页面,尝试若干次后直接放弃:具体会牵涉到较多逻辑,可以直接参考后面的参考代码的trytimes变量相关操作。大致逻辑:设置变量trytimes为页面刷新次数,初始值为0当判断出现图片加载失败时将其加一- 当某个页面爬取完成后将 trytimes 设为0- 当 trytimes 大于设置的尝试次数后直接跳到下一个页面爬取,放弃爬取无法加载的图片。(该页面爬取成功的图片还是会保存,再次加载的图片会保存或覆盖)

4,参考代码

这里直接吧代码贴出来吧,代码风格可能不太标准,欢迎指正:

from selenium import webdriver
from selenium.webdriver.chrome.options import Options
from bs4 import BeautifulSoup
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
import time
import base64
import os

defget_url(index):
    base_url ='https://XXX/img/'# 爬取的目标网站不变的部分
    url = base_url +str(index)# 网站拼接return url 

defget_broswer():
    path ="C:\Program Files\...\chrome.exe"# chrome 的可执行文件位置
    options = Options()# 无头浏览器效率更高# phantomjs 好像现在 selenium 已经不支持了# 只能使用Chrome浏览器的无头模式
    options.add_argument('--headless')
    options.add_argument('--disable-gpu')
    options.binary_location = path

    # 关闭线程阻塞
    desired_capabilities = DesiredCapabilities.CHROME
    desired_capabilities["pageLoadStrategy"]="none"
    broswer = webdriver.Chrome(chrome_options=options,executable_path='chromedriver.exe',desired_capabilities=desired_capabilities)return broswer

defget_content(broswer,url):
    broswer.get(url)
    time.sleep(5)
    broswer.execute_script("window.stop()")
    content = broswer.page_source
    return content

defget_imglist(content):
    soup = BeautifulSoup(content,'lxml')
    img_list = soup.select('div[class="..."] > img')return img_list

defget_base64(img):
    base64 = img.attrs.get('src')
    base64 = base64.split('base64,')iflen(base64)==1:return'false'return base64[1]defsave_img(base64str,foldname,num):# 解码图片
    imgdata = base64.b64decode(base64str)
    img_fold ="./img/"+foldname+"/"# 每个图集保存一个文件夹,如果文件夹不存在要创建文件夹,否则会报错if os.path.exists(img_fold)==False:
        os.mkdir(img_fold)#将图片保存为文件withopen(img_fold+str(num)+".jpg",'wb')as f:
        f.write(imgdata)# 获取图集名称,方便命名文件夹defget_title_name(cotent):
    soup = BeautifulSoup(content,'lxml')# 注意返回的是数组
    title = soup.select('div[class="content"] > div[class="title"]')iflen(title)==0:return'false'
    title_name = title[0].get_text()return title_name

# 主函数if __name__ =='__main__':
    broswer = get_broswer()#for index in range(100,4,-1):# 2304
    index =1000# 开始页    >大
    endpage =1# 结束页    >小 倒序爬取
    trytimes =0# 图片加载失败尝试次数
    dif =1# 爬取图集编号间隔,方便多开爬取,因为还没有学python多线程,就只想到这个法子,不在意速度这个可以不管while index >= endpage:
        flag =0if trytimes >2:print('TRYOVER_%d'% index)
            index -= dif
            trytimes =0continue
           
        url = get_url(index)
        content = get_content(broswer,url)
        img_list = get_imglist(content)
        foldname = get_title_name(content)if foldname =='false':
            trytimes +=1print('NOTFOUND_%d'%(index))continuefor i inrange(len(img_list)):
            img = img_list[i]  
            base64str = get_base64(img)if base64str =='false':# print('ERROLOAD_%d_%d' % (index,i))print('ERROLOAD_%d'%(index))
                flag =1continue
            save_img(base64str,foldname,i+1)if flag ==0:print('SUCCESS_%d_%s √'%(index,foldname))
            index -= dif
            trytimes =0if flag ==1:
            trytimes +=1print('OVER')

总之最后效果还是比较理想的,最后成功爬取了想要的图片:
在这里插入图片描述


本文转载自: https://blog.csdn.net/m0_55415167/article/details/126696351
版权归原作者 _Morain 所有, 如有侵权,请联系我们删除。

“【爬虫】Selenium爬取动态网页的base64图片”的评论:

还没有评论