Web自动化测试工具,可运行在浏览器,根据指令操作浏览器,只是工具,必须与第三方浏览器结合使用,相比于之前学的爬虫只是慢了一点而已。而且这种方法爬取的东西不用在意时候ajax动态加载等反爬机制。因此找标签可以直接F12找,不用确定源码中是否存在。

安装

Linux: sudo pip3 install selenium

Windows: python -m pip install selenium

phantomjs浏览器

phantomjs浏览器又叫做无界面浏览器(又称无头浏览器),在内存中进行页面加载,运行高效。

安装(phantomjs(无界面浏览器)、chromedriver(谷歌浏览器)、geckodriver(火狐浏览器))

Windows

1、下载对应版本的phantomjschromedrivergeckodriver

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()

把上面的代码改为无界面模式

from selenium import webdriver
import time


class JdSpider(object):
    def __init__(self):
        self.url = \'https://www.jd.com/\'
        self.options = webdriver.ChromeOptions()  # 设置无界面
        self.options.add_argument(\'--headless\')  # 添加无界面参数
        # 正常创建浏览器对象即可
        self.browser = webdriver.Chrome(options=self.options)
        self.i = 0  # 统计商品数

    # 获取页面信息 - 到具体商品的页面
    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):
        # 把进度条拉到底部,使所有数据动态加载
        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\')
        item = {}
        for li in li_list:
            # find_element: 查找单元素
            item[\'name\'] = li.find_element_by_xpath(\'.//div[@class="p-name"]/a/em\').text.strip()
            item[\'price\'] = li.find_element_by_xpath(\'.//div[@class="p-price"]\').text.strip()
            item[\'comment\'] = li.find_element_by_xpath(\'.//div[@class="p-commit"]/strong\').text.strip()
            item[\'shop\'] = li.find_element_by_xpath(\'.//div[@class="p-shopnum"]\').text.strip()

            print(item)
            self.i += 1

    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(3)
            else:
                break
        print(\'商品数量:\', self.i)
        self.browser.quit()


if __name__ == \'__main__\':
    spider = JdSpider()
    spider.main()

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)

 

版权声明:本文为LXP-Never原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/LXP-Never/p/11386933.html