0


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

摘要:

在写爬虫的时候,为了效率我们通常会选择解析网页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:强烈推荐!内含最详细的介绍[安装,基本使用]

设置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

除非

appMode

devtools

选项

True

executablePathstr运行 Chromium 或 Chrome 可执行文件的路径,而不是默认捆绑的 ChromiumslowMoint或float按指定的毫秒数减慢 pyppeteer 操作。argsList [str]传递给浏览器进程的附加参数(标志)。dumpiobool是否管道浏览器进程 stdout 和 stderr 进入

process.stdout

process.stderr

。默认为 False。userDataDirstr用户数据目录的路径envdict指定浏览器可见的环境变量。默认与 python 进程相同。devtoolsbool为每个选项卡自动打开 DevTools 面板。如果是此选项

True

headless

则将设置该选项

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())
标签: 爬虫

本文转载自: https://blog.csdn.net/sj13036141506/article/details/128728632
版权归原作者 神秘的doge 所有, 如有侵权,请联系我们删除。

“【爬虫】最全!selenium和pyppeteer看这一篇文章就够”的评论:

还没有评论