大家好,我是石璞东。
我们在 大麦网抢票程序之大麦网网站分析 一文中详细描述了本次项目案例的需求,并对其做了深入的分析,在本篇文章中,我们来讲解一下实现该案例所涉及的一些具体的技术栈,请看文章。
在文章开始之前,我先从两个角度回答一个问题:
- 为什么会选择使用selenium?
角度一: 我们知道,在爬虫的过程中,我们会经常遇到以下三种形式的网页:
- 第一种是在请求相应的网址之后,在网速等外部原因满足的条件下,即可得到网页的完整源代码,即浏览器所查看到的源代码就是网页实际的源代码,这种网站包括有 [大麦网](https://www.damai.cn/)、[中国研究生招生信息网](https://yz.chsi.com.cn/) 等;
- 第二种是那些通过 `Ajax` 渲染出来的页面,对于该种类型的网页,我们只需要直接去分析所请求的 `Ajax`,然后借助 `requests` 或者 `urllib` 即可实现数据的爬取,这种网站包括 [今日头条](https://www.toutiao.com/) 等;
- 第三种是那些通过 `JavaScript` 计算生成的网页,这种网站包括 [百度的Echarts](https://echarts.apache.org/zh/index.html) 等。
**角度二:** 本系列文章中,我们是要通过自动化程序来抢德云社小园子的票,所以必须有用户亲自选择座位、票档等相关操作,这就解释了我们为什么没有使用 `Chrome` 的 `Headless` 模式或者 `PhantomJS`。接下来,请看正文内容。
网站流量的计算及区别介绍
- 浏览量(page view):用户每次打开一个网站页面就被记录一次。用户多次打开同一页面,浏览量累计;
- 访客数(user view): 一天之内网站的独立访客数(以cookie为依据),一天之内同一访客多次访问同一网站只计算为1个访客;
- 访问次数:记录所有访客1天内访问了多少次我的网站,相同的访客有可能多次访问我的网站;
- IP数:一天之内访问网站的独立IP;
- 新访客数:一天的独立访客中,历史第一次访问网站的访客数;
- 新访客比率:新访客数/访客数
- 跳出率: 只浏览了一个页面便离开了网站的访问次数占总的访问次数的百分比;
- 平均访问时长: 访客在一次访问中,平均打开网站的时长。即每次访问中,从打开第一个页面到关闭最后一个页面的平均时间;
- 转化次数: 访客到达转化目标页面的次数
我们来考虑一个问题:假设中国的所有省份中每个省均只有一个接入互联网的网关,即只能通过该网关访问互联网资源,也就是说只有一个公网ip,假设这34个省每个省的所有用户每天都访问一次我的网站,请问24个小时之后,我网站的浏览量是所有用户的个数还是34,那独立ip的个数是多少呢?
首先,请看答案:网站的访问量是所有访问用户的总个数,独立IP是34,请看详细分析:
- 网站浏览量计算的是用户打开网站页面的次数,跟用户所使用的IP没有关系,就算用户使用一个固定IP访问我的网站它的浏览量也会改变的;
- 每个省分配一个公网IP,独立IP数为34, 在每个省的 局域网 环境下,又有很多的局域网IP,之所以这样分配是为了解决IPV4地址不够用的问题,这些局域网IP去访问互联网资源必须通过网关,即对外显示是通过该省的公网IP访问的,也就是说通过34个独立的IP实现了全国人的访问。
刷网站流量
小伙伴们在阅读本小节内容之前,最好有一些关于搜索引擎的基本概念,这里我给大家找出了我2019年年底考完研之后写的一篇文章 。
以我的个人网站为例,给各位小伙伴看下主流搜索引擎对我网站词条的排名情况:
- 百度
- 搜狗
从关键词搜索的词条排名情况和词条数目也能反映出所用搜索引擎的某些优缺点,从爬虫速度来说: Google>搜狗>百度>360,搜狗应该算是爬虫速度频率比较高的了,百度对于我的网站的收录还有一些死链,360就不用说了,词条数和死链都是最少的。
网站流量的提高:
- 访问网站用户数的增加(建立在优质的网站资源基础上);
- 通过爬虫程序提高网站页面的权重;
- 通过站长工具去做SEO(SEO的过程是比较漫长的,涉及到网站页面的代码格式、关键词、网站的运行时间、外链内链等等);
- 花钱做竞价排名
selenium安装
工欲善其事,必先利其器,在完成我们的案例之前,我们先来安装一下相关的环境配置。
1. windows下安装selenium
首先,请确保你已经安装好了Python和Chrome浏览器,接着请查看你的Chrome版本:
然后,请根据如下网址中的 Chrome
浏览器版本与 Chromedriver
版本的对应信息去安装对应的 Chromedriver
,请看网址:
http://chromedriver.storage.googleapis.com/index.html
下载完成之后,对其进行解压,并将其放在 Chrome
文件夹下:
完成 Chromedriver
的安装之后,我们来进行最后一步操作:通过 pip
包来安装 selenium
:
pip install selenium
完成上述所有操作之后,我们来看一个例子:通过自动化程序打开我的个人网站:
from selenium import webdriver
browser=webdriver.Chrome('C:\Program Files(x86)\Google\Chrome\Application\chromedriver.exe')
browser.get("https://www.shipudong.com")
「注」:我们可以通过配置环境变量直接使用 webdriver.Chrome()
来声明一个浏览器对象,也可以通过 webdriver.Chrome('C:\Program Files(x86)\Google\Chrome\Application\chromedriver.exe')
直接来进行模拟。
2. mac下安装selenium
mac下的安装与windows下的安装步骤大概一致,这里不再赘述,我们来看一下需要注意的地方。
大家完成mac下正确版本的 Chromedriver
下载之后,就可以将下载好的可执行文件移动到 /usr/local/bin
目录中,我们通过快捷键 Command + Shift + G
输入 /usr/local/bin
即可进入相应文件夹,接着我们将可执行文件拖入到该目录中;
完成上述操作之后,我们通过 terminal
进入到上述目录的终端页面,输入 chromedriver
查看相关信息:
安装完成 Chromedriver
之后,我们通过 pip
包来安装 selenium
即可,这里不再赘述。
我们来看一个演示案例,请看代码:
from selenium import webdriver
browser=webdriver.Chrome()
browser.get("https://www.shipudong.com")
非常好,安装成功!这里我们在来看两个案例:
- 循环执行以下操作:间隔5s打开一次浏览器并访问指定的网站,5s之后又关闭网站
from selenium import webdriver
import time
def controlBrowser():
driver = webdriver.Chrome()
driver.maximize_window()
time.sleep(5)
print("5秒后我就要访问石璞东的网站咯")
driver.get("https://www.shipudong.com")
time.sleep(5)
print("5秒后我就要关闭浏览器咯")
print("============================");
driver.close()
if __name__=="__main__":
while 1:
controlBrowser()
- 循环执行以下操作:间隔一段时间点击一次刷新按钮
from selenium import webdriver
import time
def controlBrowser():
driver = webdriver.Chrome()
driver.get("https://www.shipudong.com")
time.sleep(5)
driver.refresh()
if __name__ == "__main__":
while 1:
controlBrowser()
selenium的使用
1. 声明浏览器对象并访问页面
Selenium可以支持非常多的浏览器,包括Chrome、Firefox、Edge等,本文仅以Chrome为例进行讲解,首先我们来初始化浏览器对象,请看代码:
from selenium import webdriver
browser = webdriver.Chrome()
我们通过上述代码完成了浏览器对象的初始化,并将其赋值给 browser
对象,接下来我们就可以通过调用 browser
对象来模拟浏览器的各种操作了,请看代码:
from selenium import webdriver
browser = webdriver.Chrome()
browser.get("https://www.shipudong.com")
print(browser.page_source)
browser.close()
我们通过 get()
方法访问了 我的个人网站,并在控制台输出网页源代码之后关闭了我们通过自动程序打开的浏览器选项卡。
2. 查找节点
我们知道,对于整个爬虫来说,我们要做的无非就是三件事:
- 通过`requests`、`urllib`等库进行网页请求;
- 通过`re`、`XPath`、`Beautiful Soup`、`pyquery`等库解析源代码;
- 通过`txt`、`json`、`csv`、`SQL`等进行数据存储
因此,在使用 selenium
的过程中,当我们想要获取某些节点时,我们当然可以使用 XPath
、css
选择器等进行获取,但是由于 selenium
已经为我们提供了一系列的方法,所以关于 XPath
、css
选择器等的方法,这里就不再赘述。
请看官网地址:
https://selenium-python.readthedocs.io/locating-elements.html
请看代码:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
browser = webdriver.Chrome()
browser.get("https://www.baidu.com")
input = browser.find_element_by_id("kw")
input.send_keys("石璞东")
input.send_keys(Keys.ENTER)
wait = WebDriverWait(browser,1)
wait.until(EC.presence_of_element_located((By.ID,'content_left')))
print(browser.current_url)
上述代码中,我们首先通过 get()
方法访问百度首页,接着我们通过 find_element_by_id()
方法来获取到百度首页的输入框,并通过 send_keys()
方法输入关键词 石璞东,然后我们通过指定 send_keys()
方法的参数为 Keys.ENTER
来模拟点击 Enter
键实现 百度一下 的功能。
点击搜索按钮之后,我们通过 WebDriverWait
函数指定加载时间为1秒,如果在1秒内下图中 id
为 content_left
的 div
中的内容可以被加载出来,程序则会继续往下执行,最后会在控制台打印当前的网址;如果没有加载出来,则会抛出超时错误,请看官网解释:
请看控制台结果:
上述代码中关于 get()
方法等待加载的部分,这里不做过多讲解,请继续阅读本文。
更多关于查找节点的操作,这里不再进行一一赘述,官网中已经有足够详细的介绍和相关的例子,请读者自行学习了解,网址如下:
https://selenium-python.readthedocs.io/locating-elements.html
3. 节点交互
我们继续以百度为例,请看需求:
- 在输入框中输入石璞东;
- 3秒之后,清空输入框;
- 在输入框中重新输入 淘宝,并点击 百度一下 按钮提交所输入的搜索关键词
请看代码:
from selenium import webdriver
import time
browser = webdriver.Chrome()
browser.get("https://www.baidu.com")
input = browser.find_element_by_id("kw")
input.send_keys("石璞东")
time.sleep(3)
input.clear()
input.send_keys("淘宝")
sub_keywords = browser.find_element_by_id("su")
sub_keywords.click()
上述代码中,我们都是对特定的节点进行交互操作的,不管是按钮还是输入框都有其特定的一些方法,那么对于那些没有特定执行对象的操作(像是鼠标拖拽等)我们该如何去处理呢?
我们再来看一个案例,该案例所用到的演示网址如下:
https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable
案例很简单,用户拖拽标有 请拖拽我! 字样的 div
,当拖拽至标有 请放置到这里! 字样的 div
标签的一定范围内,会弹出 dropped
的提示框。
接下来我们通过 selenium
来完成上述通过用户进行拖拽的操作,请看代码:
from selenium import webdriver
from selenium.webdriver import ActionChains
browser = webdriver.Chrome()
browser.get("https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable")
browser.switch_to.frame("iframeResult")
source_div = browser.find_element_by_id("draggable")
target_div = browser.find_element_by_id("droppable")
actions = ActionChains(browser)
actions.drag_and_drop(source_div,target_div)
actions.perform()
仔细分析上述案例所用到的源代码,我们可以发现:整个页面的主要内容可以分为左右两部分,我们从源代码中可以获知其布局采用 栅格布局,关于布局的更多内容,请参考如下网址:
https://code.z01.com/v4/layout/grid.html
我们通过分析源代码可以知道,网页中拖拽操作的演示部分被嵌套在一个iframe
标签中,因此我们在对相关元素操作之前,首先要做的就是根据 id
把定位器切换到 iframe
上,这就是上述代码中我们使用browser.switch_to.frame("iframeResult")
一行代码跳转至 iframe
的具体实现。
更多关于节点操作的内容,请大家继续深入官网进行学习,请看官网地址:
https://selenium-python.readthedocs.io/navigating.html
4. 延时等待
我们知道,get()
方法会在网页框架渲染完成之后终止执行,此时我们如果通过 page_source
属性来获取网页源代码,很有可能得到的源代码不是最终加载完成之后的代码,因为大部分页面都会有额外的 Ajax
请求,这里我们就需要通过等待一段时间来确保所有节点的完全加载。
一般来讲,等待加载的方式主要包括两种,隐式等待(Implicit Waits)和显式等待(Explicit Waits)
关于隐式等待,这里我们不做过多赘述,大家看官网即可。
在隐式等待中,我们只规定了一个固定时间,超过固定时间之后会进行后续的相关操作,然而网页的加载受到网络条件等的影响,所以如果出现超时之后并没有加载出来的情况,隐式等待的效果就显的不那么友好了。
接下来,我们来看一下显示等待:
相比隐式等待,显示等待有两个明显的优点:
- 可以通过代码指定预期条件;
- 网页超时之后若没有成功加载,会自动抛出错误;
下面是官网中列出的一些预期条件:
上述内容中,我们结合selenium官方文档和我们的案例对节点操作、定位元素、延时等待等内容进行了介绍,关于cookies、浏览器前进后退等知识点,请小伙伴们自行阅读官网进行学习。
以上就是我们关于 Selenium
的所有内容,文中没有涉及到的内容,小伙伴们一定要到官网进行深入学习。
写在最后
为方便读者了解更为详细的信息,我为小伙伴们提供了三个我的官方渠道:
- hahaCoder(微信公众号)
- hahaAI(微信小程序)
- hahaWebsite. (个人网站)