【爬虫】Selenium爬取动态网页的base64图片
Selenium爬取动态网页的base64图片 1,需求
近日遇到需要爬取某网站的一些图片。图片所在页面为基础地址加上图片集的编号(类似.XXX./img/001,其为restful风格的网址,后面的数字为图片集的编号)。进入页面后,由动态加载网页,其技术以我现在的水平还无从得知。图片以base64风格嵌在img标签中。我的目标就是要爬取所有图集中的图片,将其保存为png或jpg格式。
2,环境和使用的技术-
使用的python版本是Python 3.7.0
-
因为是动态加载的页面,在Chrome浏览器的ork中抓包找不到相关加载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./simple # 安装bs4 pip install bs4 -i https://pypi.tuna.tsinghua.edu./simple3,难点 3.1,页面完整加载缓慢
-
问题
爬取的网站页面上个别部分加载比较慢,网页加载完成所需要的时间很长。页面加载过程线程阻塞,要页面停止加载才会执行后面的代码,每爬一个页面加载时间都很长,导致爬取速度很慢。
-
解决方案
虽然加载完成很慢,所需要的图片几秒钟就可以加载完成,所以我想着可不可以直接代码控制网页自动停止加载。
-
我想到的是直接调用js的停止执行命令,发现线程阻塞,不能够使用
'''这里只演示关键部分,表达思想,省略了一些代码和参数''' url = '.XXX./img/001' broser = ebdriver.Chrome() broser.get(url) # 只有页面加载完成后,才能执行下面代码,也就是页面加载完成后,才能执行停止页面加载的代码... broser.execute_script('indo.s()')
-
一通操作后,我在网上找到了让get不阻塞线程的方法,使得自己能够控制加载时间
from selenium.ebdriver.mon.desired_capabilities import DesiredCapabilities LOAD_TIME = 3 path = "C:...chrome.exe" options = Options() ##### 设置get不阻塞线程 ##### desired_capabilities = DesiredCapabilities.CHROME desired_capabilities["pageLoadStrategy"] = "none" broser = ebdriver.Chrome(desired_capabilities=desired_capabilities) broser.get(url) ##### 设置睡眠时间,LOAD_TIME秒后执行停止加载的js命令 ##### time.sleep(LOAD_TIME) broser.execute_script("indo.s()")
至此,加载完整页面缓慢的问题解决。
-
-
问题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]
-
问题2
Base64编码需要转码为二进制编码后才能写为png或jpg文件,
-
解决方案2
使用base64类的转码函数转码:
import base64 # base64str 为获得的base64字符串 imgdata = base64.b64decode(base64str) # imgdata 为转码后的二进制编码 ith open(img_name + ".jpg",'b') as f: f.rite(imgdata)
-
问题
测试过程中,发现一些图片编号的页面不存在,这样会导致获取页面标题失败,抛出异常,导致程序停止运行。
-
解决方案
通过if语句判断是否能够加载:
title = soup.select('div[...] > div[]') if len(title) == 0: # 后续操作
-
问题
一些照片无法加载,其对应的img标签src为加载时的标记图片的链接,无法正常解析,会导致抛出异常。
-
解决方案
尝试刷新页面,尝试若干次后直接放弃
具体会牵涉到较多逻辑,可以直接参考后面的参考代码的trytimes变量相关操作。
大致逻辑
设置变量trytimes为页面刷新次数,初始值为0 当判断出现图片加载失败时将其加一 - 当某个页面爬取完成后将 trytimes 设为0 - 当 trytimes 大于设置的尝试次数后直接跳到下一个页面爬取,放弃爬取无法加载的图片。(该页面爬取成功的图片还是会保存,加载的图片会保存或覆盖)
这里直接吧代码贴出来吧,代码风格可能不太标准,欢迎指正:
from selenium import ebdriver from selenium.ebdriver.chrome.options import Options from bs4 import BeautifulSoup from selenium.ebdriver.mon.desired_capabilities import DesiredCapabilities import time import base64 import os def get_url(index): base_url = 'https://XXX/img/' # 爬取的目标网站不变的部分 url = base_url + str(index) # 网站拼接 return url def get_broser(): 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" broser = ebdriver.Chrome(chrome_options=options,executable_path='chromedriver.exe',desired_capabilities=desired_capabilities) return broser def get_content(broser,url): broser.get(url) time.sleep(5) broser.execute_script("indo.s()") content = broser.page_source return content def get_imglist(content): soup = BeautifulSoup(content,'lxml') img_list = soup.select('div[] > img') return img_list def get_base64(img): base64 = img.attrs.get('src') base64 = base64.split('base64,') if len(base64) == 1: return 'false' return base64[1] def save_img(base64str,foldname,num): # 解码图片 imgdata = base64.b64decode(base64str) img_fold = "./img/"+foldname+"/" # 每个图集保存一个文件夹,如果文件夹不存在要创建文件夹,否则会报错 if os.path.exists(img_fold) == False: os.mkdir(img_fold) #将图片保存为文件 ith open(img_fold+str(num)+".jpg",'b') as f: f.rite(imgdata) # 获取图集名称,方便命名文件夹 def get_title_name(cotent): soup = BeautifulSoup(content,'lxml') # 注意返回的是数组 title = soup.select('div[] > div[]') if len(title) == 0: return 'false' title_name = title[0].get_text() return title_name # 主函数 if __name__ == '__main__': broser = get_broser() #for index in range(100,4,-1): # 2304 index = 1000 # 开始页 >大 endpage = 1 # 结束页 >小 倒序爬取 trytimes = 0 # 图片加载失败尝试次数 dif = 1 # 爬取图集编号间隔,方便多开爬取,因为还没有学python多线程,就只想到这个法子,不在意速度这个可以不管 hile index >= endpage: flag = 0 if trytimes > 2: print('TRYOVER_%d' % index) index -= dif trytimes = 0 continue url = get_url(index) content = get_content(broser,url) img_list = get_imglist(content) foldname = get_title_name(content) if foldname == 'false': trytimes += 1 print('NOTFOUND_%d' % (index)) continue for i in range(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 = 1 continue save_img(base64str,foldname,i+1) if flag == 0: print('SUCCESS_%d_%s √' % (index,foldname)) index -= dif trytimes = 0 if flag == 1: trytimes += 1 print('OVER')
效果还是比较理想的,成功爬取了想要的图片:
空调维修
- 海信电视维修站 海信电视维修站点
- 格兰仕空调售后电话 格兰仕空调维修售后服务电
- 家电售后服务 家电售后服务流程
- 华扬太阳能维修 华扬太阳能维修收费标准表
- 三菱电机空调维修 三菱电机空调维修费用高吗
- 美的燃气灶维修 美的燃气灶维修收费标准明细
- 科龙空调售后服务 科龙空调售后服务网点
- 华帝热水器维修 华帝热水器维修常见故障
- 康泉热水器维修 康泉热水器维修故障
- 华凌冰箱维修电话 华凌冰箱维修点电话
- 海尔维修站 海尔维修站点地址在哪里
- 北京海信空调维修 北京海信空调售后服务
- 科龙空调维修 科龙空调维修故障
- 皇明太阳能售后 皇明太阳能售后维修点
- 海信冰箱售后服务 海信冰箱售后服务热线电话
- 海尔热水器服务热线