# 安装

pip install selenium

Chrome 驱动地址

1662688524410

把你下载的浏览器驱动放在程序所在的⽂件夹。或者放到 python 解释器所在的⽂件夹。两种⼆选其⼀

1662688571224

1662689527468

# 操作

# 点击

1662689862170

from selenium.webdriver import Chrome  # 导⼊⾕歌浏览器的类
from selenium.webdriver.common.by import By
web = Chrome()
web.get("http://lagou.com")
# 找到全国按钮
btn = web.find_element(by=By.XPATH, value='//*[@id="changeCityBox"]/p[1]/a')
# 点击
btn.click()

有时上面会出现错误: selenium.common.exceptions.ElementClickInterceptedException: Message: element click intercepted: Element is not clickable with Selenium and Python ,提示按钮不可点击,此时可以用下面的方式:

btn = web.find_element(by=By.XPATH, value='//*[@id="changeCityBox"]/p[1]/a')
driver.execute_script("arguments[0].click();", btn)

# 无头浏览器

在对浏览器对象进行实例化时进行如下修改就行了。

from selenium.webdriver.chrome.options import Options
opt = Options()
opt.add_argument('--headless')
opt.add_argument('--disable-gpu')
web = Chrome(options=opt)

# 搜索

人的过程:找到⽂本框输⼊ "python", 点击 "搜索" 按钮.
机器的过程:找到⽂本框输⼊ "python", 点击 "搜索" 按钮.

selenium 最爽的地⽅就是这⾥。⼈是怎么操作的。机器就怎么操作。爽到极点

from selenium.webdriver import Chrome  # 导⼊⾕歌浏览器的类
from selenium.webdriver.common.by import By
web = Chrome()
web.get("http://lagou.com")
# 找到全国按钮
btn = web.find_element(by=By.XPATH, value='//*[@id="changeCityBox"]/p[1]/a')
# 点击
btn.click()
# 找到文本框,输入 python
web.find_element(by=By.XPATH, value='//*[@id="search_input"]').send_keys("python")
# 点击搜索按钮
web.find_element(by=By.XPATH, value='//*[@id="search_button"]').click()

send_keys() 这⾥要说⼀下。如果我们给出的是⼀个字符串。就是输⼊⽂本。但是,如果给出的是⼀个键盘指令,那就按下键盘。⽐如,我想要按回⻋按钮,则上面的搜索改为

from selenium.webdriver.common.keys import Keys
# 找到文本框,输入 python
web.find_element(by=By.XPATH, value='//*[@id="search_input"]').send_keys("python", Keys.ENTER)
# 不用再点击搜索按钮了

1662690704174

# 获取信息

# 一种错误的方式


顶层元素,包含的小item的列表
//*[@id="jobList"]/div[1]

每个小item
//*[@id="jobList"]/div[1]/div[1]

//*[@id="jobList"]/div[1]/div[2]

小item中提取信息

职位
//*[@id="jobList"]/div[1]/div[1]/div[1]/div[1]/div[1]/a

薪资
//*[@id="jobList"]/div[1]/div[1]/div[1]/div[1]/div[2]/span

//*[@id="jobList"]/div[1]/div[2]/div[1]/div[1]/div[1]/a

//*[@id="jobList"]/div[1]/div[2]/div[1]/div[1]/div[2]/span


1662691824293

1662691852940

观察它们的 xpath:

顶层元素,包含的小item的列表
//*[@id="jobList"]/div[1]

每个小item
//*[@id="jobList"]/div[1]/div[1]

//*[@id="jobList"]/div[1]/div[2]
ls = web.find_elements(by=By.XPATH, value='//*[@id="jobList"]/div[1]/div')

就能获取到所有 item 组成的列表。

<pre class="vditor-reset" placeholder="" contenteditable="true" spellcheck="false"><p data-block="0"><br class="Apple-interchange-newline"/><img src="https://file+.vscode-resource.vscode-cdn.net/d%3A/Projects/example/source/_posts/tool/ 爬虫 /images/selenium/1662691753092.png" alt="1662691753092"/></p></pre>


item项
//*[@id="jobList"]/div[1]/div[1]

职位
//*[@id="jobList"]/div[1]/div[1]/div[1]/div[1]/div[1]/a

相对:/div[1]/div[1]/div[1]/a

薪资
//*[@id="jobList"]/div[1]/div[1]/div[1]/div[1]/div[2]/span

相对:./div[1]/div[1]/div[2]/span

ls = web.find_elements_by_xpath('//*
[@id="s_position_list"]/ul/li')  # ⼀次性提取多个元素⽤ elements
for item in ls:
    name = 
item.find_element_by_xpath('./div[1]/div[1]/d iv[1]/a/h3').text
    addr = 
item.find_element_by_xpath('./div[1]/div[2]/d iv[1]/a').text

上面的思想是是先获取整个列表,然后再遍历列表从每个列表项中获取信息。

但这样往往在从列表项中提取数据的时候会出现 exception selenium.common.exceptions.StaleElementReferenceException(msg=None, screen=None, stacktrace=None) 的异常

搜到的解释如下:

当一个元素的引用 变旧

变旧的意思是这个元素不在出现在页面的 DOM 里

可能出现这个异常的原因包括但不限于: * 你不在同一个页面,或者你获取到元素之后页面被刷新了 * 元素被定位后 被移动了又重新加到屏幕上,这样元素就被重置了。典型的例子是 javascript 框架当值改变,节点就被重建了 * 元素所在的框架或者其他内容被刷新了

我的理解是:selenium 是基于浏览器进行操作,在获取的整个列表的时候,保存的是页面信息(如 url 等)而没有将页面的内容保存下载,当尝试从列表项中提取数据的时候,需要再次从浏览器中请求数据,然而这时浏览器已经刷新了,无法根据先前保存的信息获取数据。

尝试在不同时刻打印获取的相同 xpath 的元素, <selenium.webdriver.remote.webelement.WebElement (session="a2dfac359b19d52406aa9b3300f9c99e", element="b4a5929d-be1e-4448-bdf8-12929e676f09")> ,发现它们的 session 相同,但是 element 不同。

# 循环爬取信息

所以,正确的做法应该是,需要数据时直接基于浏览器对象去获取。

import time
from selenium.webdriver import Chrome  # 导⼊⾕歌浏览器的类
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
web = Chrome()
web.get("http://lagou.com")
# 找到全国按钮
btn = web.find_element(by=By.XPATH, value='//*[@id="changeCityBox"]/p[1]/a')
# 点击
btn.click()
# 找到文本框,输入 python
web.find_element(by=By.XPATH, value='//*[@id="search_input"]').send_keys("python", Keys.ENTER)
ls = web.find_elements(by=By.XPATH, value='//*[@id="jobList"]/div[1]/div')
# print(len(ls))
# print (ls [0].text)  # 打印出来的时候会自动提取文本,不会打印出标签
'''
职位
//*[@id="jobList"]/div[1]/div[1]/div[1]/div[1]/div[1]/a
薪资
//*[@id="jobList"]/div[1]/div[1]/div[1]/div[1]/div[2]/span
'''
length = len(ls)
time.sleep(1.5)
for i in range(length):
    # 分析每个数据的 xpath 特点,使用格式字符串
    work = web.find_element(by=By.XPATH, value=f'//*[@id="jobList"]/div[1]/div[{i+1}]/div[1]/div[1]/div[1]/a').text
    salary = web.find_element(by=By.XPATH, value=f'//*[@id="jobList"]/div[1]/div[{i+1}]/div[1]/div[1]/div[2]/span').text
    print(work, "  ", salary)

# 多窗口调度

在一个网站上的信息不够全⾯。我们希望得到的不仅仅是⼀个岗位名称和公司名称,我更想知道更加详细的职位描述以及岗位要求.

1662700609868

现在搜索页面点击进入详情页,然后切换窗口,爬取相应信息之后,关闭详情页窗口再返回主页面。循环操作。

import time
from selenium.webdriver import Chrome  # 导⼊⾕歌浏览器的类
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
web = Chrome()
web.set_window_size(800, 800)
web.get("http://lagou.com")
# 找到全国按钮
btn = web.find_element(by=By.XPATH, value='//*[@id="changeCityBox"]/p[1]/a')
# 点击
btn.click()
# 找到文本框,输入 python
web.find_element(by=By.XPATH, value='//*[@id="search_input"]').send_keys("python", Keys.ENTER)
ls = web.find_elements(by=By.XPATH, value='//*[@id="jobList"]/div[1]/div')
'''
//*[@id="jobList"]/div[1]/div[1]
//*[@id="jobList"]/div[1]/div[3]
f'//*[@id="jobList"]/div[1]/div[{i}]'
'''
for i in range(len(ls)):
    btn = web.find_element(by=By.XPATH, value=f'//*[@id="jobList"]/div[1]/div[{i + 1}]')
    btn.click()
    web.switch_to.window(web.window_handles[-1]) # 跳转到最后一个窗口
    detail = web.find_element(by=By.XPATH, value='//*[@id="job_detail"]/dd[2]/div').text
    print(detail)
    time.sleep(2)
    web.close()  # 关闭窗口
    web.switch_to.window(web.window_handles[0]) # 返回第一个窗口
    pro = web.find_element(by=By.XPATH, value='//*[@id="order"]/div/div[1]').text
    print('返回了 ', pro)
    # break

下面的代码是切换 iframe 的,但是实际测试的时候发现有问题。

iframe = web.find_element(by=By.XPATH, value='//*[@id="vodplay"]')
# print(iframe)
web.switch_to.frame(iframe)
import time
import pandas as pd
from selenium.webdriver import Chrome  # 导⼊⾕歌浏览器的类
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
web = Chrome()
web.set_window_size(800, 800)
web.get("http://lagou.com")
# 找到全国按钮
btn = web.find_element(by=By.XPATH, value='//*[@id="changeCityBox"]/p[1]/a')
# 点击
btn.click()
# 找到文本框,输入 python
web.find_element(by=By.XPATH, value='//*[@id="search_input"]').send_keys("python", Keys.ENTER)
ls = web.find_elements(by=By.XPATH, value='//*[@id="jobList"]/div[1]/div')
'''
//*[@id="jobList"]/div[1]/div[1]
//*[@id="jobList"]/div[1]/div[3]
f'//*[@id="jobList"]/div[1]/div[{i}]'
'''
df = pd.DataFrame(columns=['name', 'salary', 'detail'])
for i in range(len(ls) - 1):
    time.sleep(0.5)
    '''
    name: //*[@id="jobList"]/div[1]/div[2]/div[1]/div[1]/div[1]/a
          //*[@id="jobList"]/div[1]/div[3]/div[1]/div[1]/div[1]/a
          f'//*[@id="jobList"]/div[1]/div[{i + 1}]/div[1]/div[1]/div[1]/a'
    salary: //*[@id="jobList"]/div[1]/div[2]/div[1]/div[1]/div[2]/span
            //*[@id="jobList"]/div[1]/div[3]/div[1]/div[1]/div[2]/span
            f'//*[@id="jobList"]/div[1]/div[{i + 1}]/div[1]/div[1]/div[2]/span'
    '''
name = web.find_element(by=By.XPATH, value=f'//*[@id="jobList"]/div[1]/div[{i + 1}]/div[1]/div[1]/div[1]/a').text
    salary = web.find_element(by=By.XPATH, value=f'//*[@id="jobList"]/div[1]/div[{i + 1}]/div[1]/div[1]/div[2]/span').text
    print(name, '  ', salary)
    # 跳转窗口
btn = web.find_element(by=By.XPATH, value=f'//*[@id="jobList"]/div[1]/div[{i + 1}]')
    btn.click()
    web.switch_to.window(web.window_handles[-1])  # 跳转到最后一个窗口
time.sleep(0.8)
    detail = web.find_element(by=By.XPATH, value='//*[@id="job_detail"]/dd[2]/div').text
    print(detail)
    df.append({'name':name, 'salary': salary, 'detail': detail}, ignore_index=True)
    # time.sleep(2)
web.close()  # 关闭窗口
web.switch_to.window(web.window_handles[0])  # 返回第一个窗口
pro = web.find_element(by=By.XPATH, value='//*[@id="order"]/div/div[1]').text
    print('返回了 ', pro)
    # break
df.to_excel("out.xlsx", sheet_name="test")
'''
//*[@id="container"]/div[1]
//*[@id="job_detail"]/dd[2]/div
'''

# 验证方式的解决

在 selenium 的代码的需要验证部分嵌入 time.sleep() ,设置休眠时间,此时进行人为登录验证,在登录之后再进行自动化。

但是这时就无法进行无头浏览器操作了。

# 举例

爬取拉勾网的 python 下的职业招聘信息,同时使用 pandas 进行数据存储

import time
import pandas as pd
from selenium.webdriver import Chrome  # 导⼊⾕歌浏览器的类
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium import webdriver
web = Chrome()
web.set_window_size(700, 700)
web.implicitly_wait(20)
url = 'https://www.lagou.com/'
web.get(url)
# 登录
# 找到全国按钮
btn = web.find_element(by=By.XPATH, value='//*[@id="changeCityBox"]/p[1]/a')
# 点击
btn.click()
web.find_element(by=By.XPATH, value='//*[@id="lg_tbar"]/div[1]/div[2]/ul/li[1]/a').click()
web.find_element(by=By.XPATH,
                 value='/html/body/div[12]/div/div[2]/div/div[2]/div/div[2]/div[3]/div[1]/div/div[2]/div[3]/div').click()
# 输入账号和密码
web.find_element(by=By.XPATH,
                 value='/html/body/div[12]/div/div[2]/div/div[2]/div/div[2]/div[3]/div[1]/div/div[1]/div[1]/input').send_keys(
    '13659722913')
web.find_element(by=By.XPATH,
                 value='/html/body/div[12]/div/div[2]/div/div[2]/div/div[2]/div[3]/div[1]/div/div[1]/div[2]/input').send_keys(
    '!JUNyuan031517')
web.find_element(by=By.XPATH,
                 value='/html/body/div[12]/div/div[2]/div/div[2]/div/div[2]/div[3]/div[3]/div[2]/div[2]/div').click()
web.find_element(by=By.XPATH, value='/html/body/div[12]/div/div[2]/div/div[2]/div/div[2]/div[3]/div[2]/button').click()
# 人工验证
print('开始人工验证')
time.sleep(13)
print('人工验证完毕')
# 找到文本框,输入 python
web.find_element(by=By.XPATH, value='//*[@id="search_input"]').send_keys("python", Keys.ENTER)
ls = web.find_elements(by=By.XPATH, value='//*[@id="jobList"]/div[1]/div')
print('一页的信息个数是:', len(ls))
'''
//*[@id="jobList"]/div[1]/div[1]
//*[@id="jobList"]/div[1]/div[3]
f'//*[@id="jobList"]/div[1]/div[{i}]'
'''
df = pd.DataFrame(columns=['name', 'salary', 'detail'])
# 此时进入爬取的主页面,准备爬取 20 页
for j in range(20):
    print(f'进行第{j+1}页信息的爬取')
    # 进行此页的信息爬取
    for i in range(len(ls) - 1):
        print(f'    进行第{j + 1}页第{i + 1}个信息的爬取')
        time.sleep(0.5)
        '''
        name: //*[@id="jobList"]/div[1]/div[2]/div[1]/div[1]/div[1]/a
              //*[@id="jobList"]/div[1]/div[3]/div[1]/div[1]/div[1]/a
              f'//*[@id="jobList"]/div[1]/div[{i + 1}]/div[1]/div[1]/div[1]/a'
        salary: //*[@id="jobList"]/div[1]/div[2]/div[1]/div[1]/div[2]/span
                //*[@id="jobList"]/div[1]/div[3]/div[1]/div[1]/div[2]/span
                f'//*[@id="jobList"]/div[1]/div[{i + 1}]/div[1]/div[1]/div[2]/span'
        '''
        name = web.find_element(by=By.XPATH,
                                value=f'//*[@id="jobList"]/div[1]/div[{i + 1}]/div[1]/div[1]/div[1]/a').text
        salary = web.find_element(by=By.XPATH,
                                  value=f'//*[@id="jobList"]/div[1]/div[{i + 1}]/div[1]/div[1]/div[2]/span').text
        print(name, '  ', salary)
        # 跳转窗口
        # //*[@id="jobList"]/div[1]/div[1]/div[1]/div[1]/div[1]/a
        btn = web.find_element(by=By.XPATH, value=f'//*[@id="jobList"]/div[1]/div[{i + 1}]/div[1]/div[1]/div[1]/a')
        web.execute_script("arguments[0].click();", btn)
        web.switch_to.window(web.window_handles[-1])  # 跳转到最后一个窗口
        time.sleep(1)
        detail = web.find_element(by=By.XPATH, value='//*[@id="job_detail"]/dd[2]/div').text
        print(detail)
        df = df.append({'name': name, 'salary': salary, 'detail': detail}, ignore_index=True)
        # time.sleep(2)
        web.close()  # 关闭窗口
        web.switch_to.window(web.window_handles[0])  # 返回第一个窗口
        pro = web.find_element(by=By.XPATH, value='//*[@id="order"]/div/div[1]').text
        print('返回了 ', pro)
        if i == 5:
            break
    # 点击进入下一页
    time.sleep(0.5)
    btn = web.find_element(by=By.XPATH, value='//*[@id="jobList"]/div[3]/ul/li[9]/a')
    web.execute_script("arguments[0].click();", btn)
df.to_excel("out.xlsx", sheet_name="test")
print(df)