python爬虫之selenium模拟浏览器
1.前言
之前在异步加载(AJAX)网页爬虫的时候提到过,爬取这种ajax技术的网页有两种办法:一种就是通过浏览器审查元素找到包含所需信息网页的真实地址,另一种就是通过selenium模拟浏览器的方法[1]。当时爬的是豆瓣,比较容易分析出所需信息的真实地址,不过一般大点的网站像淘宝这种是不好分析的,所以利用selenium模拟浏览器的行为来爬取数据是一个比较可行的办法。
2.selenium基础
Selenium是一个用于Web应用程序测试的工具。Selenium测试直接运行在浏览器中,就像真正的用户在操作一样。支持的浏览器包括IE(7, 8, 9, 10, 11), Mozilla Firefox,Safari,Google Chrome,Opera等。这个工具的主要功能包括:测试与浏览器的兼容性——测试你的应用程序看是否能够很好得工作在不同浏览器和操作系统之上。测试系统功能——创建回归测试检验软件功能和用户需求。支持自动录制动作和自动生成 .Net、Java、Perl等不同语言的测试脚本。 ——百度百科对selenium的定义
可以看到selenium最开始不是用于爬虫的而是一款web开发的测试工具,简单点解释:可以操控浏览器做人类浏览网页的动作,比如:鼠标点击、拖拽;用户输入,表单填充;Cookie;等等,当然通过它得到ajax网页的真实数据也不在话下。
selenium教程:https://www.yiibai.com/selenium
3.安装selenium+Chromedriver
3.1 安装selenium
pip install selenium
3.2 安装ChromeDriver
不要使用PhantomJS,已经停止维护。最新版的selenium也不再支持了,继续使用会报这个:Warning: Selenium support for PhantomJS has been deprecated, please use headless versions of Chrome or Firefox instead
谷粉当然选择Chrome了,Selenium可以操作浏览器,但是需要借助ChromeDriver驱动来实现。ChromeDriver通过chrome的自动代理框架控制浏览器。
①查看Chrome浏览器版本信息,点右上角三个白点,选择帮助,再选择关于Chrome就可以看到当前的Chrome版本信息。
77.0.3865.120就是当前的版本信息。
②再打开http://npm.taobao.org/mirrors/chromedriver/选择对应版本的Chromedriver。
选择查看notes.txt,可以看到是支持77的,找到浏览器对应版本的Chromedriver了。
下载32位的就可以了,占资源更少,64位也可以用。下载好后解压文件看到:
别双击运行,推荐放到python安装路径下,比如我的python在D盘。(不和python放一起也行就是以后使用不方便。)
完成后在cmd下输入Chromedriver验证是否安装成功:
到这里所有工具配置完毕。
4.利用selenium+Chromedriver模拟操作
4.1 使用selenium打开百度,并搜索关键字“python”
selenium支持很多的浏览器,但是如果想要声明并调用浏览器则需要:
# 从selenium导入webdriver
from selenium import webdriver
# 声明调用哪个浏览器,本文使用的是Chrome,其他浏览器同理。有如下两种方法及适用情况
driver = webdriver.Chrome()#把Chromedriver放到python安装路径里
driver = webdriver.Chrome( executable_path="#放入Chromedriver的路径")#没有把Chromedriver放到python安装路径
运行下面代码,会自动打开浏览器,然后访问百度:
注:driver.get()类似于requests.get(),不过区别就是driver.get()请求的源代码中还包含异步加载的数据,即“所见即所得”,这也是selenium模拟浏览器在爬虫中存在的意义。
上图的百度界面就是通过selenium+Chromedriver打开的,显示Chrome正在受到自动测试软件的控制。抓取完数据,记得要关掉driver,不然会占内存:
driver.close()#浏览器可以同时打开多个界面,close只关闭当前界面,不退出浏览器
driver.quit()#退出整个浏览器
下面代码实现打开百度界面等待三秒后退出:
基本的打开退出学会了,再来看看人工搜索关键词的流程:打开浏览器>>>打开百度>>>找到搜索输入框>>>输入要搜索的关键词>>>点击搜索按钮
前两个步骤上面代码已经实现了,再来看看找到输入框,和之前爬虫时寻找所需元素信息的套路一样,把鼠标放到输入框上,右击选择检查,很容易就找到了输入框的标签信息,如下图:
可以看到输入框的标签信息:
<input id="kw" name="wd" class="s_ipt" value="" maxlength="255" autocomplete="off">
之前文章中定位元素信息用的最多的就是beautifulsoup的find和find_all,现在是通过Chromedriver打开浏览器的,人家也就自己的定位元素的一套办法:
红色框里就是selenium+webdriver定位元素的办法,常用办法如下:
- find_elements_by_id # ID
- find_elements_by_class_name # class
- find_elements_by_tag_name # 标签名
- find_elements_by_name # name
- find_elements_by_link_text # a标签中的text查找(精确匹配)
- find_elements_by_partial_link_text #a标签中的text查找(部分匹配即可)
- find_elements_by_css_selector # css选择器查找
- find_elements_by_xpath # find_elements_by_xpath("//input")
注:find_element_by_将获取find_elements_by_的第一个元素,记住element是获取单个元素,elements是获取符合条件的多个元素信息。
上面的办法通过标签名称还有标签属性定位元素应该已经很熟悉了,遇到复杂网页xpath的定位功能也是很强大,这两个办法应对绝大多数网页已经足够,第7种方法:通过CSS选择器获取信息,学习一下。
有了这些方法,获取输入框信息应该已经很简单了,如下代码:
#通过id信息来获取
input = driver.find_element_by_id('kw')
#还可以通过xpath来获取
input = =driver.find_element_by_xpath(//*[@id="kw"])
最后两个步骤,代码实现如下:
input.send_keys('python')#在输入框里输入搜索关键词“python”
searchButton = driver.find_element_by_id('su') #获取搜索按钮
searchButton.click()#点击搜索按钮
到这里,selenium打开百度,并搜索关键字“python”就已经实现了,完整代码如下:
4.2 使用selenium爬取京东商品信息
京东是一个异步加载的网页,移动滚动条会一直加载数据,和之前说的豆瓣电影排行榜的网页是类似的[2]。继续重复上面在百度输入关键词搜索的步骤,获取京东页面>>>获取搜索输入框>>>输入搜索关键词>>>点击搜索按钮>>>移动滚动条到网页底部>>>获取整页商品的名称和价格信息。移动滚动条到网页底部,是采用js脚本直接操作[3],完整代码详细及注释:
from selenium import webdriver # 从selenium导入webdriver
import time
url = 'https://www.jd.com/'
driver = webdriver.Chrome() # 声明调用Chrome
driver.get(url) # 获取京东页面
input = driver.find_element_by_xpath('//*[@id="key"]')#获取输入框
input.send_keys('iphone')#输入搜索关键词
driver.find_element_by_xpath('//*[@id="search"]/div/div[2]/button').click()#点击搜索按钮
js="var q=document.documentElement.scrollTop=100000"#将滚动条移动到页面底部的js语句
driver.execute_script(js)#执行上面移动滚动条的js语句
time.sleep(5)#睡眠,等待异步加载的数据加载完成
#获取文本信息时selenium中的xpath语法与之前区别:前面是在xpath路径后加/text(),而selenium是整个语句后面加.text
prices = driver.find_elements_by_xpath('//*[@id="J_goodsList"]/ul/li/div/div[3]/strong/i')
names = driver.find_elements_by_xpath('//*[@id="J_goodsList"]/ul/li/div/div[4]/a/em')
print(len(names))#京东每页有60个商品信息,检查爬取数据是否完整
for name,price in zip(names,prices):
print(name.text.replace('\n',''),price.text)
driver.quit()#退出
运行结果:
显示60条商品信息全部爬取成功。
5.小结
本文只是对selenium+webdriver爬虫做了一个简单的介绍及应用,起到一个抛砖引玉的作用,要想利用好这个工具还需要后期查看更多的资料进行补充学习,上面介绍的Chromedriver爬虫效率还很低,后期可以使用Chrome的无头浏览器模式即headless模式,设置无图属性不用加载图片等方法,提升selenium模拟浏览器爬虫效率。
参考
- ^python爬取豆瓣电影分类排行榜引出的异步加载(AJAX)问题 python爬取豆瓣电影分类排行榜引出的异步加载(AJAX)问题 - 知乎
- ^python爬取豆瓣电影分类排行榜引出的异步加载(AJAX)问题 python爬取豆瓣电影分类排行榜引出的异步加载(AJAX)问题 - 知乎
- ^python中selenium 滚动条下拉 操作 python中selenium 滚动条下拉 操作_driver下划python-CSDN博客
相关文章:

并发编程下的集合:数组寻址、LinkedList、HashMap、ConcurrentHashMap
如果发现hash取模后的数组索引位下无元素则直接新增,若不是空那就说明存在hash冲突,则判断数组索引位链表结构中的第一个元素的key以及hash值是否与新的key一致则直接覆盖,若不一致则判断当前的数组索引下的链表结构是否为红黑树,若为红黑树则走红黑树的新增方法,若不为红黑树则遍历当前链表结构,遍历中发现某个节点元素的next为null是则直接将新元素指针与next进行关联,若在遍历到next为空前判断到,某个节点的key以及key的hash值与新的key与新的keyhash值一致时则走覆盖。

【日常开发之Windows共享文件】Java实现Windows共享文件上传下载
下拉框选择你选择的用户点击添加,然后共享确定。创建一个文件夹然后点击属性界面,点击共享。maven版本存在于SMB协议的兼容问题。首先开启服务,打开控制面板点击程序。点击启用或关闭Windows功能。我这边是专门创建了一个用户。SMB1.0选中红框内的。

rust wasm入门
demo## 编译 Rust 为 WebAssembly在本教程中,我们将使用 Rust 的 npm 包构建工具 wasm-pack 来构建一个 npm 包。

iced 入门一
本教程的目标是创建一个简单的购物清单应用程序。我们希望允许添加和删除购物清单中的项目。在编写代码之前,我们必须首先了解 Iced 构建的结构:Elm 架构。它是 GUI 库使用的架构,最初用于 Elm 编程语言。它的核心原则很简单。它围绕三个概念构建:模型、视图和更新。

注解annotation
Kubernetes的系统组件(例如,kube-scheduler、kube-controller-manager、kube-apiserver、kubectl 或其他第三方组件)向用户的Kubernetes对象添加注解时,必须指定一个前缀。注解(annotation)可以用来向 Kubernetes 对象的 metadata.annotations 字段添加任意的信息。除了使用注解,您也可以将这类信息存放在一个外部的数据库,然而,在使用、分享这些信息的时候,可能会变得难以管理。

Rust XTask 模式介绍与应用
XTask(扩展任务)是一种在Rust项目中定义和执行自定义构建任务的方式。它通过创建一个独立的Rust库或二进制项目来封装这些任务,利用Rust语言的强类型、安全性和跨平台能力,使得构建流程更加健壮、可读和可维护。

ModuleNotFoundError: No module named ‘qcloud_cos‘
是腾讯云提供的一个Python SDK,用于与腾讯云对象存储(COS)服务进行交互。使用pip安装qcloud_cos报以下错误。这个错误表示Python无法找到名为。

需要在method方法被调用之后,仅打印出a=100,b=200,请写出method方法的代码
通常,此流对应于显示器输出或者由主机环境或用户指定的另一个输出目标。通常,此流对应于键盘输入或者由主机环境或用户指定的另一个输入源。public static final PrintStream err“标准”错误输出流。PrintStream 是打印输出流,它继承于FilterOutputStream。第二个用的是用的是char类型,根本不是方法,当要输出方法体的时候,会给你遍历数组。通常,此流对应于显示器输出或者由主机环境或用户指定的另一个输出目标。诡异的是,如果错了,面试官对你说了一句:你回去看看,

Math: Math.atan() 与 Math.atan2() 计算两点间连线的夹角
Math.atan2()函数返回点(x,y)和原点(0,0)之间直线的倾斜角.那么如何计算任意两点间直线的倾斜角呢?只需要将两点x,y坐标分别相减得到一个新的点(x2-x1,y2-y1).然后利用他求出角度就可以了.使用下面的一个转换可以实现计算出两点间连线的夹角.然而,Math.atan()只能返回一个角度值,因此确定他的角度非常的复杂,而且,90度和270度的正切是无穷大,因为除数为零,我们也是比较难以处理的~!angel为一个角度的弧度值,slope为直线的斜率,是一个数字,这个数字可以是负的。

一个合格的Java选手必须要掌握的并发锁知识
Java内置锁:基于Java语法层面(关键词)实现的锁,主要是根据Java语义来实现,最典型的应用就是synchronized。Java显式锁:基于JDK层面实现的锁,主要是根据基于Lock接口和ReadWriteLock接口,以及统一的AQS基础同步器等来实现,最典型的有ReentrantLock。使用方式:synchronized关键字互斥锁主要有作用于对象方法上面,作用于类静态方法上面,作用于对象方法里面,作用于类静态方法里面等4种方式。

Integer.toHexString(b & 0xff)理解以及& 0xff什么意思
首先toHexString传的参数应该是int类型32位,此处传的是byte类型8位,所以前面需要补24个0。然后& 0xff 就是把前面24个0去掉只要后8位。toHexString(b & 0xff)相当于做了一次位的与运算,将前24位字符省略,将后8位保留。是两个十六进制的数,每个f用二进制表示是1111,所以占四位(bit),两个f()占八位(bit),八位(bit)也就是一个字节(byte).这个方法是把字节(转换成了int)以16进制的方式显示。我的理解是这样,如有不对欢迎指正!

C语言常见面试题:什么是变量?变量有哪些作用?
变量是编程中的一个基本概念,其定义和用法因编程语言和上下文而异。但通常来说,变量是用于存储数据的容器,这些数据可以是数字、文本、布尔值等。总的来说,变量在编程中扮演着重要的角色,它们使得我们能够有效地存储、操作和使用数据。不同的编程语言和上下文可能会对变量的具体定义和使用方式有所不同,但上述作用是通用的。总的来说,变量和常量都是编程中重要的概念,它们各自有其特定的用途和特性。在编程中正确地使用它们可以帮助我们更好地组织和控制程序的行为。变量和常量在编程中都是重要的概念,但它们之间存在明显的区别。

python安装成功的图标_ubuntu下:安装anaconda、环境配置、软件图标的创建、成功启动anaconda图形界面...
Ubuntu安装anaconda常见的四大问题:目录1、介绍2、安装anaconda3、环境配置4、软件图标的创建5、成功启动anaconda图形界面1、介绍先介绍一下anaconda和python的关系:初学者所安装的python2/3只是python的环境,没有python的工具包&a…

CSS局限属性contain:优化渲染性能的利器
在网页开发中,优化渲染性能是一个重要的目标。CSS局限属性contain是一个强大的工具,可以帮助我们提高网页的渲染性能。本文将介绍contain属性的基本概念、用法和优势,以及如何使用它来优化网页的渲染过程。

使用JavaScript实现复杂功能:一个完整的电商网站搜索功能
随着互联网的发展,电子商务网站已经成为人们购物的重要平台。而在这些网站中,搜索功能无疑是核心功能之一。用户可以通过搜索快速找到他们需要的商品,从而提高购物体验。本文将详细介绍如何使用JavaScript实现一个完整的电商网站搜索功能。

C++并发编程:互斥锁std::mutex和lock_guard的使用
对象离开其作用域时,会自动调用析构函数,该析构函数会释放锁。这确保了在任何情况下(包括由于异常等原因导致的提前退出),锁都会被正确释放,从而避免了忘记手动释放锁而导致的死锁问题。mutex 用于控制多个线程访问共享资源,确保在任意时刻只有一个线程可以访问该资源,避免数据竞争。这确保了同一时刻只有一个线程可以访问被保护的资源,从而防止多线程并发访问导致的数据不一致性。是 C++ 标准库中提供的一个模板类,用于在其构造时自动获取锁,在析构时自动释放锁。是 C++ 标准库中提供的一种用于多线程同步的互斥锁实现。

上位机图像处理和嵌入式模块部署(qt插件的使用)
【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】 一个软件一般有很多的功能,但是主流程只有一个。但在软件开发的过程当中,一般来说功能是需要不断添加的,但是主流程最好不要轻易修改。这里的插件就相当于各种各样的功能,而主流程就是如何怎么去调用这些插件的功能。所以,今天正好来学一下怎么添加qt插件,个人觉得这部分还是非常重要的。

C程序的内存空间布局(栈、堆、数据区、常量区、代码区)
较详细的介绍了栈、堆、数据区、常量区、代码区

NRF24L01模块传输MPU6050数据,接收端数据一直为0问题记录
问题描述:一、发射端1、正确配置NRF模块,以及测试过能够正常通信,在发射端的发射线程中进行了如下操作2、这里是获取了陀螺仪的x轴数据,将其而分为两个8位的数据存入发送缓冲区中。因为一个陀螺仪x轴数据是16位的,所以对其进行了拆分,这里只获取gyro的x轴数据进行发送,目的是进行测试。3、这个是发送函数,只要把发送缓冲区的地址作为参数传入就可以发送了。二、接收端1、接收端的NRF24L01模块也正确配置后,在接收线程中进行如下操作2、读取NRF传输过来的数据,存到接收BUF中,然后打印

Java中的四种访问权限(private,public,protected,无修饰)
/实体类属性和数据库字段名称不一致//实体类属性和数据库字段名称不一致return id;return age;emp.test();//直接调用public修饰的变量//private修饰的变量进行赋值//调用private修饰的变量1、public修饰符定义的属性和方法通过对象实例化进行调用,2、private修饰的属性通过set、get方法进行调用。

Java中的方法重载和方法重写有什么区别?
Java中的方法重载(Overloading)和方法重写(Overriding)都是面向对象编程中的重要概念,但它们之间有一些区别。方法重载是指在同一个类中,可以定义多个具有相同名称但参数列表不同的方法。这些方法具有不同的参数类型、参数个数或参数顺序。在调用重载方法时,Java编译器会根据传递给方法的参数类型和数量来选择要调用的正确方法。方法重载主要用于解决方法的命名冲突和提高代码的可读性和可维护性。

python基础使用之变量,表达式,语句
PYTHON基础知识系列之变量、表达式、语句

C语言常见面试题:什么是宏,宏的作用是什么?
宏在计算机科学中是一种批量处理程序命令,它是一种抽象的规则或模式,用于说明某一特定输入(通常是字符串)如何根据预定义的规则转换成对应的输出(通常也是字符串)。在编译时,预处理器会对宏进行展开,即将宏的内容替换到宏所在的位置。以上是宏的一些主要作用,但并不是全部。在实际编程中,根据需要选择是否使用宏以及如何使用宏,以实现更好的代码组织和可读性。,这样就可以计算出a和b的和。这个例子展示了宏的基本用法和作用。在这个例子中,我们定义了一个宏。,用于计算两个数的和。时,预处理器会将其展开为。

python基础小知识:引用和赋值的区别
通过引用,就可以在程序范围内任何地方传递大型对象而不必在途中进行开销巨大的赋值操作。不过需要注意的是,这种赋值仅能做到顶层赋值,如果出现嵌套的情况下仍不能进行深层赋值。赋值与引用不同,复制后会产生一个新的对象,原对象修改后不会影响到新的对象。如果在原位置修改这个可变对象时,可能会影响程序其他位置对这个对象的引用

Golang 搭建 WebSocket 应用(八) - 完整代码
本文应该是本系列文章最后一篇了,前面留下的一些坑可能后面会再补充一下,但不在本系列文章中了。

Python自动化实战之接口请求的实现
作为一位过来人也是希望大家少走一些弯路,如果你不想再体验一次学习时找不到资料,没人解答问题,坚持几天便放弃的感受的话,在这里我给大家分享一些自动化测试的学习资源,希望能给你前进的路上带来帮助。

C#winform上位机开发学习笔记3-串口助手的信息保存功能添加
上位机开发的系列学习笔记,避免遗忘多记录多补充多优化

Python中如何简化if...else...语句
我们通常在Python中采用if...else..语句对结果进行判断,根据条件来返回不同的结果,如下面的例子。这段代码是一个简单的Python代码片段,让用户输入姓名并将其赋值给变量user_input。我们能不能把这几行代码进行简化,优化代码的执行效率呢?以下是对各行代码的解读。这里使用了or这个逻辑运算符,当user_input不为空时,user_input为真,name就被赋于user_input的值。采用这种方法可以轻松实现if...else语句的简化。我们可以使用一行简短的代码来实现上面的任务。

Java数据结构与算法:排序算法之插入排序
插入排序是一种基础的比较排序算法,其核心思想是将待排序的序列分为两部分,一部分是已排序的,另一部分是未排序的。在未排序部分选择一个元素,插入到已排序部分的适当位置,以此类推,直到所有元素都被排序完毕。插入排序的实现简单直观,是初学者入门排序算法的绝佳选择。

Java数据结构与算法:排序算法之归并排序
归并排序是一种基于分治思想的排序算法,它将待排序的序列划分成若干个子序列,分别进行排序,最后再合并成一个有序的序列。这一过程通过递归实现,直到每个子序列中只有一个元素,即可认为其已经有序。随后,通过两两合并有序序列,最终完成整个序列的排序。