爬虫(关于selenium)

一.标签内部存在iframe时, 普通的方式无法直接定位到iframe内部的标签元素,这时候就需要进行切换

举个栗子...

比如登录QQ邮箱的输入框就是iframe,以此为例

from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait


driver = webdriver.Firefox()
driver.get('https://www.baidu.com/')
"""请求登录邮箱的网址"""
driver.get('https://mail.qq.com/')
# frame()查找和定位iframe框架的方法
driver.switch_to.frame(driver.find_element_by_id('login_frame'))

driver.find_element_by_id('switcher_plogin').click()

username = WebDriverWait(driver, 10).until(lambda driver: driver.find_element_by_css_selector('#u'))
pwd = WebDriverWait(driver, 10).until(lambda driver: driver.find_element_by_css_selector('#p'))

username.send_keys('123')
pwd.send_keys('123')

二.一个浏览器对象, 存在多个标签选项卡, 这时候也需要进行切换

第一种:

需要多个标签选项卡来回切换提取信息, 这时候需要不停的来回切换标签选项卡

举个栗子...

比如下载天堂图片的图片(需要在列表页和详情页不断的切换)

import time,requests, os, shutil
from selenium import webdriver
from urllib.request import urlretrieve
"""进行多个窗口的爬虫任务时要记得窗口之间的切换"""

options = webdriver.FirefoxOptions()
options.headless = True

# driver = webdriver.Firefox()
driver = webdriver.Firefox(firefox_options=options)

driver.get('http://www.ivsky.com/tupian/ziranfengguang/')

"""获取当前窗口(列表页)并打印当前窗口id和title"""
current_window = driver.current_window_handle
print(current_window, driver.title)

list_pics = driver.find_elements_by_css_selector('li > p > a')
print(list_pics)
for pics in list_pics:

    title = pics.text
    print(title)

    if os.path.exists(title):
        shutil.rmtree(title, ignore_errors=True)  # 移除目录树
    os.mkdir(title)
    os.chdir(title)
    """跳转到一个新的界面(详情页), 但此时的窗口id还是原来界面(列表页)的窗口id"""
    pics.click()
    time.sleep(3)
    """获取当前打开的所有窗口(列表页和详情页)"""
    windows = driver.window_handles
    print(windows)
    for win in windows:
        """判断当前(第一次打开的窗口id是否等于win, 若不等于...)"""
        if current_window != win:
            """切换窗口id(窗口id是详情页的id)"""
            driver.switch_to.window(win)

            list_imgs = driver.find_elements_by_css_selector(' li > div > a > img')

            for img in list_imgs:
                src = img.get_attribute('src')
                print(src)
                img_title = src.split('/')[-1]

                urlretrieve(src, '%s.jpg' % img_title)
            """下载图片完成,出来文件夹"""
            os.chdir(os.path.pardir)
            time.sleep(2)
            """关闭当前页面"""
            driver.close()  # 关闭当前页

    time.sleep(2)
    """将窗口id切换到列表页"""
    driver.switch_to.window(current_window)

第二种:

算是第一种的另外一种办法吧(迷茫脸)...

第一种是通过来回切换选项卡来提取数据的; 第二种是避免了第一类来回切换选项卡的操作, 采用另外一种方法来提取多个窗口的数据...大概描述一下:打开一个窗口可以获取到列表页的信息, 由于打开详情页是又新打开一个窗口呈现的, 在这里我们通过解析列表页拿到详情页的url, 然后单独的去driver.get(详情页url), 然后提取数据, 这样就避免了来回切换窗口提取数据的操作...

个人觉得第二种还有待深入的去学习

上一个栗子...淘宝网, 并且有使用多进程

# selenium本身自带这种幽灵浏览器/无头浏览器
import time

from multiprocessing import Pool
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait
from lxml.html import etree

# apply_async() / map() 方法添加函数任务的时候,需要注意如果是在类中,保证类中的方法不能是实例方法。


class TaoBao(object):
    options = webdriver.FirefoxOptions()
    options.headless = True
    driver = webdriver.Firefox()
    # driver = webdriver.Firefox(firefox_options=options)

    def __init__(self, pool):
        self.pool = pool
        self.start_url = 'https://www.taobao.com/'

    def start(self):
        self.driver.get(self.start_url)
        search_input = WebDriverWait(self.driver, 15).until(
            lambda driver: driver.find_element_by_id('q'))
        search_input.send_keys('笔记本电脑')
        """点击之后不会跳转到新的窗口(是在当前窗口打开的)"""
        self.driver.find_element_by_class_name('btn-search').click()

        windows = self.driver.window_handles
        print(windows)

        # 点击进入到列表页之后,需要使用selenium操作滚动条,让页面滚动到底部。
        for x in range(1, 11, 2):
            height = float(x) / 10
            # 根据x的值,计算整个页面高度需要循环的次数
            # document.documentElement.scrollTop:当前页面相对于窗口顶部的偏移量
            # document.documentElement.scrollHeight: 整个页面的高度,包含可滚动的部分
            js = "document.documentElement.scrollTop = document.documentElement.scrollHeight * %f" % height

            self.driver.execute_script(js)
            time.sleep(0.2)

        html = self.driver.page_source
        self.parse_list_page(html)

    # @classmethod
    # def get_list_page(cls, html):
    #     print('获取下一页源码的方法')

    def parse_list_page(self, html):

        list_html = etree.HTML(html, parser=etree.HTMLParser(encoding='utf-8'))

        # 根据list_html找到下一页的连接,然后将下一页的连接的请求也加入进程池
        # next_url = 'list_html中提取'
        # self.pool.apply_async(self.get_list_page, args=(next_url,), callback=self.parse_list_page)

        divs = list_html.cssselect('.info-cont')
        for div in divs:
            """获取详情页的url(用详情页的url请求详情页数据, 就不用打开新的窗口然后切换窗口拿数据, 是不是很方便了)"""
            detail_url = 'https:'+div.cssselect('.product-title')[0].attrib['href']
            self.pool.apply_async(self.get_detail_page, args=(detail_url,), callback=self.parse_detail_page)

    @classmethod
    def get_detail_page(cls, detail_url):
        """请求详情页的url"""
        cls.driver.get(detail_url)
        time.sleep(1)
        """提取详情页的数据"""
        detail_html = etree.HTML(cls.driver.page_source, parser=etree.HTMLParser(encoding='utf-8'))

        title = detail_html.cssselect('.panel-head > .spu-title')[0].text
        price = detail_html.cssselect('.price > strong')[0].text

        return {
            'title': title,
            'content': price
        }

    @classmethod
    def parse_detail_page(cls, data):
        print(data)


if __name__ == '__main__':
    pool = Pool()

    taobao = TaoBao(pool)
    taobao.start()

    pool.close()
    pool.join()


猜你喜欢

转载自blog.csdn.net/qq_42336549/article/details/80843943