爬虫解析模块(bs4,selenium)

bs4文档

from bs4 import BeautifulSoup
  1. Beautiful Soup提供一些简单的、python式的函数用来处理导航、搜索、修改分析树等功能。
  2. Beautiful Soup自动将输入文档转换为Unicode编码,输出文档转换为utf-8编码。

解析器

解析器 使用方法 优势 劣势
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)))	#上一个兄弟

find()和find_all()方法

  1. find_all()方法返回值:所有符合条件的元素列表。
  2. find()方法 返回值:返回符合条件的第一个元素。
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对象:

  1. tag.text 表示其中的文本内容
  2. tag.get(attr) 获取attr属性
  3. tag.name 表示标签的名字

其他find_*

find_parents()  返回所有祖先节点
find_parent()  返回直接父节点

find_next_siblings()  返回后面所有兄弟节点
find_next_sibling()	返回后面第一个兄弟节点

find_all_next()  返回节点后所有符合条件的节点
find_next() 返回第一个符合条件的节点

find_all_previous() 返回节点后所有符合条件的节点
find_previous() 返回第一个符合条件的节点

select()方法

select()主要是以CSS选择器(selector)的方式寻找元素。如果找到元素,则返回元素列表;否则返回空列表。

  1. 标签选择器:直接指定标签名
    1. Soup.select(‘p’):寻找所有

      标签的元素

    2. Soup.select(‘img’):寻找所有标签的元素
    3. Soup.select(‘input[name]’):寻找所有标签且包含name属性的元素
  2. 类(class)选择器:.
    1. Soup.select('.name):寻找所有class属性为name的元素
  3. 选择器:#
    1. Soup.select('#name):寻找所有id属性为name的元素
  4. 层级选择器:
    1. 单层:>
      Soup.select(‘div.list > ul > li > a’):查找所有class为list的div中的ul里的li标签里面的a标签
    2. 多层:空格
      Soup.select(‘p #name’):寻找所有

      标签且id为name的元素
      Soup.select(‘p .name’):寻找所有class为name的

      标签

小结

大部分情况下,find和find_all方法就可以解决问题

selenium中文文档----------文档

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()

浏览器驱动

  1. Chrome Chrome找到对应版本的下载即可。
  2. Edge Edge找到对应版本下载即可。

可以将浏览器驱动放到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>
  1. id:标签ID定位
find_element_by_id("submit")
  1. name:标签name属性
find_element_by_name("submitBtn")
  1. class name:标签类名字定位
find_element_by_class_name("text-center")
  1. tag name:标签名字定位
find_element_by_tag_name("button")
  1. link text:链接文本定位
find_element_by_link_text("提交")
  1. partial link text:部分链接文本定位
find_element_by_link_text("交")
  1. xpath:xpath定位
find_element_by_xpath("//div[@id='csdn-toolbar']/div/div/div[2]/div/div/input[1]")
  1. css selector:css选择器定位
方法 例子 描述
.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参数。

爬虫解析模块(bs4,selenium)_第1张图片

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()

iframe

  1. 对于内嵌的页面 selenium 是无法直接定位的,需要使用 switch_to.frame() 方法将当前操作的对象切换成 frame/iframe 内嵌的页面。

  2. 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')

cookies

  1. edge.get_cookies:以字典的形式返回当前会话中可见的 cookie 信息。
  2. edge.get_cookie(name):返回 cookie 字典中 key == name 的 cookie 信息。
  3. edge.add_cookie(cookie_dict):将 cookie 添加到当前会话中
  4. edge.delete_cookie(name):删除指定名称的单个 cookie。
  5. edge.delete_all_cookies():删除会话范围内的所有 cookie。

关闭操作

  1. quit()关闭所有页面并退出驱动
  2. close()关闭当前页面

下拉框选择操作

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)的博客,对爬虫学习做一个总结。

你可能感兴趣的:(爬虫,爬虫,selenium,python)