Python爬虫小白入门(四)PhatomJS+Selenium篇下

一、前言


前文介绍了PhatomJS 和Selenium 的用法,工具准备完毕,我们来看看如何使用它们来改造我们之前写的小爬虫。

我们的目的是模拟页面下拉到底部,然后页面会刷出新的内容,每次会加载10张新图片。

大体思路是,用Selenium + PhatomJS 来请求网页,页面加载后模拟下拉操作,可以根据想要获取的图片多少来选择下拉的次数,然后再获取网页中的全部内容。

二、爬虫实战改造


2.1 模拟下拉操作

要想实现网页的下拉操作,需要使用Selenium的一个方法来执行js代码。该方法如下:
driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")

由此可见,使用execute_script方法可以调用JavaScript API在一个加载完成的页面中去执行js代码。可以做任何你想做的操作哦,只要用js写出来就可以了。

改造的爬虫的第一步就是封装一个下拉方法,这个方法要能控制下拉的次数,下拉后要有等待页面加载的时间,以及做一些信息记录(在看下面的代码前自己先想一想啦):

    def scroll_down(self, driver, times):for i in range(times):print("开始执行第", str(i + 1),"次下拉操作")driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")  #执行JavaScript实现网页下拉倒底部print("第", str(i + 1), "次下拉操作执行完毕")print("第", str(i + 1), "次等待网页加载......")time.sleep(20)  # 等待20秒(时间可以根据自己的网速而定),页面加载出来再执行下拉操作

这部分做完之后就是修改页面爬取逻辑,之前是使用request 请求网址,然后找到图片url所在位置,再然后挨个请求图片url。现在,我们要首先使用Selenium 请求网址,然后模拟下拉操作,等待页面加载完毕再遵循原有逻辑挨个请求图片的url。

逻辑部分改造如下:

    def get_pic(self):print('开始网页get请求')# 使用selenium通过PhantomJS来进行网络请求driver = webdriver.PhantomJS()driver.get(self.web_url)self.scroll_down(driver=driver, times=5)  #执行网页下拉到底部操作,执行5次print('开始获取所有a标签')all_a = BeautifulSoup(driver.page_source, 'lxml').find_all('a', class_='cV68d')  #获取网页中的class为cV68d的所有a标签print('开始创建文件夹')self.mkdir(self.folder_path)  #创建文件夹print('开始切换文件夹')os.chdir(self.folder_path)   #切换路径至上面创建的文件夹print("a标签的数量是:", len(all_a))  #这里添加一个查询图片标签的数量,来检查我们下拉操作是否有误for a in all_a: #循环每个标签,获取标签中图片的url并且进行网络请求,最后保存图片img_str = a['style'] #a标签中完整的style字符串print('a标签的style内容是:', img_str)first_pos = img_str.index('"') + 1  #获取第一个双引号的位置,然后加1就是url的起始位置second_pos = img_str.index('"', first_pos)  #获取第二个双引号的位置img_url = img_str[first_pos: second_pos] #使用Python的切片功能截取双引号之间的内容#注:为了尽快看到下拉加载的效果,截取高度和宽度部分暂时注释掉,因为图片较大,请求时间较长。##获取高度和宽度的字符在字符串中的位置#width_pos = img_url.index('&w=')#height_pos = img_url.index('&q=')#width_height_str = img_url[width_pos : height_pos] #使用切片功能截取高度和宽度参数,后面用来将该参数替换掉#print('高度和宽度数据字符串是:', width_height_str)#img_url_final = img_url.replace(width_height_str, '')  #把高度和宽度的字符串替换成空字符#print('截取后的图片的url是:', img_url_final)#截取url中参数前面、网址后面的字符串为图片名name_start_pos = img_url.index('photo')name_end_pos = img_url.index('?')img_name = img_url[name_start_pos : name_end_pos]self.save_img(img_url, img_name) #调用save_img方法来保存图片

逻辑修改完毕。执行一下,发现报错了,图片的url截取错误。那就看看到底是为什么,先输出找到url看看:

看看输出内容,发现通过PhatomJS 请求网页获得的url(https://blablabla) 中,竟然没有双引号,这跟使用Chrome 看到的不一样。好吧,那就按照没有双引号的字符串重新截取图片的url。

其实,我们只需要把url的起始位置和结束逻辑改成通过小括号来获取就可以了:

first_pos = img_str.index('(') + 1  #起始位置是小括号的左边
second_pos = img_str.index(')')  #结束位置是小括号的右边

好啦,这次获取图片的url就没什么问题了,程序可以顺利的执行。

但是,细心的小伙伴儿肯定发现了,我们这个爬虫在爬取的过程中中断了,或者过一段时间网站图片更新了,我再想爬,有好多图片都是重复的,却又重新爬取一遍,浪费时间。那么我们有什么办法来达到去重的效果呢?

2.2 去重

想要做去重,无非就是在爬取图片的时候记录图片名字(图片名字是唯一的)。在爬取图片前,先检查该图片是否已经爬取过,如果没有,则继续爬取,如果已经爬取过,则不进行爬取。

去重的逻辑如上,实现方式的不同主要体现在记录已经爬取过的信息的途径不同。比如可以使用数据库记录、使用log文件记录,或者直接检查已经爬取过的数据信息。

根据爬取内容的不同实现方式也不同。我们这里先使用去文件夹下获取所有文件名,然后在爬取的时候做对比。
后面的爬虫实战再使用其他方式来做去重。

单从爬取unsplash 网站图片这个实例来看,需要知道文件夹中所有文件的名字就够了。

Python os 模块中有两种能够获取文件夹下所有文件名的方法,分别来个示例:

for root, dirs, files in os.walk(path):for file in files:print(file)
for file in os.listdir(path):print(file)

其中,os.walk(path) 是获取path文件夹下所有文件名和所有其子目录中的文件夹名和文件名。
而os.listdir(path) 只是获取path文件夹下的所有文件的名字,并不care其子文件夹。

这里我们使用os.listdir(path) 就能满足需求。

写一个获取文件夹内所有文件名的方法:

    def get_files(self, path):pic_names = os.listdir(path)return pic_names

因为在保存图片之前就要用到文件名的对比,所以把save_img方法修改了一下,在方法外部拼接图片名字,然后直接作为参数传入,而不是在图片存储的过程中命名:

    def save_img(self, url, file_name): ##保存图片print('开始请求图片地址,过程会有点长...')img = self.request(url)print('开始保存图片')f = open(file_name, 'ab')f.write(img.content)print(file_name,'图片保存成功!')f.close()

为了更清晰去重逻辑,我们每次启动爬虫的时候检测一下图片存放的文件夹是否存在,如果存在则检测与文件夹中的文件做对比,如果不存在则不需要做对比(因为是新建的嘛,文件夹里面肯定什么文件都没有)。
逻辑修改如下,是新建的则返回True,不是新建的则返回False:

    def mkdir(self, path):  ##这个函数创建文件夹path = path.strip()isExists = os.path.exists(path)if not isExists:print('创建名字叫做', path, '的文件夹')os.makedirs(path)print('创建成功!')return Trueelse:print(path, '文件夹已经存在了,不再创建')return False

然后修改我们的爬取逻辑部分:
主要是添加获取的文件夹中的文件名列表,然后判断图片是否存在。

is_new_folder = self.mkdir(self.folder_path)  #创建文件夹,并判断是否是新创建
file_names = self.get_files(self.folder_path)  #获取文件家中的所有文件名,类型是list
            if is_new_folder:self.save_img(img_url, img_name)  # 调用save_img方法来保存图片else:if img_name not in file_names:self.save_img(img_url, img_name)  # 调用save_img方法来保存图片else:print("该图片已经存在:", img_name, ",不再重新下载。")

好了,来个完整版代码(https://github.com/AlbertShoubinLi/UnsplashSpider)):

from selenium import webdriver  #导入Selenium
import requests
from bs4 import BeautifulSoup  #导入BeautifulSoup 模块
import os  #导入os模块
import timeclass BeautifulPicture():def __init__(self):  #类的初始化操作self.headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/22.0.1207.1 Safari/537.1'}  #给请求指定一个请求头来模拟chrome浏览器self.web_url = 'https://unsplash.com'  #要访问的网页地址self.folder_path = 'C:\D\BeautifulPicture'  #设置图片要存放的文件目录def get_pic(self):print('开始网页get请求')# 使用selenium通过PhantomJS来进行网络请求driver = webdriver.PhantomJS()driver.get(self.web_url)self.scroll_down(driver=driver, times=3)  #执行网页下拉到底部操作,执行3次print('开始获取所有a标签')all_a = BeautifulSoup(driver.page_source, 'lxml').find_all('a', class_='cV68d')  #获取网页中的class为cV68d的所有a标签print('开始创建文件夹')is_new_folder = self.mkdir(self.folder_path)  #创建文件夹,并判断是否是新创建print('开始切换文件夹')os.chdir(self.folder_path)   #切换路径至上面创建的文件夹print("a标签的数量是:", len(all_a))   #这里添加一个查询图片标签的数量,来检查我们下拉操作是否有误file_names = self.get_files(self.folder_path)  #获取文件家中的所有文件名,类型是listfor a in all_a: #循环每个标签,获取标签中图片的url并且进行网络请求,最后保存图片img_str = a['style'] #a标签中完整的style字符串print('a标签的style内容是:', img_str)first_pos = img_str.index('(') + 1second_pos = img_str.index(')')img_url = img_str[first_pos: second_pos] #使用Python的切片功能截取双引号之间的内容# 注:为了尽快看到下拉加载的效果,截取高度和宽度部分暂时注释掉,因为图片较大,请求时间较长。#获取高度和宽度的字符在字符串中的位置# width_pos = img_url.index('&w=')# height_pos = img_url.index('&q=')# width_height_str = img_url[width_pos : height_pos] #使用切片功能截取高度和宽度参数,后面用来将该参数替换掉# print('高度和宽度数据字符串是:', width_height_str)# img_url_final = img_url.replace(width_height_str, '')  #把高度和宽度的字符串替换成空字符# print('截取后的图片的url是:', img_url_final)#截取url中参数前面、网址后面的字符串为图片名name_start_pos = img_url.index('.com/') + 5  #通过找.com/的位置,来确定它之后的字符位置name_end_pos = img_url.index('?')img_name = img_url[name_start_pos : name_end_pos] + '.jpg'img_name = img_name.replace('/','')  #把图片名字中的斜杠都去掉if is_new_folder:self.save_img(img_url, img_name)  # 调用save_img方法来保存图片else:if img_name not in file_names:self.save_img(img_url, img_name)  # 调用save_img方法来保存图片else:print("该图片已经存在:", img_name, ",不再重新下载。")def save_img(self, url, file_name): ##保存图片print('开始请求图片地址,过程会有点长...')img = self.request(url)print('开始保存图片')f = open(file_name, 'ab')f.write(img.content)print(file_name,'图片保存成功!')f.close()def request(self, url):  #返回网页的responser = requests.get(url)  # 像目标url地址发送get请求,返回一个response对象。有没有headers参数都可以。return rdef mkdir(self, path):  ##这个函数创建文件夹path = path.strip()isExists = os.path.exists(path)if not isExists:print('创建名字叫做', path, '的文件夹')os.makedirs(path)print('创建成功!')return Trueelse:print(path, '文件夹已经存在了,不再创建')return Falsedef scroll_down(self, driver, times):for i in range(times):print("开始执行第", str(i + 1),"次下拉操作")driver.execute_script("window.scrollTo(0, document.body.scrollHeight);")  #执行JavaScript实现网页下拉倒底部print("第", str(i + 1), "次下拉操作执行完毕")print("第", str(i + 1), "次等待网页加载......")time.sleep(30)  # 等待30秒,页面加载出来再执行下拉操作def get_files(self, path):pic_names = os.listdir(path)return pic_namesbeauty = BeautifulPicture()  #创建类的实例
beauty.get_pic()  #执行类中的方法

注释写的很详细,有任何问题可以留言。

三、后语


该实战就先到这里,需要注意的是Unsplash 这个网站经常不稳定,小伙伴有遇到请求异常的情况可以多试几次。

如果你也喜欢编程,想通过学习Python获取更高薪资,这里给大家分享一份Python学习资料。

👉Python所有方向的学习路线👈
Python所有方向路线就是把Python常用的技术点做整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照上面的知识点去找对应的学习资源,保证自己学得较为全面。(全套教程文末领取)
在这里插入图片描述

👉Python学习视频600合集👈

在这里插入图片描述

温馨提示:篇幅有限,已打包文件夹,获取方式在:文末

👉学习软件👈
工欲善其事必先利其器。学习Python常用的开发软件都在这里了,还有环境配置的教程,给大家节省了很多时间。
在这里插入图片描述

👉Python70个实战练手案例&源码👈
在这里插入图片描述

👉 这份完整版的Python全套学习资料已经上传,朋友们如果需要可以直接下方领取
【保证100%免费】
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/30793.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RuoYi-Vue-Plus(vue3)点击字典详情页面报404

复现过程: 1.新增菜单目录,设置目录路由地址为data 2.打开字典管理 -》 点击字典数据 -》 页面404 路由地址不允许重复 如果设置的地址重复就把字典顶掉l 建菜单的时候 这个路由名称不要使用data 后添加的/data 默认首字母大写 name为Data 覆盖了字典详…

网络文化经营许可证:互联网时代的护航之舵

文网文由中国文化和旅游部(原文化部)颁发,旨在对网络文化活动进行规范管理,确保文化内容的健康积极。通过对文化市场的严格监管,文网文有效杜绝了低俗、违法、侵权等不良现象的发生。网络文化市场的良性发展&#xff0…

2024年【N1叉车司机】作业考试题库及N1叉车司机实操考试视频

题库来源:安全生产模拟考试一点通公众号小程序 2024年N1叉车司机作业考试题库为正在备考N1叉车司机操作证的学员准备的理论考试专题,每个月更新的N1叉车司机实操考试视频祝您顺利通过N1叉车司机考试。 1、【多选题】《中华人民共和国特种设备安全法》第…

计算机专业毕设-springboot论坛系统

1 项目介绍 基于SSM的论坛网站:后端 SpringBoot、Mybatis,前端thymeleaf,具体功能如下: 基本功能:登录注册、修改个人信息、修改密码、修改头像查看帖子列表:按热度排序、按更新时间排序、查看周榜月榜查…

golang去掉前后空格

str : " ce s "str strings.TrimSpace(str)fmt.Printf("--%v--", str)

如何移植libwebsockets

libwebsockets是一个高性能的开源C语言库,专为实现WebSocket协议及相关的HTTP协议而设计。它不仅使开发者能够在客户端与服务器端轻松构建WebSocket连接,还可以用作标准HTTP服务器。WebSocket是一种在单个TCP连接上进行全双工通信的协议,可以…

卷积神经网络(CNN)理解

1、引言(卷积概念) 在介绍CNN中卷积概念之前,先介绍一个数字图像中“边缘检测edge detection”案例,以加深对卷积的认识。图中为大小8X8的灰度图片,图片中数值表示该像素的灰度值。像素值越大,颜色越亮&…

视觉应用线扫相机速度反馈(倍福CX7000PLC应用)

运动控制实时总线相关内容请参考运动控制专栏,这里不再赘述 1、运动控制常用单位u/s运动控制单位[u/s]介绍_运动控制 unit是什么单位-CSDN博客文章浏览阅读176次。运动控制很多手册上会写这样的单位,这里的u是英文单词unit的缩写,也就是单位的意思,所以这里的单位不是微米…

编程精粹—— Microsoft 编写优质无错 C 程序秘诀 05:糖果机接口

这是一本老书,作者 Steve Maguire 在微软工作期间写了这本书,英文版于 1993 年发布。2013 年推出了 20 周年纪念第二版。我们看到的标题是中译版名字,英文版的名字是《Writing Clean Code ─── Microsoft’s Techniques for Developing》&a…

Word 文本框技巧2则

1 调整大小 一种方法是,选中文本框,周围出现锚点,然后用鼠标拖动来调整大小; 精确按数值调整,在 格式 菜单下有多个分栏,一般最后一个分栏是 大小 ;在此输入高度和宽度的数值,来调整…

MySQL的数据存储一定是基于硬盘吗?

一、典型回答 不是的,MySQL也可以基于内存的,即MySQL的内存表技术。它允许将数据和索引存储在内存中,从而提高了检验速度和修改数据的效率。优点包括具有快速响应的查询性能和节约硬盘存储空间。此外,使用内存表还可以实现更高的复…

【C++】类和对象(三)构造与析构

文章目录 一、类的6个默认成员函数二、 构造函数干嘛的?语法定义特性综上总结什么是默认构造函数? 三、析构函数干嘛的 ?语法定义析构顺序 一、类的6个默认成员函数 如果一个类中什么成员都没有,简称为空类。空类中并不是真的什么…

Mac数据如何恢复?3 款最佳 Mac 恢复软件

如果您认为 Mac 上已删除的文件永远丢失了,那您就大错特错了!实际上,即使您清空了 Mac 上的垃圾箱,也有许多解决方案可以帮助您恢复已删除的文件。最好的解决方案之一是 Mac 恢复删除软件。最好的Mac 恢复删除应用程序可以轻松准确…

反射机制详解

✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏:Java从入门到精通 ✨特色专栏&#xff…

SM9加密算法:安全、高效的国产密码技术

随着信息技术的飞速发展,网络安全问题日益凸显。加密算法作为保障信息安全的核心技术,受到了广泛关注。在我国,一种名为SM9的加密算法逐渐崭露头角,凭借其卓越的安全性能和高效计算能力,成为了新一代国产密码技术的代表…

常用的Java日志框架:Log4j、SLF4J和Logback

日志是软件开发中不可或缺的一部分,它有助于记录应用程序的运行状态、调试问题和监控系统。Java中有多个流行的日志框架,如Log4j、SLF4J和Logback。 一、Log4j 1.1 什么是Log4j? Log4j是Apache基金会开发的一个开源日志框架,它…

Milvus跨集群数据迁移

将 Milvus 数据从 A 集群(K8S集群)迁到 B 集群(K8S集群),解决方案很多,这里提供一个使用官方 milvus-backup 工具进行数据迁移的方案。 注意:此方案为非实时同步方案,但借助 MinIO 客…

C++基础std::bind

目录 说明 举例子: 说明 std::bind是一个函数模板,用于创建一个可调用对象,该对象可以在稍后的时候被调用。bind的作用是将函数与参数绑定在一起,在调用时可以自动传入预定的参数值。 std::bind的基本语法如下: templ…

1. zabbix监控服务器部署

zabbix监控服务器部署 一、监控的作用1、监控的方式2、zabbix监控获取数据的方式 二、zabbix server部署1、确保时间同步2、添加epel源3、添加zabbix仓库4、安装zabbix服务端软件5、在数据库创建zabbix需要的表、授权用户6、编辑zabbix server配置文件,指定数据库连…

在WordPress中使用AI的实用方法:入门级

随着人工智能(AI)的快速发展,WordPress平台上引入了越来越多的工具和插件,为网站管理员提供了强大的功能。这些工具不仅可以提升网站的用户体验,还能简化网站管理过程。本文将介绍几种在WordPress中使用AI的实用方法&a…