• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录

雇的辣客

雇的辣客
  • 博客园
  • 联系
  • 管理

公告

View Post

python selenium爬虫 java selenium (五) 元素定位大全

java selenium (五) 元素定位大全

 

python selenium爬虫

 

目录

   
  browser浏览器对象方法
京东爬虫案例
  实现步骤
  chromedriver设置无界面模式
  键盘操作
  鼠标操作
  切换页面
  民政部网站
  Web客户端验证
  iframe子框架

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个节点对象)

  1. browser.find_element_by_id('')
  2. browser.find_element_by_name('')
  3. browser.find_element_by_class_name('')
  4. browser.find_element_by_xpath('')
  5. browser.find_element_by_link_text('')
  6. ... ...

多元素查找([节点对象列表])

  1. browser.find_elements_by_id('')
  2. browser.find_elements_by_name('')
  3. browser.find_elements_by_class_name('')
  4. browser.find_elements_by_xpath('')
  5. ... ...

节点对象操作

  1. .send_keys('')     搜索框发送内容
  2. .click()        点击
  3. .text           获取文本内容
  4. .get_attribute('src') 获取属性值
  5. .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. 打开京东,到商品搜索页
  2. 匹配所有商品节点对象列表
  3. 把节点对象的文本内容取出来,查看规律,是否有更好的处理办法?
  4. 提取完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;
复制代码

思路

  1. selenium+Chrome打开一级页面,并提取二级页面最新链接
  2. 增量爬取: 和数据库version表中进行比对,确定之前是否爬过(是否有更新)
  3. 如果没有更新,直接提示用户,无须继续爬取
  4. 如果有更新,则删除之前表中数据,重新爬取并插入数据库表
  5. 最终完成后: 断开数据库连接,关闭浏览器
复制代码
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 提供了很多种元素定位的方法。  测试人员应该熟练掌握各种定位方法。 使用最简单,最稳定的定位方法。

 

阅读目录

  1. 自动化测试步骤
  2. 定位方法大全
  3. 如何定位
  4. 通过ID查找元素: By.id()
  5. 通过Name查找元素:By.name()
  6. 通过TagName查找元素: By.tagName()
  7. 通过ClassName 查找元素 By.className
  8. 通过LinkText查找元素 By.linkText();
  9. 通过PartialLinkText 查找元素 By.partialLinkText()
  10. 通过CSS选择器查找元素、
  11. 通过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&amp;tpl=mn&amp;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选择器查找元素、

 请看 java selenium (七) CSS 定位

通过Xpath 查找元素

 请看 java selenium (六) XPath 定位

 

附: selenium java教程 (连载中, 敬请期待)

java selenium (一) selenium 介绍

java selenium (二) 环境搭建方法一

java selenium (三) 环境搭建 基于Maven

java selenium (四) 使用浏览器调试工具

java selenium (五) 元素定位大全

java selenium (六) xpath 定位

java selenium (七) CSS 定位

java selenium (八) Selenium IDE 用法

java selenium (九) 常见web UI 元素操作 及API使用

java selenium (十) 操作浏览器

java selenium (十一) 操作弹出对话框

java selenium (十二) 操作弹出窗口

java selenium (十三) 智能等待页面加载完成

java selenium (十四) 处理Iframe 中的元素

未完待续

 

posted on 2025-01-25 23:40  雇的辣客  阅读(54)  评论(0)    收藏  举报

刷新页面返回顶部
 
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3