• selenium爬虫


    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('
    ')
                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)
  • 相关阅读:
    sprintf的用法
    sscanf
    Decode the tape
    poj 1579 Function Run Fun
    Where's Waldorf?
    uva Andy's First Dictionary
    UVA Hangman Judge
    UVa Automatic Editing
    界面设计规范
    web标准下的web开发流程思考
  • 原文地址:https://www.cnblogs.com/LXP-Never/p/11386933.html
Copyright © 2020-2023  润新知