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,一经查实,立即删除!

相关文章

nessus扫描原理

Nessus的扫描原理主要基于漏洞检测和漏洞利用两个方面 漏洞检测:Nessus利用其内置的漏洞检测插件库,通过与目标系统进行交互,发送特定的测试数据包来验证漏洞。这些数据包可能包含特定的漏洞利用代码,用于触发目标系统中的漏洞。N…

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

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

vue3封装接口请求 useAxiosWithCancel

实际项目需求,类似机器人对话。生成对话过程中有个停止生成。点击停止生成中断请求。axios提供两种方法 1 .使用 AbortController2CancelToken 取消请求机制说明: 创建CancelToken源:在每个请求发出前,使用CancelToken.source()创…

“选择最佳数据库解决方案:MySQL、SQL Server 和 PostgreSQL 的比较与实际应用指南“

目录 典型中高端数据库服务器硬件配置 CPU 内存 存储 网络 操作系统 不同数据库系统在上述硬件上的性能表现 MySQL PostgreSQL SQL Server 具体硬件配置示例 示例配置1:中小型Web应用 示例配置2:复杂查询和事务处理 示例配置3:…

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

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

AJAX 数据库

AJAX 数据库 1. 引言 AJAX(Asynchronous JavaScript and XML)是一种流行的网络开发技术,它允许网页在不重新加载整个页面的情况下与服务器交换数据和更新部分网页内容。AJAX技术与数据库的结合,为现代网络应用提供了更加丰富和动…

如何移植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…

OpenCV--滤波器(一)

低通滤波器 代码和笔记 代码和笔记 import cv2 import numpy as np""" 滤波器--用于图像处理的重要工具,它们可以根据图像中像素的邻域信息来修改像素值,以实现去噪、模糊、锐化、边缘检测等效果。低通滤波器(Low-pass Filte…

Word 文本框技巧2则

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

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

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

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

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

java每日一题:static与final的区别

static和final在Java中都是用于修饰类、方法、变量等的修饰符,但它们各自有不同的作用和含义。以下是关于static与final修饰后的主要区别: 一、static修饰符 含义: static是静态修饰符,表示所修饰的成员(变量、方法…

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

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

反射机制详解

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