python selenium爬虫 java selenium (五) 元素定位大全
java selenium (五) 元素定位大全
python selenium爬虫
Web自动化测试工具,可运行在浏览器,根据指令操作浏览器,只是工具,必须与第三方浏览器结合使用,相比于之前学的爬虫只是慢了一点而已。而且这种方法爬取的东西不用在意时候ajax动态加载等反爬机制。因此找标签可以直接F12找,不用确定源码中是否存在。
安装
Linux: sudo pip3 install selenium
Windows: python -m pip install selenium
phantomjs浏览器
phantomjs浏览器又叫做无界面浏览器(又称无头浏览器),在内存中进行页面加载,运行高效。
安装(phantomjs(无界面浏览器)、chromedriver(谷歌浏览器)、geckodriver(火狐浏览器))
Windows
1、下载对应版本的phantomjs、chromedriver、geckodriver
2、chromedriver下载与谷歌浏览器对应的版本,把chromedriver.exe拷贝到python安装目录的Scripts目录下(添加到系统环境变量),查看python安装路径: where python
3、验证,cmd命令行: chromedriver
Linux
1、下载后解压:tar -zxvf geckodriver.tar.gz
2、拷贝解压后文件到 /usr/bin/ (添加环境变量):sudo cp geckodriver /usr/bin/
3、更改权限
sudo -i
cd /usr/bin/
chmod 777 geckodriver
示例代码一:使用 selenium+谷歌浏览器 打开百度,并截图百度首页
from selenium import webdriver
browser = webdriver.Chrome() # 创建浏览器对象
browser.get('http://www.baidu.com/') # 打开百度
browser.save_screenshot('baidu.png') # 截屏
browser.quit() # 退出浏览器
示例代码二:打开百度,搜索赵丽颖
from selenium import webdriver
import time
# 创建浏览器对象 - 已经打开了浏览器
browser = webdriver.Chrome()
browser.get('http://www.baidu.com/') # 打开百度
ele = browser.find_element_by_xpath('//*[@id="kw"]') # 找到搜索框
ele.send_keys('赵丽颖') # 向搜索框发送文字: 赵丽颖
time.sleep(1)
# 找到 百度一下 按钮,点击一下
browser.find_element_by_xpath('//*[@id="su"]').click()
time.sleep(2)
browser.quit() # 关闭浏览器
browser浏览器对象方法
- browser = webdriver.Chrome(executable_path='path') path为浏览器驱动地址
- browser.get(url) 打开path路径
- browser.page_source: 查看响应内容(网页源代码)
- browser.page_source.find('字符串'):从html源码中搜索指定字符串,没有找到返回:-1
- browser.quit():关闭浏览器
元素查找
单元素查找(1个节点对象)
- browser.find_element_by_id('')
- browser.find_element_by_name('')
- browser.find_element_by_class_name('')
- browser.find_element_by_xpath('')
- browser.find_element_by_link_text('')
- ... ...
多元素查找([节点对象列表])
- browser.find_elements_by_id('')
- browser.find_elements_by_name('')
- browser.find_elements_by_class_name('')
- browser.find_elements_by_xpath('')
- ... ...
节点对象操作
- .send_keys('') 搜索框发送内容
- .click() 点击
- .text 获取文本内容
- .get_attribute('src') 获取属性值
- .find("") 查找响应中的字符串
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://www.qiushibaike.com/text/')
# 单元素查找
div = browser.find_element_by_class_name('content')
print(div.text)
# 多元素查找: [<selenium xxx at xxx>,<selenium xxx >]
divs = browser.find_elements_by_class_name('content')
for div in divs:
print('*************************')
print(div.text)
print('*************************')
browser.quit() # 退出浏览器
京东爬虫案例
目标网址 :https://www.jd.com/
抓取目标 :商品名称、商品价格、评价数量、商品商家
思路提醒
- 打开京东,到商品搜索页
- 匹配所有商品节点对象列表
- 把节点对象的文本内容取出来,查看规律,是否有更好的处理办法?
- 提取完1页后,判断如果不是最后1页,则点击下一页
实现步骤
找节点
- 首页搜索框 : //*[@id="key"]
- 首页搜索按钮 ://*[@id="search"]/div/div[2]/button
- 商品页的 商品信息节点对象列表 ://*[@id="J_goodsList"]/ul/li
执行JS脚本,获取动态加载数据
browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
from selenium import webdriver
import time
class JdSpider(object):
def __init__(self):
self.i = 0
self.url = 'https://www.jd.com/'
self.browser = webdriver.Chrome()
# 获取页面信息 - 到具体商品的页面
def get_html(self):
self.browser.get(self.url)
self.browser.find_element_by_xpath('//*[@id="key"]').send_keys('爬虫书') # 搜索框输入“爬虫书”
self.browser.find_element_by_xpath('//*[@id="search"]/div/div[2]/button').click() # 点击搜索
time.sleep(3) # 给商品页面加载时间
# 解析页面
def parse_html(self):
# 把下拉菜单拉到底部,执行JS脚本
self.browser.execute_script('window.scrollTo(0,document.body.scrollHeight)')
time.sleep(2)
# 提取所有商品节点对象列表 li列表
li_list = self.browser.find_elements_by_xpath('//*[@id="J_goodsList"]/ul/li')
for li in li_list:
info_list = li.text.split('\n')
if info_list[0].startswith('每满') or info_list[1].startswith('¥'):
price = info_list[1]
name = info_list[2]
comment = info_list[3]
shop = info_list[4]
elif info_list[0].startswith('单件'):
price = info_list[3]
name = info_list[4]
comment = info_list[5]
shop = info_list[6]
else:
price = info_list[0]
name = info_list[1]
comment = info_list[2]
shop = info_list[3]
print(price, comment, shop, name)
# 主函数
def main(self):
self.get_html()
while True:
self.parse_html()
# 判断是否该点击下一页,没有找到说明不是最后一页
if self.browser.page_source.find('pn-next disabled') == -1:
self.browser.find_element_by_class_name('pn-next').click()
time.sleep(2)
else:
break
print(self.i)
if __name__ == '__main__':
spider = JdSpider()
spider.main()
chromedriver设置无界面模式
from selenium import webdriver
options = webdriver.ChromeOptions() # 设置无界面
options.add_argument('--headless') # 添加无界面参数
browser = webdriver.Chrome(options=options)
browser.get('http://www.baidu.com/')
browser.save_screenshot('baidu.png')
browser.quit()
把上面的代码改为无界面模式
View Code键盘操作
from selenium.webdriver.common.keys import Keys
browser = webdriver.Chrome()
browser.get('http://www.baidu.com/')
# 1、在搜索框中输入"selenium"
browser.find_element_by_id('kw').send_keys('赵丽颖')
# 2、输入空格
browser.find_element_by_id('kw').send_keys(Keys.SPACE)
# 3、Ctrl+a 模拟全选
browser.find_element_by_id('kw').send_keys(Keys.CONTROL, 'a')
# 4、Ctrl+c 模拟复制
browser.find_element_by_id('kw').send_keys(Keys.CONTROL, 'c')
# 5、Ctrl+v 模拟粘贴
browser.find_element_by_id('kw').send_keys(Keys.CONTROL, 'v')
# 6、输入回车,代替 搜索 按钮
browser.find_element_by_id('kw').send_keys(Keys.ENTER)
鼠标操作
import time
from selenium import webdriver
# 导入鼠标事件
from selenium.webdriver import ActionChains
browser = webdriver.Chrome()
browser.get('http://www.baidu.com/')
# 找到“设置”节点
element = browser.find_element_by_xpath('//*[@id="u1"]/a[8]')
# 把鼠标移动到 设置 节点,move_to_element()
actions = ActionChains(browser)
actions.move_to_element(element)
actions.perform() # perform()是真正执行操作
time.sleep(1)
# 找到高级设置节点,并点击
browser.find_element_by_link_text('高级搜索').click()
切换页面
适用与页面中点开链接出现新的页面的网站,但是浏览器对象browser还是之前页面的对象
all_handles = browser.window_handles 获取当前所有句柄(窗口)
browser.switch_to_window(all_handles[1]) 切换browser到新的窗口,获取新窗口对象
民政部网站
将民政区划代码爬取到数据库中,按照层级关系(分表 -- 省表、市表、县表)
数据库中建表
# 建库
create database govdb charset utf8;
use govdb;
# 建表
create table province(
p_name varchar(20),
p_code varchar(20)
)charset=utf8;
create table city(
c_name varchar(20),
c_code varchar(20),
c_father_code varchar(20)
)charset=utf8;
create table county(
x_name varchar(20),
x_code varchar(20),
x_father_code varchar(20)
)charset=utf8;
思路
- selenium+Chrome打开一级页面,并提取二级页面最新链接
- 增量爬取: 和数据库version表中进行比对,确定之前是否爬过(是否有更新)
- 如果没有更新,直接提示用户,无须继续爬取
- 如果有更新,则删除之前表中数据,重新爬取并插入数据库表
- 最终完成后: 断开数据库连接,关闭浏览器
from selenium import webdriver
import pymysql
class GovSpider(object):
def __init__(self):
# 设置无界面
options = webdriver.ChromeOptions()
options.add_argument('--headless')
self.browser = webdriver.Chrome(options=options) # 添加参数
self.one_url = 'http://www.mca.gov.cn/article/sj/xzqh/2019/'
# 创建数据库和相关变量
self.db = pymysql.connect('localhost', 'root', '123456', 'govdb', charset='utf8')
self.cursor = self.db.cursor()
# 创建3个列表,用来executemany()往3张表中插入记录
self.province_list = []
self.city_list = []
self.county_list = []
# 获取首页,并提取二级页面链接(虚假链接)
def get_incr_url(self):
self.browser.get(self.one_url)
# 提取最新链接,判断是否需要增量爬
td = self.browser.find_element_by_xpath('//td[@class="arlisttd"]/a[contains(@title,"代码")]')
# 提取链接 和 数据库中做比对,确定是否需要怎俩那个抓取
# get_attribute()会自动补全提取的链接
two_url = td.get_attribute('href')
# result为返回的受影响的条数
result = self.cursor.execute('select url from version where url=%s', [two_url])
if result:
print('无须爬取')
else:
td.click()
# 切换句柄
all_handlers = self.browser.window_handles
self.browser.switch_to.window(all_handlers[1])
self.get_data() # 数据抓取
# 把URL地址存入version表
self.cursor.execute('delete from version')
self.cursor.execute('insert into version values(%s)', [two_url])
self.db.commit()
# 二级页面中提取行政区划代码
def get_data(self):
# 基准xpath
tr_list = self.browser.find_elements_by_xpath('//tr[@height="19"]')
for tr in tr_list:
code = tr.find_element_by_xpath('./td[2]').text.strip()
name = tr.find_element_by_xpath('./td[3]').text.strip()
print(name, code)
# 判断层级关系,添加到对应的数据库表中(对应表中字段)
# province: p_name p_code
# city : c_name c_code c_father_code
# county : x_name x_code x_father_code
# 把数据添加到对应的表中
if code[-4:] == '0000':
self.province_list.append([name, code])
if name in ['北京市', '天津市', '上海市', '重庆市']:
self.city_list.append([name, code, code])
elif code[-2:] == '00':
self.city_list.append([name, code, (code[:2] + '0000')])
else:
if code[:2] in ['11', '12', '31', '50']:
self.county_list.append([name, code, (code[:2] + '0000')])
else:
self.county_list.append([name, code, (code[:4] + '00')])
# # 和for循环同缩进,所有数据爬完后统一excutemany(),
# 执行数据库插入语句
self.insert_mysql()
def insert_mysql(self):
# 1. 更新时一定要先删除表记录
self.cursor.execute('delete from province')
self.cursor.execute('delete from city')
self.cursor.execute('delete from county')
# 2. 插入新数据
self.cursor.executemany('insert into province values(%s,%s)', self.province_list)
self.cursor.executemany('insert into city values(%s,%s,%s)', self.city_list)
self.cursor.executemany('insert into county values(%s,%s,%s)', self.county_list)
# 3.提交到数据库执行
self.db.commit()
print('数据抓取完成,成功存入数据库')
def main(self):
self.get_incr_url()
self.cursor.close() # 所有数据处理完成后断开连接
self.db.close()
self.browser.quit() # 关闭浏览器
if __name__ == '__main__':
spider = GovSpider()
spider.main()
SQL命令练习
1. 查询所有省市县信息(多表查询实现)
select province.p_name,city.c_name,county.x_name from province,city,county where province.p_code=city.c_father_code and city.c_code=county.x_father_code;
2. 查询所有省市县信息(连接查询实现)
select province.p_name,city.c_name,county.x_name from province inner join city on province.p_code=city.c_father_code inner join county on city.c_code=county.x_father_code;
Web客户端验证
在URL地址中填入即可
url = 'http://用户名:密码@正常地址'
示例: 爬取某一天笔记
from selenium import webdriver url = 'http://tarenacode:code_2013@code.tarena.com.cn/AIDCode/aid1904/15-spider/spider_day06_note.zip' browser = webdriver.Chrome() browser.get(url)
iframe子框架
iframe子框架适用于网页中嵌套了网页,这种情况应该先切换到iframe子框架,然后再执行其他操作。
browser.switch_to.iframe(iframe_element)
示例 - 登录qq邮箱
import time
from selenium import webdriver
browser = webdriver.Chrome()
browser.get('https://mail.qq.com/cgi-bin/loginpage')
# 找iframe子框架并切换到此iframe
login_frame = browser.find_element_by_id('login_frame')
browser.switch_to.frame(login_frame)
# qq+密码+登录
browser.find_element_by_id('u').send_keys('账号')
browser.find_element_by_id('p').send_keys('密码')
browser.find_element_by_id('login_button').click()
time.sleep(5) # 预留页面记载时间
# 提取数据
ele = browser.find_element_by_id('useralias')
print(ele.text)
作
页面元素定位是自动化中最重要的事情, selenium Webdriver 提供了很多种元素定位的方法。 测试人员应该熟练掌握各种定位方法。 使用最简单,最稳定的定位方法。
- 自动化测试步骤
- 定位方法大全
- 如何定位
- 通过ID查找元素: By.id()
- 通过Name查找元素:By.name()
- 通过TagName查找元素: By.tagName()
- 通过ClassName 查找元素 By.className
- 通过LinkText查找元素 By.linkText();
- 通过PartialLinkText 查找元素 By.partialLinkText()
- 通过CSS选择器查找元素、
- 通过Xpath 查找元素
自动化测试步骤

在自动化测试过程中, 测试程序通常的操作页面元素步骤
1. 找到Web的页面元素,并赋予到一个存储对象中 (WebElement)
2. 对存储页面元素的对象进行操作, 例如:点击链接,在输入框中输入字符等
3. 验证页面上的元素是否符合预期
通过这三个步骤, 我们可以完成一个页面元素的操作, 找到页面元素是很重要的一个步骤。 找不到页面元素,后面就没法做了
Web页面技术的现实复杂性, 造成大量的页面元素很难定位。 经常有人不知道怎么定位。
定位方法大全
使用WebDriver对象的findElement函数定义一个Web页面元素
使用findElements函数可以定位页面的多个元素
定位的页面元素需要使用WebElement对象来存储,以便后续使用
常用的定位页面元素方法如下, 按推荐排序
|
定位方法 |
Java语言实现实例 |
|
id 定位 |
driver.findElement(By.id(“id的值”)); |
|
name定位 |
driver.findElement(By.name(“name的值”)); |
|
链接的全部文字定位 |
driver.findElement(By.linkText(“链接的全部文字”)); |
|
链接的部分文字定位 |
driver.findElement(By.partialLinkText(“链接的部分文字”)); |
|
css 方式定位 |
driver.findElement(By.cssSelector(“css表达式”)); |
|
xpath 方式定位 |
driver.findElement(By.xpath(“xpath表达式”)); |
|
Class 名称定位 |
driver.findElement(By.className(“class属性”)); |
|
TagName 标签名称定位 |
driver.findElement(By.tagName(“标签名称”)); |
|
Jquery方式 |
Js.executeScript(“return jQuery.find(“jquery表达式”)”) |
如何定位
在使用selenium webdriver进行元素定位时,通常使用findElement或findElements方法结合By类返回元素句柄来定位元素
findElement() 方法返回一个元素, 如果没有找到,会抛出一个异常 NoElementFindException()
findElements()方法返回多个元素, 如果没有找到,会返回空数组, 不会抛出异常
如何选择 定位方法
策略是, 选择简单,稳定的定位方法。
1. 当页面元素有id属性的时候, 尽量使用id来定位。 没有的话,再选择其他定位方法
2. cssSelector 执行速度快, 推荐使用
3. 定位超链接的时候,可以考虑linkText或partialLinkText: 但是要注意的是 , 文本经常发生改变, 所以不推荐用
3. xpath 功能最强悍。 当时执行速度慢,因为需要查找整个DOM, 所以尽量少用。 实在没有办法的时候,才使用xpath
通过ID查找元素: By.id()
通过页面元素的ID来查找元素是最为推荐的方式, W3C标准推荐开发人员为每一个页面元素都提供独一无二的ID属性
一旦元素被赋予了唯一的ID属性., 我们做自动化测试的时候,很容易定位到元素. 元素的ID被作为首选的识别属性, 因为是最快的识别策略.
以百度主页为例, 搜索框的HTML示例代码如下, 它的ID为kw
<input type="text" autocomplete="off" maxlength="100" id="kw" name="wd" class="s_ipt">
"百度一下"搜索按钮元素的HTML示例代码如下, 它ID为su
<input type="submit" class="btn self-btn bg s_btn" id="su" value="百度一下">
在Selenium/WebDriver 中通过ID查找元素的Java示例代码如下
WebDriver driver = new FirefoxDriver();
driver.get("http://www.baidu.com");
WebElement searchBox = driver.findElement(By.id("kw"));
searchBox.sendKeys("小坦克 博客园");
WebElement searchButton = driver.findElement(By.id("su"));
searchButton.submit();
driver.close();
通过Name查找元素:By.name()
以豆瓣网的主页搜索框为例, 其搜索框的HTML代码如下, 它name是: q
<input type="text" autocomplete="off" name="q" placeholder="书籍、电影、音乐、小组、小站、成员" size="12" maxlength="60">
WebDriver中通过name查找豆瓣主页上的搜索框的Java代码如下:
WebDriver driver = new FirefoxDriver();
driver.get("http://www.douban.com");
WebElement searchBox = driver.findElement(By.name("q"));
searchBox.sendKeys("小坦克");
searchBox.submit();
通过TagName查找元素: By.tagName()
通过tagName来搜索元素的时候,会返回多个元素. 因此需要使用findElements()
WebDriver driver = new FirefoxDriver();
driver.get("http://www.cnblogs.com");
List<WebElement> buttons = driver.findElements(By.tagName("div"));
System.out.println("Button:" + buttons.size());
注意: 如果使用tagName, 要注意很多HTML元素的tagName是相同的,
比如单选框,复选框, 文本框,密码框.这些元素标签都是input. 此时单靠tagName无法精确获取我们想要的元素, 还需要结合type属性,才能过滤出我们要的元素
WebDriver driver = new FirefoxDriver();
driver.get("http://www.cnblogs.com");
List<WebElement> buttons = driver.findElements(By.tagName("input"));
for (WebElement webElement : buttons) {
if (webElement.getAttribute("type").equals("text")) {
System.out.println("input text is :" + webElement.getText());
}
}
通过ClassName 查找元素 By.className
以淘宝网的主页搜索为例, 其搜索框的HTML代码如下: class="search-combobox-input"
<input autocomplete="off" autofocus="true" accesskey="s" aria-label="请输入搜索文字" name="q" id="q" class="search-combobox-input" aria-haspopup="true" aria-combobox="list" role="combobox" x-webkit-grammar="builtin:translate" tabindex="0">
Java 示例代码如下
WebDriver driver = new FirefoxDriver();
driver.get("http://www.taobao.com");
Thread.sleep(15000);
WebElement searchBox = driver.findElement(By.className("search-combobox-input"));
searchBox.sendKeys("羽绒服");
searchBox.submit();
注意:使用className 来进行元素定位时, 有时会碰到一个
通过LinkText查找元素 By.linkText();
直接通过超链接上的文字信息来定位元素:例如
<a href="https://passport.baidu.com/v2/?login&tpl=mn&u=http%3A%2F%2Fwww.baidu.com%2F" name="tj_login" class="lb" onclick="return false;">登录</a>
HTML 代码如下
WebDriver driver = new FirefoxDriver();
driver.get("http://www.baidu.com");
WebElement loginLink = driver.findElement(By.linkText("登录"));
loginLink.click();
通过PartialLinkText 查找元素 By.partialLinkText()
此方法是上一个方法的加强版, 单你只想用一些关键字匹配的时候,可以使用这个方法,通过部分超链接文字来定位元素
HTML 代码如下
WebDriver driver = new FirefoxDriver();
driver.get("http://www.baidu.com");
WebElement loginLink = driver.findElement(By.partialLinkText("登"));
loginLink.click();
注意:用这种方法定位时, 可能会引起的问题是, 当你的页面中不知一个超链接包含“等”时, findElement方法只会返回第一个查找到的元素,而不会返回所有符合条件的元素
如果你想要获得所有符合条件的元素,还是只能用findElements方法
通过CSS选择器查找元素、
通过Xpath 查找元素
附: selenium java教程 (连载中, 敬请期待)
java selenium (三) 环境搭建 基于Maven
java selenium (八) Selenium IDE 用法
java selenium (九) 常见web UI 元素操作 及API使用

浙公网安备 33010602011771号