from bs4 import BeautifulSoup
解析器 | 使用方法 | 优势 | 劣势 |
---|---|---|---|
Python标准库 | BeautifulSoup(markup, “html.parser”) | Python的内置标准库、执行速度适中 、文档容错能力强 | Python 2.7.3 or 3.2.2)前的版本中文容错能力差 |
lxml HTML 解析器 | BeautifulSoup(markup, “lxml”) | 速度快、文档容错能力强 | 需要安装C语言库 |
lxml XML 解析器 | BeautifulSoup(markup, “xml”) | 速度快、唯一支持XML的解析器 | 需要安装C语言库 |
html5lib | BeautifulSoup(markup, “html5lib”) | 最好的容错性、以浏览器的方式解析文档、生成HTML5格式的文档 | 速度慢、不依赖外部扩展 |
def __init__(self, markup="", features=None, builder=None,
parse_only=None, from_encoding=None, exclude_encodings=None,
element_classes=None, **kwargs):
"""Constructor.
:param markup: html文本或file-like对象
:param features: 解析器:("lxml","lxml-xml", "html.parser", or "html5lib")
"""
soup = BeautifulSoup("html","lxml")
print(soup.title) #选择title标签。
print(soup.head) #选择head标签
print(soup.p) #选择p标签
print(soup.title.name) #获取title标签名
print(soup.p.attrs['name']) #获取p标签name属性值
print(soup.p['name']) #获取p标签name属性值
print(soup.p.string) #获取p标签内容,该方法只能获取第一个p标签内容
print(soup.head.title.string) #嵌套选择,用点号分割,层层嵌套
print(soup.p.contents) # 获取p标签的子节点及其内容,->list
print(soup.p.children) # 获取获取标签的子节点->iter
print(soup.p.descendants) # 返回子孙节点(不仅仅是子节点,返回结果为迭代器)
print(soup.a.parent) # 返回的是第一个a标签的父节点
print(soup.a.parents) # 返回的是父节点以及祖先节点
print(list(enumerate(soup.a.next_siblings))) #a标签的下一个兄弟节点
print(list(enumerate(soup.a.previous_siblings))) #上一个兄弟
def find_all(self, name=None, attrs={}, recursive=True, string=None,
limit=None, **kwargs):
"""
:param name: 标签名称,可以为字符串,列表,正则
:param attrs: 标签属性
:param recursive: 递归
:param limit: 限制数量
:kwargs: key=val格式的标签属性,如果是class属性,需要写为class_=val
:return: A ResultSet of PageElements.
:rtype: bs4.element.ResultSet
"""
def find(self, name=None, attrs={}, recursive=True, string=None,
**kwargs):
r = None
l = self.find_all(name, attrs, recursive, string, 1, **kwargs)
if l:
r = l[0]
return r
对于返回的Element对象:
find_parents() 返回所有祖先节点
find_parent() 返回直接父节点
find_next_siblings() 返回后面所有兄弟节点
find_next_sibling() 返回后面第一个兄弟节点
find_all_next() 返回节点后所有符合条件的节点
find_next() 返回第一个符合条件的节点
find_all_previous() 返回节点后所有符合条件的节点
find_previous() 返回第一个符合条件的节点
select()主要是以CSS选择器(selector)的方式寻找元素。如果找到元素,则返回元素列表;否则返回空列表。
寻找所有标签的元素
;寻找所有标签的元素
寻找所有标签且包含name属性的元素
;.
寻找所有class属性为name的元素
;#
寻找所有id属性为name的元素
;>
查找所有class为list的div中的ul里的li标签里面的a标签
;寻找所有标签且id为name的元素
寻找所有class为name的标签
;大部分情况下,find和find_all方法就可以解决问题
Selenium 是一个自动化测试工具,利用它可以驱动浏览器执行特定的动作,如点击、下拉等操作。
pip install selenium
Selenium 支持非常多的浏览器
from selenium import webdriver
# 常用Chrome和Edge
browser = webdriver.Chrome()
browser = webdriver.Edge()
browser = webdriver.Firefox()
browser = webdriver.Safari()
browser = webdriver.Android()
browser = webdriver.Ie()
browser = webdriver.Opera()
browser = webdriver.PhantomJS()
可以将浏览器驱动放到selenium库的目录下,这样在初始化browser对象时就可以不用传入参数了或者干脆丢个文件夹下,配置环境变量就行。
from selenium import webdriver
from time import sleep
browser = webdriver.Chrome()
# 打开百度
browser.get('https://www.baidu.com')
sleep(5)
# 使用完关闭
browser.close()
<button id="submit" name="submitBtn" class="text-center">提交button>
find_element_by_id("submit")
find_element_by_name("submitBtn")
find_element_by_class_name("text-center")
find_element_by_tag_name("button")
find_element_by_link_text("提交")
find_element_by_link_text("交")
find_element_by_xpath("//div[@id='csdn-toolbar']/div/div/div[2]/div/div/input[1]")
方法 | 例子 | 描述 |
---|---|---|
.class | .toolbar-search-container |
选择 class = 'toolbar-search-container' 的所有元素 |
#id | #toolbar-search-input |
选择 id = 'toolbar-search-input' 的元素 |
* | * |
选择所有元素 |
element | input |
选择所有 元素 |
element>element | div>input |
选择父元素为
|
element+element | div+input |
选择同一级中在
|
[attribute=value] | type='text' |
选择 type = 'text' 的所有元素 |
find_element_by_css_selector('#submit')
find_element_by_css_selector('html>body>div>div>div>div>div>div>button')
定位一个元素 | 定位多个元素 | 含义 |
---|---|---|
find_element_by_id | find_elements_by_id | 通过元素id定位 |
find_element_by_name | find_elements_by_name | 通过元素name定位 |
find_element_by_xpath | find_elements_by_xpath | 通过xpath表达式定位 |
find_element_by_link_text | find_elements_by_link_tex | 通过完整超链接定位 |
find_element_by_partial_link_text | find_elements_by_partial_link_text | 通过部分链接定位 |
find_element_by_tag_name | find_elements_by_tag_name | 通过标签定位 |
find_element_by_class_name | find_elements_by_class_name | 通过类名进行定位 |
find_elements_by_css_selector | find_elements_by_css_selector | 通过css选择器进行定位 |
注意:高版本的selenium只提供了find_element和find_elements方法,通过什么寻找需要额外传入By参数。
from selenium.webdriver.common.by import By
class By:
"""
Set of supported locator strategies.
"""
ID = "id"
XPATH = "xpath"
LINK_TEXT = "link text"
PARTIAL_LINK_TEXT = "partial link text"
NAME = "name"
TAG_NAME = "tag name"
CLASS_NAME = "class name"
CSS_SELECTOR = "css selector"
def find_element(self, by=By.ID, value=None) -> WebElement:
pass
def find_elements(self, by=By.ID, value=None) -> List[WebElement]:
pass
方法 | 说明 |
---|---|
set_window_size() | 设置浏览器的大小 |
back() | 控制浏览器后退 |
forward() | 控制浏览器前进 |
refresh() | 刷新当前页面 |
clear() | 清除文本 |
send_keys (value) | 模拟按键输入 |
click() | 单击元素 |
submit() | 用于提交表单 |
get_attribute(name) | 获取元素属性值 |
is_displayed() | 设置该元素是否用户可见 |
size | 返回元素的尺寸 |
text | 获取元素的文本 |
execute_script(js) | 执行js脚本 |
page_source | 获取页面源码(js已经加载过了) |
from selenium import webdriver
from selenium.webdriver.common.by import By
edge = webdriver.Edge()
# 提交表单
edge.find_element(By.ID, "submit").submit()
def set_window_size(self, width, height, windowHandle: str = 'current') -> None:
"""
设置浏览器的长宽
driver.set_window_size(800,600)
"""
还有一些快捷方式:
def maximize_window(self) -> None:
"""
最大化
"""
command = Command.W3C_MAXIMIZE_WINDOW
self.execute(command, None)
def fullscreen_window(self) -> None:
"""
全屏
"""
self.execute(Command.FULLSCREEN_WINDOW)
def minimize_window(self) -> None:
"""
最小化
"""
self.execute(Command.MINIMIZE_WINDOW)
webdriver 提供 back 和 forward 方法来实现页面的后退与前进。
#返回
driver.back()
#前进
driver.forward()
焦点:selenium中,弹出新窗口,焦点不会切换过去,会导致找不到新页面的标签。
方法 | 说明 |
---|---|
current_window_handle | 获得当前窗口句柄 |
window_handles | 返回所有窗口的句柄到当前会话 |
switch_to.window() | 用于切换到相应的窗口,与上一节的switch_to.frame()类似,前者用于不同窗口的切换,后者用于不同表单之间的切换。 |
# 获取打开的多个窗口句柄
windows = driver.window_handles
# 切换到当前最新打开的窗口
driver.switch_to.window(windows[-1])
方法 | 说明 |
---|---|
ActionChains(driver) | 构造ActionChains对象 |
context_click() | 执行鼠标悬停操作 |
move_to_element(above) | 右击 |
double_click() | 双击 |
drag_and_drop() | 拖动 |
move_to_element(above) | 执行鼠标悬停操作 |
context_click() | 用于模拟鼠标右键操作, 在调用时需要指定元素定位 |
perform() | 执行所有 ActionChains 中存储的行为,可以理解成是对整个操作的提交动作 |
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.common.action_chains import ActionChains
edge = webdriver.Edge()
# 提交表单
edge.maximize_window()
# 点击验证码
ActionChains(edge).move_to_element_with_offset(验证码标签,偏移量x,偏移量y).click().perform()
# 定位要拖动的元素
source = edge.find_element_by_xpath('xxx')
# 定位目标元素
target = edge.find_element_by_xpath('xxx')
# 执行拖动动作
ActionChains(edge).drag_and_drop(source, target).perform()
rom selenium.webdriver.common.keys import Keys
# 模拟回车键进行跳转(输入内容后)
edge.find_element_by_id('xxx').send_keys(Keys.ENTER)
模拟键盘按键 | 说明 |
---|---|
send_keys(Keys.BACK_SPACE) | 删除键(BackSpace) |
send_keys(Keys.SPACE) | 空格键(Space) |
send_keys(Keys.TAB) | 制表键(Tab) |
send_keys(Keys.ESCAPE) | 回退键(Esc) |
send_keys(Keys.ENTER) | 回车键(Enter) |
模拟键盘按键 | 说明 |
---|---|
send_keys(Keys.CONTROL,‘a’) | 全选(Ctrl+A) |
send_keys(Keys.CONTROL,‘c’) | 复制(Ctrl+C) |
send_keys(Keys.CONTROL,‘x’) | 剪切(Ctrl+X) |
send_keys(Keys.CONTROL,‘v’) | 粘贴(Ctrl+V) |
send_keys(Keys.F1…Fn) | 键盘 F1…Fn |
显式等待:设置一个超时时间,每个一段时间就去检测一次该元素是否存在,如果存在则执行后续内容,如果超过最大时间(超时时间)则抛出超时异常(TimeoutException)。显示等待需要使用 WebDriverWait,同时配合 until 或 not until 。
WebDriverWait(driver, timeout, poll_frequency=0.5, ignored_exceptions=None)
- driver:浏览器驱动
- timeout:超时时间,单位秒
- poll_frequency:每次检测的间隔时间,默认为0.5秒
- ignored_exceptions:指定忽略的异常,如果在调用 until 或 until_not 的过程中抛出指定忽略的异常,则不中断代码,默认忽略的只有 NoSuchElementException 。
------------------------------------------------------
until(method, message=’ ‘)
until_not(method, message=’ ')
- method:指定预期条件的判断方法
- message:TimeoutException报错信息
-----------------------------------------------------------------------------
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.common.by import By
driver = webdriver.Chrome()
element = WebDriverWait(driver, 5, 0.5).until(EC.presence_of_element_located((By.ID, 'submitBtn')),
message='guys,不是叔找不到,是你程序有Bug!')
方法 | 描述 |
---|---|
title_is(‘百度一下’) | 判断当前页面的 title 是否等于预期 |
title_contains(‘百度’) | 判断当前页面的 title 是否包含预期字符串 |
presence_of_element_located(locator) | 判断元素是否被加到了 dom 树里,并不代表该元素一定可见 |
visibility_of_element_located(locator) | 判断元素是否可见,可见代表元素非隐藏,并且元素的宽和高都不等于0 |
visibility_of(element) | 跟上一个方法作用相同,但传入参数为 element |
text_to_be_present_in_element(locator , ‘百度’) | 判断元素中的 text 是否包含了预期的字符串 |
text_to_be_present_in_element_value(locator , ‘某值’) | 判断元素中的 value 属性是否包含了预期的字符串 |
frame_to_be_available_and_switch_to_it(locator) | 判断该 frame 是否可以 switch 进去,True 则 switch 进去,反之 False |
invisibility_of_element_located(locator) | 判断元素中是否不存在于 dom 树或不可见 |
element_to_be_clickable(locator) | 判断元素中是否可见并且是可点击的 |
staleness_of(element) | 等待元素从 dom 树中移除 |
element_to_be_selected(element) | 判断元素是否被选中,一般用在下拉列表 |
element_selection_state_to_be(element, True) | 判断元素的选中状态是否符合预期,参数 element,第二个参数为 True/False |
element_located_selection_state_to_be(locator, True) | 跟上一个方法作用相同,但传入参数为 locator |
alert_is_present() | 判断页面上是否存在 alert |
隐式等待是全局性的,即运行过程中,如果元素可以定位到,它不会影响代码运行,但如果定位不到,则它会以轮询的方式不断地访问元素直到元素被找到,若超过指定时间,则抛出异常。
# 等待5s
driver.implicitly_wait(5)
try:
# 触发隐式等待
driver.find_element_by_id('btn')
except Exception as e:
print(f'叔找不到你要的标签,叔给你报个错:{str(e)}')
time.sleep()
对于内嵌的页面 selenium 是无法直接定位的,需要使用 switch_to.frame() 方法将当前操作的对象切换成 frame/iframe 内嵌的页面。
switch_to.frame() 默认可以用的 id 或 name 属性直接定位,但如果 iframe 没有 id 或 name ,这时就需要使用 xpath 进行定位。
# 1.通过id定位
driver.switch_to.frame('fuck off')
# 2.通过name定位
driver.switch_to.frame('fuck off')
# 3. 通过xpath定位
iframe_label = driver.find_element_by_xpath('/html/body/fuck/off/iframe')
driver.switch_to.frame(iframe_label)
PS:**的,之前写超星某某通自动评论的脚本,弹窗真心恶心到我了。
JavaScript 有三种弹窗 alert(确认)、confirm(确认、取消)、prompt(文本框、确认、取消)。
方法 | 描述 |
---|---|
text |
获取弹窗中的文字 |
accept() |
接受(确认)弹窗内容 |
dismiss() |
解除(取消)弹窗 |
send_keys() |
发送文本至警告框 |
alert = driver.switch_to.alert
alert.accept()
对于文件操作,我还是觉得可以先拿到url然后多进程,多线程,协程下载就行
edge.find_element_by_xpath('//*[@name="upload"]').send_keys(file_path)
需要用到浏览器选项,from selenium.webdriver import ChromeOptions
from selenium.webdriver import ChromeOptions
prefs = {
# 禁止弹窗
'profile.default_content_settings.popups': 0,
# 文件下载路径
'download.default_directory': "file_Path"
}
option = webdriver.ChromeOptions()
option.add_experimental_option('prefs', prefs)
driver = webdriver.Chrome(options=option)
driver.find_element_by_xpath('下载链接').click()
# 如果出现非安全需要切换到最新窗口
driver.switch_to.window(driver.window_handles[-1])
# 输入thisisunsafe即可
driver.find_element_by_xpath('./html').send_keys('thisisunsafe')
from selenium.webdriver.support.select import Select
方法 | 说明 |
---|---|
select_by_value(“选择值”) | select标签的value属性的值 |
select_by_index(“索引值”) | 下拉框的索引 |
select_by_visible_testx(“文本值”) | 下拉框的文本值 |
sel = edge.find_element_by_xpath("//select[@id='nr']")
for selectIndex in range(len(sel.options)):
# 索引
print(selectIndex)
Select(sel).select_by_value('50') # 显示50条
# 截图
edge.get_screenshot_as_file(r'截图后保存路径')
# 获取网页图片二进制数据
edge.find_element_by_xpath(xx).screenshot_as_png()
# 对页面进行截图,返回二进制数据
edge.get_screenshot_as_png()
# 获取当前页面url
driver.current_url
# 获取当前html源码
driver.page_source
# 获取当前页面标题
driver.title
# 获取浏览器名称(chrome)
driver.name
# 对页面进行截图,返回二进制数据
driver.get_screenshot_as_png()
# 设置浏览器尺寸
driver.get_window_size()
# 获取浏览器尺寸,位置
driver.get_window_rect()
# 获取浏览器位置(左上角)
driver.get_window_position()
# 设置浏览器尺寸
driver.set_window_size(width=1000, height=600)
# 设置浏览器位置(左上角)
driver.set_window_position(x=500, y=600)
# 设置浏览器的尺寸,位置
driver.set_window_rect(x=200, y=400, width=1000, height=600)
from selenium import webdriver
from selenium.webdriver import ChromeOptions
option = ChromeOptions()
option.add_argument('--headless')
# 都无头了要个锤子图片,不用gpu渲染
option.add_argument("--disable-gpu")
browser = webdriver.Chrome(options=option)
防止webdriver检测不通过,感谢 @CSDN博主「小小明-代码实体」
from selenium import webdriver
from selenium.webdriver import ChromeOptions
option = ChromeOptions()
option.add_argument("--headless")
option.add_experimental_option('excludeSwitches', ['enable-automation'])
option.add_experimental_option('useAutomationExtension', False)
option.add_argument('user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.198 Safari/537.36')
option.add_argument("--disable-blink-features=AutomationControlled")
browser = webdriver.Chrome(options=option)
# https://github.com/kingname/stealth.min.js
with open('stealth.min.js') as f:
js = f.read()
browser.execute_cdp_cmd('Page.addScriptToEvaluateOnNewDocument', {
'source': js
})
由于无论去哪都携带着十几个纸质笔记本过于不便,最近计划将笔记本内容全部搬到CSDN做存储。
本文结合了我的爬虫笔记和网络大佬(小小明-代码实体,Dream丶Killer)的博客,对爬虫学习做一个总结。