饭饭的Selenium+xlwt笔记

165 阅读6分钟

饭饭的Selenium+xlwt笔记

欢迎来我的博客与我讨论饭饭的Selenium+xlwt笔记

这几天写了一个教务处成绩查询的爬虫,用到了Python的一个爬虫库Selenium和execl处理库xlwt,这里做一点记录

项目GITHUB仓库: github.com/chaoers/xjt…

Selenium

简介

Selenium是一个模拟浏览器的爬虫库,和目前大部分爬虫不一样的是它是直接模拟浏览器行为。因此爬取网页中有较多异步处理请求和鉴权复杂的网站会很方便,但缺点是无法大规模爬取数据,爬取速度较慢,但同时基本不会触发网站的爬虫检测,因此对于个人爬取少量的用途是一个很好用的库

安装

Selenium本身安装很简单

pip install selenium

但我们需要配置Selenium能够调用我们的浏览器进行自动操作

所以我们需要下载对应的WebDriver

例如笔主使用的是Chrome,就下载ChromeDriver

然后我们需要将下载的配置文件放入环境变量中

例如笔主使用的macOS,就直接放入/usr/bin

然后我们尝试爬取一下百度的数据

from selenium import webdriver
 
driver = webdriver.Chrome()
driver.get("https://www.baidu.com")
print driver.page_source

如果没有报错Selenium就配置正常了

教程

Selenium实际上就是模拟浏览器的行为,因此我们只要用平时浏览网页的逻辑去操作Selenium就可以了

网页操作

常用的网页操作有

# 打开网页
driver.navigate().to("http://www.example.com");

# 刷新网页
driver.refresh()

# 退出网页
driver.quit()

# 前进
driver.navigate().forward();

# 后退
driver.navigate().back();

# 切换帧(frames中使用)
driver.switchTo().window("windowName");

查找元素

查找元素是爬虫中最重要的一个操作,Selenium提供非常多的查找元素的方式

# 通过元素id查找
driver.find_element_by_id(":id")

# 通过元素class查找
driver.find_elements_by_class_name(":class_name")

# 通过元素标签名查找(div span等)
driver.find_elements_by_tag_name(":tag_name")

# 通过元素name查找
driver.find_elements_by_name(":name")

# 通过元素链接文字查找
driver.find_elements_by_link_text(":link_text")

# 通过元素链接文字部分匹配
driver.find_elements_by_partial_link_text(":partial_link_text")
Xpath

如果上述查找方式无法满足你的需求(大概率是不够用的),那么与其花费时间写一些复杂的查找方式,强烈建议你学习一下Xpath的查找方式。

Xpath教程

Xpath开始学起来可能有一些摸不着头脑,但我们可以把它类比为命令行中的位置表达式理解起来就很容易了

对于Selenium来说,我们只需要学会Xpath的使用方式就行了,所以下面会重点讲解Xpath使用部分,而其他部分略过不讲

  • 选取节点

XPath 使用路径表达式在 XML 文档中选取节点。节点是通过沿着路径或者 step 来选取的。 下面列出了最有用的路径表达式:

表达式描述
nodename选取此节点的所有子节点。
/从根节点选取。
//从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置。
.选取当前节点。
..选取当前节点的父节点。

实际上选取节点方式和路径表达式十分相似,只是可以选择任意子代节点(//)

  • 谓语(Predicates)

谓语用来查找某个特定的节点或者包含某个指定的值的节点。

谓语被嵌在方括号中。

在下面的表格中,我们列出了带有谓语的一些路径表达式,以及表达式的结果:

路径表达式结果
/bookstore/book[1]选取属于 bookstore 子元素的第一个 book 元素。
/bookstore/book[last()]选取属于 bookstore 子元素的最后一个 book 元素。
/bookstore/book[last()-1]选取属于 bookstore 子元素的倒数第二个 book 元素。
/bookstore/book[position()<3]选取最前面的两个属于 bookstore 元素的子元素的 book 元素。
//title[@lang]选取所有拥有名为 lang 的属性的 title 元素。
//title[@lang='eng']选取所有 title 元素,且这些元素拥有值为 eng 的 lang 属性。
/bookstore/book[price>35.00]选取 bookstore 元素的所有 book 元素,且其中的 price 元素的值须大于 35.00。
/bookstore/book[price>35.00]//title选取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值须大于 35.00。

通过谓语,我们可以只通过Xpath完成直接介绍过的所有功能,因此Xpath是十分强大的

  • 选取未知节点

XPath 通配符可用来选取未知的 XML 元素。

通配符描述
*匹配任何元素节点。
@*匹配任何属性节点。
node()匹配任何类型的节点。

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式结果
/bookstore/*选取 bookstore 元素的所有子元素。
//*选取文档中的所有元素。
//title[@*]选取所有带有属性的 title 元素。
  • 选取若干路径 通过在路径表达式中使用"|"运算符,您可以选取若干个路径。

在下面的表格中,我们列出了一些路径表达式,以及这些表达式的结果:

路径表达式结果
//book/title | //book/price选取 book 元素的所有 title 和 price 元素。
//title | //price选取文档中的所有 title 和 price 元素。
/bookstore/book/title | //price选取属于 bookstore 元素的 book 元素的所有 title 元素,以及文档中所有的 price 元素。

接下来我们就可以快乐的使用Xpath查找几乎任何我们想查找的元素

driver.find_element_by_xpath(":Xpath")
注意事项

在查找元素中有一些需要注意的事情

  • find_element只会返回查找到的第一个元素,而find_elements默认会返回一个Lis(即使只有一个元素)
  • 元素查找可以进行叠加,即以下操作是可行的
cell = driver.find_elements_by_xpath(
            "//div[@class='cjcx-tab-content-1 bh-mt-8 jqx-tabs-content-element jqx-rc-b']//tr")[i].find_elements_by_xpath("./td/span")

操作元素

对于一个元素进行操作也是一件很常见的事,下面介绍一些常用操作

# 点击操作
element.click()

# 输入文字
element.send_keys(":keys")

附加教程

有了上述介绍,我们已经能基本能实现浏览器中的大多数常用操作,下面是一些能优化的附加教程

动态等待

由于网络原因,元素不能立即加载出来,这时如果我们不加上一个等待可能会使得程序找不到接下来额元素而停止,

我们一般直接加上一个等待时间,例如

time.sleep(1)

但是这样做会明显增加我们的运行时间,如何当元素加载出来时就进行下一步呢,这时我们可以运用动态等待来执行

# 注意要引入WebDriverWait和EC才可以使用
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

# 等待元素出现执行操作
WebDriverWait(driver, 10).until(EC.presence_of_element_located(
    (By.XPATH, "//div[text()='成绩查询']"))).click()

xlwt

Xlwt是一个很简单的python库,但是由于搜索引擎上搜到的教程都被一些垃圾行业的教程污染了这里大致写一下基本操作

import xlwt

#设置编码
workbook = xlwt.Workbook(encoding='utf-8') 
#表名
worksheet = workbook.add_sheet('Grade') 

#写入
worksheet.write(:row, :col, label=":label") 

#保存文件
workbook.save('Grade.xls')