【爬虫】最全!selenium和pyppeteer看这一篇文章就够

目录

摘要:

思路:

区别:

一、selenium 简介

1.1、元素定位

1.2、属性选择器

1.3、定位方式选择

二、Pyppeteer简介

2.1、安装模块

2.2、等待机制和浏览器实例

2.3、常用的页面操作

执行js

元素操作

鼠标事件

键盘事件

内嵌框架

2.4、使用思路和案例

三、BeautifulSoup简介

3.1、安装模块

3.2、解析器

3.3、Beautiful Soup的使用

3.4、查找元素

1、遍历文档树

2、搜索文档树


摘要:

在写爬虫的时候,为了效率我们通常会选择解析网页api来获取数据,但是有时候解析方式比较困难(很多网站会对请求数据和返回数据加密),或者我们纯粹是为了快速实现爬虫,可使用浏览器自动化操作——selenium或pyppeteer。

思路:

对于爬取网站,一般有两种思路:

  1. 分析 Ajax 请求,通过模拟请求requests得到真实的数据,该情况受限于网站加密(可通过分析js加密解密函数来破解)
  2. 使用 selenium(或pyppeteer) 模拟浏览器进行动态渲染,从而获取网站返回的html内容,再通过Beautiful Soup4解析获得想要的数据。以下我们将详细讲解这种方法

区别:

selenium和pyppeteer都是模拟浏览器进行渲染,它们的区别如下:

  1. 环境配置:selenium使用起来是不太方便的,要安装浏览器、下载对应的驱动,而且各个工具的版本还要匹配,大规模部署时就比较麻烦;pyppeteer提供自动化下载chromium浏览器(支持浏览器比较单一),省去了 driver 配置的环节
  2. 语法结构:pyppeteer基于异步编程思想(使用asyncio构建),所以在使用的时候需要用到 async/await 结构。selenium是同步编程,则没有这些要求。
  3. 性能方面:pyppeteer基于协程,性能上会比selenium更高。

一、selenium 简介

selenium 就是一个用于 Web 应用程序的测试工具
根据官方文档所说,selenium 最大的优点就是它可以直接运行在浏览器上,模拟用户的真实行为
但同时这也是它最大的缺点,由于需要模拟真实的渲染过程,所以导致它的运行速度变慢

无论是selenium还是pyppeteer,都可以结合beautifulsoup使用。只需获取html源代码后,丢入beautifulsoup解析即可

1.1、元素定位

使用的时候需要导入By模块

 from selenium.webdriver.common.by import By

定位元素find_element_by_*find_element()
通过元素id定位find_element_by_id(x)find_element(By.ID,x)
通过元素name定位find_element_by_name(x)find_element(By.NAME,x)
通过xpath表达式定位find_element_by_xpath(x)find_element(By.XPATH,x)
通过完整超链接定位find_element_by_link_text(x)find_element(By.LINK_TEXT,x)
通过部分链接定位find_element_by_partial_link_text(x)find_element(By.PARTIAL_LINK_TEXT,x)
通过标签定位find_element_by_tag_name(x)find_element(By.TAG_NAME,x)
通过类名进行定位find_element_by_class_name(x)find_element(By.CLASS_NAME,x)
通过css选择器进行定位find_element_by_css_selector(x)find_element(By.CSS_SELECTOR,x)

定位多个元素,就是把上述element后面多了复数标识s,变为elements,其他操作一致。

以上的操作可以等同于以下:

find_element_by_xxfind_elements_by_xx
没有匹配到元素执行报错返回空列表
匹配到一个元素返回元素返回包含一个元素的列表
匹配到多个元素返回第一个元素返回包含所有匹配元素列表
from selenium.webdriver.common.by import By
element = web.find_element(By.ID,'kw')
element = web.find_element(By.NAME,'wd')
element = web.find_element(By.CLASS_NAME,'s_ipt')
element = web.find_element(By.TAG_NAME,'input')
element = web.find_element(By.LINK_TEXT,'新闻')
element = web.find_element(By.PARTIAL_LINK_TEXT,'闻')
element = web.find_element(By.XPATH,'//*[@id="kw"]')
element = web.find_element(By.CSS_SELECTOR,'#kw')
element = web.find_element(By.CSS_SELECTOR,'[id="kw"]')
element = web.find_element(By.CSS_SELECTOR,'input[id="kw"]')

目前,由于selenium版本升级,使用find_element_by_*,会提示弃用警告,建议使用find_element()。

DeprecationWarning: find_element_by_* commands are deprecated. Please use find_element()

1.2、属性选择器

根据标签中的属性来定位元素, 格式: [属性名=”属性值”],或标签名[属性名=属性值]。如果属性是唯一的,那么标签名可以不用写。如下:

<input type="text" class="s_ipt" name="wd" id="kw" maxlength="100" autocomplete="off">

元素定位如下:

element = web.find_element_by_css_selector('[id="kw"]') 
element = web.find_element_by_css_selector('input[id="kw"]') 
#定位复合class属性
element = web.find_element_by_css_selector('[class="s-top-left-new s-isindex-wrap"]')

1.3、定位方式选择

  • 当页面元素有id属性时,尽量用id来定位;
  • 当有链接需要定位时,可以考虑link text或partial link text方式;
  • 当要定位一组元素相同元素时,可以考虑用tag name或class name;
  • css selector定位速度比较快,效率高。
  • 一般id>name>css>XPath

安装和基础语法参考:Selenium:强烈推荐!内含最详细的介绍[安装,基本使用]

鼠标和键盘操作参考:Selenium中鼠标、键盘等操作

设置js加载等待时间参考:Selenium的三种等待,强制等待、隐式等待、显式等待

二、Pyppeteer简介

pyppeteer是puppeteer的Python版本,而puppeteer是什么呢?puppeteer是Google基于Node.js开发的一个工具,它可以使我们通过JavaScript来控制Chrome浏览器执行一些操作,拥有丰富的API,功能非常强大,因此也可以用于网络爬虫。pyppeteer是一位日本的程序员根据Puppeteer开发的非官方Python版本。

2.1、安装模块

pip install pyppeteer

# 使用时导入
import pyppeteer

2.2、等待机制和浏览器实例

page.waitForXPath等待 xPath 对应的元素出现,返回对应的 ElementHandle 实例

page.waitForSelector :等待选择器对应的元素出现,返回对应的 ElementHandle 实例

启动器

  • pyppeteer.launcher.launch()
    启动 Chrome 进程并返回浏览器实例

参数:

参数类型解释
ignoreHTTPSErrorsbool是否忽略 HTTPS 错误。默认为 False
ignoreDefaultArgsList [str]不要使用 pyppeteer 的默认参数。这是危险的选择;小心使用
headlessbool无头模式下运行浏览器。默认为 True 除非 appModedevtools 选项 True
executablePathstr运行 Chromium 或 Chrome 可执行文件的路径,而不是默认捆绑的 Chromium
slowMoint或float按指定的毫秒数减慢 pyppeteer 操作。
argsList [str]传递给浏览器进程的附加参数(标志)。
dumpiobool是否管道浏览器进程 stdout 和 stderr 进入 process.stdoutprocess.stderr。默认为 False。
userDataDirstr用户数据目录的路径
envdict指定浏览器可见的环境变量。默认与 python 进程相同。
devtoolsbool为每个选项卡自动打开 DevTools 面板。如果是此选项 Trueheadless 则将设置该选项 False
logLevelint或str用于打印日志的日志级别。默认值与根记录器相同。
autoClosebool脚本完成时自动关闭浏览器进程。默认为 True
loopasyncio.AbstractEventLoop事件循环(实验)。

移除Chrome正受到自动测试软件的控制,可直接绕过浏览器window.navigator.webdriver检测

# 添加ignoreDefaultArgs=["--enable-automation"] 参数
from pyppeteer import launch
browser = await launch(headless=False, ignoreDefaultArgs=["--enable-automation"])

浏览器的console运行如下代码,同正常打开浏览器一样都为undefined,如果不设置就为true

2.3、常用的页面操作

执行js

page.evaluate ( pageFunction [, …args] ) ,返回 pageFunction 执行的结果,pageFunction 表示要在页面执行的函数或表达式, args 表示传入给 pageFunction 的参数

课外内容

scrollTo和scrollBy这两个JS API也是用来控制元素或者窗体的滚动距离的。

scrollTo()表示滚到到指定的位置,而scrollBy()表示相对当前的位置滚动多少距离。

scrollTo和scrollBy两个JS API的优点有两个:

  1. 调用统一
    scrollLeft/scrollTop这两个属性只能作为元素上,在window对象上没有效果。而pageXOffset/pageYOffset只能作用于window对象上,在元素上没有效果。而scrollTo和scrollBy不仅可以作用于window对象上,还可以作用于元素上。实现的调用的统一。
  2. 平滑支持
    scrollLeft/scrollTop和pageXOffset/pageYOffset控制滚动定位,想要定位平滑,只能借助于CSS scroll-behavior属性,JS这块设置无力。但是scrollTo和scrollBy在比较方便,直接有API参数支持。

代码

scroll_top = 100

await page.evaluate(f'document.getElementsByClassName("mp-layout-content-container")[0].scrollBy(0, {scroll_top})')

元素操作

ElementHandle 表示页内的DOM元素,你可以通过 page.querySelector() 方法创建。DOM 元素具有和 page 相同的某些方法:J()、JJ()、Jeval()、JJeval()、screenshot()、type()、click()、tap()。此外,还有一些好用的方法:

(1) 获取元素边界框坐标:boundingBox(),返回元素的边界框(相对于主框架)=> x 坐标、 y 坐标、width、height

(2) 元素是否可见:isIntersectingViewport()

(3) 上传文件:uploadFile(*filpaths)

(4) ElementHandle 类 转 Frame类:contentFrame(),如果句柄未引用iframe,则返回None。

(5) 聚焦该元素:focus()

(6) 与鼠标相关:hover () ,将鼠标悬停到元素上面

(7) 与键盘相关:press (key[, options]),按键,key 表示按键的名称,option可配置:

    text (string) - 如果指定,则使用此文本生成输入事件

    delay (number) - keydown 和 keyup 之间等待的时间。默认是 0

鼠标事件

Mouse 类在相对于视口左上角的主框架 CSS 像素中运行。

(1) page.mouse.down([options]) 按下鼠标,options 可配置:

    button(str) 按下了哪个键,可选值为 [ left, right, middle ], 默认是 left, 表示鼠标左键

    clickCount(int) 按下的次数,单击,双击或者其他次数

(2) page.mouse.up([options]) 松开鼠标,options 同上

(3) page.mouse.move(x, y, [options]) 移动鼠标到指定位置,options.steps 表示移动的步长

(4) page.mouse.click(x, y, [options]) 鼠标点击指定的位置,其实是 mouse.move 和 mouse.down 或 mouse.up 的快捷操作

键盘事件

Keyboard 提供一个接口来管理虚拟键盘. 高级接口为 keyboard.type, 其接收原始字符, 然后在你的页面上生成对应的 keydown, keypress/input, 和 keyup 事件。

为了更精细的控制(虚拟键盘), 你可以使用 keyboard.down, keyboard.up 和 keyboard.sendCharacter 来手动触发事件, 就好像这些事件是由真实的键盘生成的。

键盘的几个API如下:

    keyboard.down(key[, options]) 触发 keydown 事件
    keyboard.press(key[, options]) 按下某个键,key 表示键的名称,比如‘ArrowLeft’ 向左键;
    keyboard.sendCharacter(char) 输入一个字符
    keyboard.type(text, options) 输入一个字符串
    keyboard.up(key) 触发 keyup 事件

详细的键名映射可以看源码
Lib\site-packages\pyppeteer\us_keyboard_layout.py

内嵌框架

可以通过 Page.frames、ElementHandle.contentFrame 方法获取,同时具有和 page一样的多个方法;

**其它:

    childFrames 获取子框架,返回列表
    parentFrame 返回父框架
    content() 返回框架的 html 内容
    url 获取 url
    name 获取 name
    title() 获取 title

更多内容可参考:Pyppeteer库之四:Pyppeteer的页面操作

2.4、使用思路和案例

无论是使用Selenium还是Pyppeteer原理都是模拟浏览器进行加载js渲染页面,所以我们最后要拿到经过渲染后的网页源代码,再结合Beautiful Soup进行html标签元素解析提取

在Pyppeteer中,它操作的是一个类似Chrome的Chromium浏览器,Chromium是相当于Chrome的开发版,是完全开源的,Chrome的所有新功能都会先在Chromium上实现,稳定后才会移植到Chrome上,因此Chromium会包含很多新功能。Pyppeteer就是依赖于Chromium来运行的,当我们第一次运行Pyppeteer的时候,如果Chromium没有安装,那么程序会自动帮我们安装和配置,省去了环境配置这一步。

下面我们详细了解一下Pyppeteer的使用思路。

  1. aunch 方法新建一个Browser对象,赋值给browser变量,这一步就相当于启动了浏览器
  2. 然后browser调用newPage方法相当于新建一个选项卡,并且返回一个Page对象,这一步还是一个空白的页面,并未访问任何页面
  3. 然后Page调用goto方法,就相当于访问此页面
  4. Page对象调用waitForXpath方法,那么页面就会等待选择器所对应的节点信息加载出来,如果加载出来就立即返回,否则就会持续等待直到超时。这里就比selenium的等待元素加载完毕要清晰的多了。
  5. 页面加载完成后再调用content方法,获取渲染出来的页面源代码
  6. 通过BeautifulSoup解析源代码,提取需要的数据

例子:

# -*- coding: utf-8 -*-
"""
@Time : 2023/1/5 11:22 AM
@File :web_to_excel.py
"""
import datetime
import os.path
import time

import requests
from bs4 import BeautifulSoup
import xlrd
from xlutils.copy import copy
import asyncio
from pyppeteer import launch
import lxml


async def main(cookies_str):
    html_source = await collect_data(cookies_str)
    table_data = await parse_html(html_source)
    await write_excel_data(table_data)




async def collect_data(cookies_str):
    cookies = []
    for i in cookies_str.split(';'):
        print(i)
        tmp = i.split('=', 1)
        cookie = {"name": tmp[0].strip(), "value": tmp[1].strip()}
        cookies.append(cookie)

    conf_dict = {
        'autoClose': True,
        'headless': False,
        'dumpio': True,
        'ignoreDefaultArgs': ["--enable-automation"] # 移除Chrome正受到自动测试软件的控制
    }
    browser = await launch(conf_dict)
    page = (await browser.pages())[0]

    # 是否启用JS,enabled设为False,则无渲染效果
    await page.goto('需要爬取的网站')

    # print('current cookies', page.cookies())
    # 刷新网页
    await page.setCookie(*cookies)
    await page.reload()
    # await asyncio.sleep(10)
    await page.waitForSelector('.mp-table')  # 等待节点出现
    html_source = await page.content()
    # print(type(html_source), html_source)
    return html_source


async def parse_html(html_source):
    soup = BeautifulSoup(html_source, 'lxml')
    div_table_html = soup.find_all('div', attrs={'class': 'slate-card'})[1]  # 找到第二个表格
    title_tr = div_table_html.find('tr', attrs={'class': 'mp-table-row-sticky'})  # 标题的标签
    title_td = title_tr.find_all('td')
    title_text = []
    for t_td in title_td:
        cell_span = t_td.find_all('span', attrs={'data-slate-string': 'true'})  # 标题的文本
        cell_str = '\n'.join([c_span.text for c_span in cell_span])
        title_text.append(cell_str)
    print(111, len(title_text), title_text)  # 标题

    content_text = []  # 内容:是个二维列表
    for other_tr in title_tr.next_siblings:
        other_td = other_tr.find_all('td')
        row_text = []
        for o_td in other_td:
            cell_span = o_td.find_all('span', attrs={'data-slate-string': 'true'})  # 内容的文本
            cell_str = '\n'.join([c_span.text for c_span in cell_span])
            row_text.append(cell_str)
        print(112, len(row_text), row_text)  # 内容
        content_text.append(row_text)

    # content_span = title_tr.next_siblings.find_all('span', attrs={'data-slate-string': 'true'}) #除标题外的其他行
    # content_text = [span.text for span in content_span]
    content_text.insert(0, title_text)
    print(222, content_text)
    return content_text


async def write_excel_data(data, save_path="/Users/Desktop/变更操作单"):
    # data: 二维数组,表示插入excel的数据
    # save_path: 工作簿的路径
    # formatting_info=True:保留Excel的原格式
    workbook = xlrd.open_workbook('/Users/Desktop/变更操作单/变更操作单模板.xls', formatting_info=True)

    new_workbook = copy(workbook)  # 将xlrd对象拷贝转化为xlwt对象

    print(workbook.sheets())

    # 写入表格信息
    # 第一次建立工作簿时候调用
    write_sheet = new_workbook.get_sheet(0)

    index = len(data)  # 获取需要写入数据的行数
    # workbook = xlwt.Workbook()  # 新建一个工作簿
    for i in range(0, index):
        for j in range(0, len(data[i])):
            write_sheet.write(i, j, data[i][j])  # 像表格中写入数据(对应的行和列)

    # now_date_str = time.strftime("%Y%m%d", time.localtime())
    now_date_str = '20230129'
    save_path = os.path.join(save_path, "{}变更操作单.xls".format(now_date_str))
    new_workbook.save(save_path)  # 保存工作簿


if __name__ == '__main__':
    # 需手动更新cookie
    cookies_str = '页面中获取'

    asyncio.get_event_loop().run_until_complete(main(cookies_str))

要实现用户自动登录,可获取浏览器cookie

 参考:获取cookies(pyppeteer)

还有另一种方法实现自动登录验证,即启动浏览器时,传入userDataDir参数即可:

conf_dict = {
    'userDataDir': "存放浏览记录的文件夹", #完成第一次手动输入验证,后续就不用再验证了
    'autoClose': False,
    'headless': False,
    'dumpio': True,
    'ignoreDefaultArgs': ["--enable-automation"]
}
browser = await launch(conf_dict)

三、BeautifulSoup简介

Beautiful Soup 是一个可以从HTML或XML文件中提取数据的Python库。它能够通过转换器实现惯用的文档导航、查找、修改文档的方式。Beautiful Soup 3 目前已经停止开发,官网推荐在现在的项目中使用Beautiful Soup 4, 移植到BS4。

3.1、安装模块

# 安装 Beautiful Soup
pip install beautifulsoup4

# 安装解析器
pip install lxml

3.2、解析器

下表列出了主要的解析器,以及它们的优缺点,官网推荐使用lxml作为解析器,因为效率更高。 在Python2.7.3之前的版本和Python3中3.2.2之前的版本,必须安装lxml或html5lib, 因为那些Python版本的标准库中内置的HTML解析方法不够稳定。

解析器使用方法优势劣势
Python标准库BeautifulSoup(markup, "html.parser")
  • Python的内置标准库
  • 执行速度适中
  • 文档容错能力强
  • Python 2.7.3 or 3.2.2)前 的版本中文档容错能力差
lxml HTML 解析器BeautifulSoup(markup, "lxml")
  • 速度快
  • 文档容错能力强
  • 需要安装C语言库
lxml XML 解析器

BeautifulSoup(markup, ["lxml", "xml"])

BeautifulSoup(markup, "xml")

  • 速度快
  • 唯一支持XML的解析器
  • 需要安装C语言库
html5libBeautifulSoup(markup, "html5lib")
  • 最好的容错性
  • 以浏览器的方式解析文档
  • 生成HTML5格式的文档
  • 速度慢
  • 不依赖外部扩展

3.3、Beautiful Soup的使用

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's story</b></p>
<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>
<p class="story">...</p>
"""
# 容错处理,文档的容错能力指的是在html代码不完整的情况下,使用该模块可以识别该错误。
# 使用BeautifulSoup解析上述代码,能够得到一个 BeautifulSoup 的对象,并能按照标准的缩进格式的结构输出
from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'lxml') #具有容错功能
res=soup.prettify() #处理好缩进,结构化显
print(res)

3.4、查找元素

1、遍历文档树

# 遍历文档树:即直接通过标签名字选择,特点是选择速度快,但如果存在多个相同的标签则只返回第一个
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p id="my p" class="title"><b id="bbb" class="boldest">The Dormouse's story</b></p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

from bs4 import BeautifulSoup
# 获取BeautifulSoup对象
soup=BeautifulSoup(html_doc,'lxml')

print(soup.p)     # 存在多个相同的标签则只返回第一个
print(soup.a)     # 存在多个相同的标签则只返回第一个

# 1. 获取标签的名称
print(soup.p.name)

# 2. 获取标签的属性
print(soup.p.attrs)

# 3. 获取标签的内容
print(soup.p.string)     # p下的文本只有一个时,取到,否则为None
print(soup.p.strings)      # 拿到一个生成器对象, 取到p下所有的文本内容,可以转换为list
print(soup.p.text)       # 取到p下所有的文本内容
for line in soup.stripped_strings:    # 去掉空白
    print(line)

# 4. 嵌套选择
print(soup.head.title.string)
print(soup.body.a.string)


# 5. 子节点、子孙节点
print(soup.p.contents)      # p下所有子节点
print(soup.p.children)      # 得到一个迭代器,包含p下所有子节点
for i,child in enumerate(soup.p.children):
    print(i,child)

print(soup.p.descendants) 	# 获取子孙节点,p下所有的标签都会选择出来
for i,child in enumerate(soup.p.descendants):
    print(i,child)

# 6. 父节点、祖先节点
print(soup.a.parent)  		# 获取a标签的父节点
print(soup.a.parents) 	    # 找到a标签所有的祖先节点,父亲的父亲,父亲的父亲的父亲...


# 7. 兄弟节点
print(soup.a.next_sibling) 		# 下一个兄弟
print(soup.a.previous_sibling) 		# 上一个兄弟

print(list(soup.a.next_siblings)) 		# 下面的兄弟们=>生成器对象
print(soup.a.previous_siblings) 		# 上面的兄弟们=>生成器对象

2、搜索文档树

(1)五种过滤器

字符串正则表达式列表True方法

# 过滤器结合find() 和 find_all()方法使用查找元素
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p id="my p" class="title"><b id="bbb" class="boldest">The Dormouse's story</b>
</p>

<p class="story">Once upon a time there were three little sisters; and their names were
<a href="http://example.com/elsie" class="sister" id="link1">Elsie</a>,
<a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
<a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
and they lived at the bottom of a well.</p>

<p class="story">...</p>
"""

from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'lxml')

# 1.字符串
print(soup.find_all('b'))

# 2.、正则表达式
# 利用re.compile()使用正则
import re
print(soup.find_all(re.compile('^b')))   # 找出b开头的标签,结果有body和b标签

# 3.列表:
# 如果传入列表参数,Beautiful Soup会将与列表中任一元素匹配的内容返回.下面代码找到文档中所有<a>标签和<b>标签:
print(soup.find_all(['a','b']))

# 4.True
# 可以匹配任何值,下面代码查找到所有的tag,但是不会返回字符串节点
print(soup.find_all(True))
for tag in soup.find_all(True):
    print(tag.name)

# 5.方法
# 如果没有合适过滤器,那么还可以定义一个方法,方法只接受一个元素参数 ,如果这个方法返回 True 表示当前元素匹配并且被找到,如果不是则反回 False
def has_class_but_no_id(tag):
    return tag.has_attr('class') and not tag.has_attr('id')

print(soup.find_all(has_class_but_no_id))

# 匿名函数
print(soup.find_all(lambda tag: True if tag.has_attr("class") and tag.has_attr("id") else False))

(2)find_all( name , attrs , recursive , text , **kwargs )

# 1、name: 搜索name参数的值可以使任一类型的 过滤器 ,字符窜,正则表达式,列表,方法或是 True .
print(soup.find_all(name=re.compile('^t')))

# 2、keyword: key=value的形式,value可以是过滤器:字符串 , 正则表达式 , 列表, True .
print(soup.find_all(id=re.compile('my')))
print(soup.find_all(href=re.compile('lacie'),id=re.compile('d'))) #注意类要用class_
print(soup.find_all(id=True))    # 查找有id属性的标签

# 有些tag属性在搜索不能使用,比如HTML5中的 data-* 属性:
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>','lxml')
# data_soup.find_all(data-foo="value") #报错:SyntaxError: keyword can't be an expression
# 但是可以通过 find_all() 方法的 attrs 参数定义一个字典参数来搜索包含特殊属性的tag:
print(data_soup.find_all(attrs={"data-foo": "value"}))
# [<div data-foo="value">foo!</div>]

# 3、按照类名查找,注意关键字是class_,class_=value,value可以是五种选择器之一
print(soup.find_all('a',class_='sister')) #查找类为sister的a标签
print(soup.find_all('a',class_='sister ssss')) #查找类为sister和sss的a标签,顺序错误也匹配不成功
print(soup.find_all(class_=re.compile('^sis'))) #查找类为sister的所有标签

# 4、attrs
print(soup.find_all('p',attrs={'class':'story'}))

# 5、text: 值可以是:字符,列表,True,正则
print(soup.find_all(text='Elsie'))
print(soup.find_all('a',text='Elsie'))

# 6、limit参数:如果文档树很大那么搜索会很慢.如果我们不需要全部结果,可以使用 limit 参数限制返回结果的数量.效果与SQL中的limit关键字类似,当搜索到的结果数量达到 limit 的限制时,就停止搜索返回结果
print(soup.find_all('a',limit=2))

# 7、recursive:调用tag的 find_all() 方法时,Beautiful Soup会检索当前tag的所有子孙节点,如果只想搜索tag的直接子节点,可以使用参数 recursive=False
print(soup.html.find_all('a'))
print(soup.html.find_all('a',recursive=False))

(3)find( name , attrs , recursive , text , **kwargs )

唯一的区别是 find_all() 方法的返回结果是值包含一个元素的列表,而 find() 方法直接返回结果.
find_all() 方法没有找到目标是返回空列表, find() 方法找不到目标时,返回 None .
print(soup.find("nosuchtag"))
# None

soup.head.title 是 tag的名字 方法的简写.这个简写的原理就是多次调用当前tag的 find() 方法

(4)CSS选择器(select('.class'))

#该模块提供了select方法来支持css,详见官网:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#id37
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title">
    <b>The Dormouse's story</b>
    Once upon a time there were three little sisters; and their names were
    <a href="http://example.com/elsie" class="sister" id="link1">
        <span>Elsie</span>
    </a>
    <a href="http://example.com/lacie" class="sister" id="link2">Lacie</a> and
    <a href="http://example.com/tillie" class="sister" id="link3">Tillie</a>;
    <div class='panel-1'>
        <ul class='list' id='list-1'>
            <li class='element'>Foo</li>
            <li class='element'>Bar</li>
            <li class='element'>Jay</li>
        </ul>
        <ul class='list list-small' id='list-2'>
            <li class='element'><h1 class='yyyy'>Foo</h1></li>
            <li class='element xxx'>Bar</li>
            <li class='element'>Jay</li>
        </ul>
    </div>
    and they lived at the bottom of a well.
</p>
<p class="story">...</p>
"""
from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'lxml')

# 1. CSS选择器
# select 返回的是一个列表
print(soup.p.select('.sister'))
print(soup.select('.sister span'))
print(soup.select('#link1'))
print(soup.select('#link1 span'))
print(soup.select('#list-2 .element.xxx'))

print(soup.select('#list-2')[0].select('.element'))  # 可以一直select,但其实没必要,一条select就可以了

# 2. 获取属性
print(soup.select('#list-2 h1')[0].attrs)

# 3. 获取内容
print(soup.select('#list-2 h1')[0].get_text())

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值