共计 6844 个字符,预计需要花费 18 分钟才能阅读完成。
大数据时代,各行各业对数据采集的需要日益增多,网络爬虫的使用也更为宽泛,越来越多的人开始学习网络爬虫这项技术,K 哥爬虫此前曾经推出不少爬虫进阶、逆向相干文章,为实现从易到难全方位笼罩,特设【0 根底学爬虫】专栏,帮忙小白疾速入门爬虫,本期为自动化工具 Selenium 的应用。
概述
目前,很多网站都采纳 Ajax 等技术进行动静加载数据,想要采集这类网站的数据,须要通过抓包对网站的数据接口进行剖析,去寻找想要采集的数据由哪个接口传输。而且,就算找到了数据接口,这些接口可能也是被加密过的,想要通过接口获取数据,须要对加密参数进行逆向剖析,这个过程对于初学者来说非常复杂。
为了解决这些问题,可能更加简略的进行爬取数据,咱们能够应用到一些自动化工具,如 Selenium、playwright、pyppeteer 等,这些工具能够模仿浏览器运行,间接获取到数据加载实现后的网页源码,这样咱们就能够省去简单的抓包、逆向流程,间接拿到数据。
Selenium 的应用
介绍
Selenium 是一个风行的自动化测试框架,可用于测试 Web 应用程序的用户界面。它反对多种编程语言,如 Java、Python、Ruby 等,并提供了一系列 API,能够间接操作浏览器进行测试。
装置
应用 selenium 首先须要下载浏览器驱动文件,这里以谷歌浏览器为例。在驱动下载页面找到与本人浏览器版本最为靠近的文件,如我的谷歌浏览器版本为 112.0.5615.86
,最靠近的文件为 112.0.5615.49
,抉择此文件,下载对应零碎版本的压缩包,将压缩包中的 chromedriver.exe 程序放到 python 目录中。因为失常状况下 Python 在装置时就会被增加到零碎环境变量之中,将 chromedriver.exe 放到 Python 目录下它就能够在任意地位被执行。
增加好驱动文件后须要装置 Python 的第三方库 selenium。
pip install selenium
应用
Selenium 反对多种浏览器,如谷歌、火狐、Edge、Safari 等,这里咱们以谷歌浏览器为例。
from selenium import webdriver
# 初始化浏览器对象
driver = webdriver.Chrome()
# 驱动浏览器关上指标网址
driver.get('https://www.baidu.com/')
# 打印以后页面的源代码
print(driver.page_source)
# 敞开浏览器
driver.quit()
运行代码后咱们会发现主动关上了一个浏览器,拜访了指标网址,在控制台输入了页面的源代码,而后主动敞开。
Selenium 提供了一系列实用的 Api,通过它咱们能够实现更多操作。
元素查找
在之前的文章《解析库的应用》中,咱们曾经讲到了 Xpath、bs4 这两个库的应用办法,讲到了 Xpath 的门路表达式和 CSS 选择器,因而这里次要解说定位办法,门路表达式与 CSS 选择器的应用能够去前文中理解。
以京东首页为例,想要获取秒杀栏目的商品信息,咱们能够通过多种办法来进行定位。
from selenium import webdriver
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
driver.get('https://www.jd.com/')
# 依据 Xpath 定位
goods_xpath = driver.find_elements(By.XPATH, '//div[@class="slider_list"]/div/a[@class="slider_item seckill-item slider_active"]')
# 依据 Css 选择器定位
goods_css = driver.find_elements(By.CSS_SELECTOR, 'a[class="slider_item seckill-item slider_active"]')
# 依据类名定位
goods_class_name = driver.find_elements(By.CLASS_NAME,'seckill-item')
print(goods_xpath)
for goods in goods_xpath:
# 输入节点的文本信息
print(goods.text)
driver.quit()
# [<selenium.webdriver.remote.webelement.WebElement(session="f49c1906753e404ca0a017...]
# 欧臻廷保湿修护亮颜银霜面霜 70ml 护肤品化妆品乳液滋润送女友礼物礼盒款
# ¥1380.00
# Redmi K50Pro 天玑 9000 AMOLED 2K 柔性直屏 OIS 光学防抖 120W 快充 幻镜 8GB+256GB 5G 智能手机 小米红米
# ¥2619.00
# 卡诗(KERASTASE)黑钻钥源鱼子酱洗发水 250ml 改善毛躁呵护受损
# ¥219.00
除了示例代码中的,还有其它定位办法:
driver.find_elements(By.ID,’ID’)
driver.find_elements(By.LINK_TEXT,’LINK_TEXT’)
driver.find_elements(By.PARTIAL_LINK_TEXT,’PARTIAL_LINK_TEXT’)
driver.find_elements(By.TAG_NAME,’TAG_NAME’)
元素交互
Selenium 能够实现对页面中元素的点击、输出等操作。
想要采集京东的指定商品信息,首先须要在输入框输出商品名称,而后点击搜寻按钮,网页就会跳转到搜寻页面,展现咱们搜寻的商品信息。这个流程咱们也能够通过 Selenium 来模仿实现。
driver.get('https://www.jd.com/')
# 获取搜寻框
search = driver.find_element(By.XPATH,'//div[@role="serachbox"]/input')
# 获取查问按钮
button = driver.find_element(By.XPATH,'//div[@role="serachbox"]/button')
# 在搜寻框中输出 Python
search.send_keys('Python')
# 点击查问按钮
button.click()
期待
在咱们应用 Selenium 时会遇到以下两种状况:
- 页面未加载结束,然而咱们须要的元素曾经加载结束
- 页面加载结束,然而咱们须要的元素为加载结束
Selenium 的 get 办法是默认期待页面加载结束后再执行上面的操作。在遇到第一种状况时,要采集的数据曾经生成了,然而可能因为某个资源加载迟缓导致页面始终在加载中状态,这样 Selenium 就会始终期待页面齐全加载,造成采集速度迟缓等问题。而状况二,页面曾经加载实现了,然而要采集的数据仍旧没有渲染进去,这就使 Selenium 定位元素失败导致程序异样。为了防止解决这两种状况,咱们能够设置不期待页面齐全加载,只期待指标元素加载结束。
from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
caps = DesiredCapabilities().CHROME
#不期待页面加载
caps["pageLoadStrategy"] = "none"
driver = webdriver.Chrome(desired_capabilities=caps)
强制期待
应用 time.sleep() 实现强制期待。不举荐应用。
driver.get('https://www.jd.com/')
# 强制休眠 6 秒
time.sleep(6)
隐式期待
期待页面加载的工夫,当页面加载实现后执行下一步,如果加载工夫超过设置的工夫时间接执行下一步。不举荐应用。
# 隐式期待 10 秒
driver.implicitly_wait(10)
driver.get('https://www.jd.com/')
显式期待
期待条件满足后执行下一步,条件不满足则始终期待,当超过设置的工夫时抛出异样。举荐应用。
from selenium import webdriver
import selenium.common.exceptions
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
driver = webdriver.Chrome()
driver.get('https://www.jd.com/')
try:
WebDriverWait(driver, 10).until(
EC.presence_of_all_elements_located((By.CSS_SELECTOR, 'a[class="slider_item seckill-item slider_active"]')
)
)
except selenium.common.exceptions.TimeoutException:
print('元素加载超时')
当 CSS 选择器指向的元素存在时则执行下一部,不存在则持续期待,直到超过设置的 10 秒,抛出超时异样。
Actions
上文中讲到了元素交互,其中点击、输出行为都是属于 Selenium 的动作 Api 之中的,除此之外,Selenium 还提供了十分丰盛的动作 Api,这里只介绍罕用的办法。
鼠标操作
from selenium.webdriver import ActionChains
# 单击元素并按住
clickable = driver.find_element(By.ID, "clickable")
ActionChains(driver).click_and_hold(clickable).perform()
# 双击,将鼠标挪动到元素核心并双击
clickable = driver.find_element(By.ID, "clickable")
ActionChains(driver).double_click(clickable).perform()
# 按偏移量挪动鼠标
mouse_tracker = driver.find_element(By.ID, "mouse-tracker")
ActionChains(driver).move_to_element_with_offset(mouse_tracker, 8, 0).perform()
# 按以后指针地位进行偏移,如之前没有挪动鼠标,则默认在窗口的左上角。(13, 15) 为横纵坐标的偏移值,13 为向右挪动 13,15 为向下挪动 15,正数则反之。ActionChains(driver).move_by_offset(13, 15).perform()
# 按偏移拖放。点击元素并按钮,挪动指定偏移量,而后开释鼠标
draggable = driver.find_element(By.ID, "draggable")
start = draggable.location
finish = driver.find_element(By.ID, "droppable").location
ActionChains(driver).drag_and_drop_by_offset(draggable, finish['x'] - start['x'], finish['y'] - start['y']).perform()
滚轮
# 滚动到指定元素
iframe = driver.find_element(By.TAG_NAME, "iframe")
ActionChains(driver).scroll_to_element(iframe).perform()
# 按给定值滚动,(0, delta_y) 为向右和向下滚动的量,负值则反之。footer = driver.find_element(By.TAG_NAME, "footer")
delta_y = footer.rect['y']
ActionChains(driver).scroll_by_amount(0, delta_y).perform()
反检测
Selenium 有着非常明显的缺点,就是容易被网站检测到。咱们通过 Selenium 关上网页时会发现,窗口上方会显示浏览器正受到自动测试软件的管制,这就阐明 Selenium 驱动浏览器与用户失常关上浏览器是不同的,它存在着许多 WebDriver 的特色,网站能够通过检测这些特色来禁止 Selenium 拜访。
咱们能够通过一些特征值检测的网站来比照失常拜访与 Selenium 拜访的区别。
下面是失常拜访,上面是 Selenium 拜访,能够很清晰的看到 WebDriver 一栏标红了,这就阐明 Selenium 被检测到了。网站的检测原理次要是通过查看 window.navigator 对象中是否存在 webdriver 属性。咱们理解到这一点后,能够通过一些操作来批改 window.navigator 对象,在页面未加载时将它的 webdriver 属性设置为 false,这样或者就能避开网站的检测机制。
from selenium import webdriver
from selenium.webdriver import ChromeOptions
options = ChromeOptions()
# 以最高权限运行
options.add_argument('--no-sandbox')
# navigator.webdriver 设置为 false
options.add_argument("--disable-blink-features=AutomationControlled")
# 暗藏 "Chrome 正在受到主动软件的管制" 提醒
options.add_experimental_option("excludeSwitches", ["enable-automation"])
options.add_experimental_option('useAutomationExtension', False)
driver = webdriver.Chrome(options=options)
with open('./stealth.min.js', 'r') as f:
js = f.read()
driver.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {'source': js})
能够看到,咱们进行了一些暗藏特色的操作,但在最初咱们读取一个文件,而后将这个文件信息传入到了 execute_cdp_cmd() 办法中,这个操作其实也是在暗藏特色。
stealth.min.js 来自于 puppeteer 的一个插件,puppeteer 是一个管制 headless Chrome 的 Node.js API,puppeteer 有一个插件名为 puppeteer-extra-plugin-stealth,它的开发目标就是为了避免 puppeteer 被检测,它能够暗藏许多自动化特色。puppeteer-extra 的作者也编写了一个脚本,用于将最新的特色暗藏办法 puppeteer-extra-stealth 提取到 JS 文件之中,生成的 JS 文件能够用于纯 CDP 实现,也能够用于测试开发工具中的检测躲避。而 Selenium 正好反对 CDP 的调用,CDP 全称(Chrome DevTools Protocol),利用它能够在浏览器加载之前执行 JS 语句。
如果你曾经装置了 node.js,npx extract-stealth-evasions
执行此命令就能够生成 stealth.min.js 文件。下图就暗藏特色后拜访后果。
无头模式
无头模式下网站运行不会弹出窗口,能够缩小一些资源耗费,也防止了浏览器窗口运行时对设施失常应用带来的影响,在服务器上运行须要用到。然而无头模式下被网站检测的特色点十分多,因而须要依据本人的利用场景来应用。
options = ChromeOptions()
options.add_argument('--headless')
driver = webdriver.Chrome(options=options)
总结
应用 Selenium 来进行数据的爬取是一种劣势与劣势都非常明显的抉择。它的劣势就是简略,不须要对网站进行调试,不须要关注数据的起源,大大减少了爬虫程序的开发工夫。它的劣势有多种:采集效率低,资源占用大,不稳固,容易被检测,且须要依赖于 WebDriver,当浏览器更新后就须要更新对应的 WebDriver。因而 Selenium 实用于那些逆向难度较大,且对采集效率要求不高的场景。