Python爬虫实战(5) | 爬取知网文献信息

简介: Python爬虫实战(5) | 爬取知网文献信息

最近有小伙伴后台跟我说,临近毕业,写毕业论文需要上知网查找大量的文献

但是一篇一篇看文献信息以及文献摘要又有点麻烦,能不能让我写一个爬虫去批量获取文献相关信息

我一听好家伙,这位小伙伴是懂偷懒的,想起当初我写毕业论文的时候也是饱经查阅文献的折磨,深知那种滋味

但是知网作为国内知名的文献数据库之一,有着极其复杂的反爬虫机制,例如动态JS、iframe、验证码等等,不是说想爬就能爬的

像我之前采用 requests 模拟请求的方法来爬取的话难度很大,一个不小心就有可能被封 IP

本篇文章就主要介绍该如何使用 Selenium来巧爬知网

初识 selenium

selenium 是一个自动化测试工具,可以用来进行 web 自动化测试

selenium 本质是通过驱动浏览器,完全模拟浏览器的操作,比如跳转、输入、点击、下拉等,来拿到网页渲染之后的结果,可支持多种浏览器

爬虫中用到 selenium 主要是为了解决 requests 无法直接执行 JavaScript 代码等问题

下面就来介绍下 selenium 基础用法

声明浏览器对象

Selenium 支持非常多的浏览器,如Chrome、Firefox、Edge 等

我们只要首先下载好相应浏览器的驱动(webdriver)到Python主目录中,或者加入环境变量即可

#Firefox浏览器驱动:
https://link.zhihu.com/?target=https%3A//github.com/mozilla/geckodriver/releases

#Chrome浏览器驱动:
https://registry.npmmirror.com/binary.html?path=chromedriver/

#IE浏览器驱动:IEDriverServer
https://link.zhihu.com/?target=http%3A//selenium-release.storage.googleapis.com/index.html

#Edge浏览器驱动:MicrosoftWebDriver
https://link.zhihu.com/?target=https%3A//developer.microsoft.com/en-us/microsoft-edge/tools/webdriver

下载好驱动之后就可以浏览器初始化了

from selenium import webdriver

browser = webdriver.Chrome()
browser = webdriver.Firefox()
browser = webdriver.Edge()
browser = webdriver.Safari()

访问页面

我们可以用 get() 方法来请求一个网页,传入参数链接URL

browser.get('https://blog.csdn.net/s_alted')

查找元素

#根据 id 查找
find_element_by_id()

#根据 name 查找
find_element_by_name()

#根据 class name 查找
find_element_by_class_name()

#根据 Tag name 查找
find_element_by_tag_name()

#根据 完整超链接 查找
find_element_by_link_text()

#根据 部分超链接 查找
find_element_by_partial_link_text()

#根据 xpath 查找
find_element_by_xpath()

#根据 css选择器 查找
find_element_by_css_selector()

PS:

上面的 element 变成 elements(例如find_elements_by_id)就是找到所有满足的条件,然后返回数据

等待页面加载完成

有显式等待和隐式等待

显式等待使 WebdDriver 等待某个条件成立时继续执行,否则在达到最大时长时抛出超时异常(TimeoutException)

WebDriverWait 类是由 WebDirver 提供的等待方法。在设置时间内,默认每隔一段时间检测一次当前页面元素是否存在,如果超过设置时间检测不到则抛出异常

常用浏览器操作

在找到浏览器相应元素的位置之后,我们就需要进行一些交互动作,例如双击、点击、输入、获取网页源码等等

element = find_element_by_id(ID,id)

element.send_keys(Keys.CONTROL,'c')   # 复制

element.send_keys("hello") #传入 hello

element.clear()  # 清除输入框

element.click()  # 单击元素

element.text  # 获取元素文本信息

element.get_attribute('href')  # 获取元素属性

有了上面这些基本用法,就可以开始编写代码程序了!

网页分析

知网官网:https://www.cnki.net/

按照这位同学的需求,需要进入到知网官网之后——>点击高级搜索图标
image-20230116141805053.png
然后在文献来源输入框处输入相关内容,然后点击检索图标
image-20230116141850001.png
image-20230116141948278.png
因此可以得到如下步骤:

进入官网点击高级搜索——>在文献来源处输入信息——>点击检索

image-20230116142659490.png
image-20230116142733460.png

通过 F12 检查浏览器页面,得到高级搜索图标和输入框以及检索图标元素的 xpath 分别如下:
image-20230116143015133.png

#高级搜索 xpath
/html/body/div[2]/div[2]/div/div[2]/a[1]

#输入框 xpath
/html/body/div[2]/div/div[2]/div/div[1]/div[1]/div[2]/dl/dd[3]/div[2]/input

#检索 xpath
/html/body/div[2]/div/div[2]/div/div[1]/div[1]/div[2]/div[2]/input

接着我们发现结果页(以搜索管理世界为例)
image-20230116143423957.png
共找到 8550 条结果,300页,每一页包含 20 条文献条目

每个条目包含题目、作者、来源等信息

通过对当前页面分析,发现每条文献条目的 xpath 是有规律的

#题名
/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[1]/td[2]

#作者
/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[1]/td[3]

tr[1] 表示本页第一条条目,而 td[2] 中的2-6 分别代表作者、来源、发表时间和数据库

我们在当前页面是无法获取到文献的摘要、关键字等信息,需要进一步点击进入相关文献条目

进入到相关文献页面之后,根据 class name来获取摘要、关键字、是否为CSSCI 这些元素

image-20230116144215540.png
image-20230116144241123.png
完成以上知网页面的分析后,我们就可以根据需求开始写代码了!

代码实现

导入所需包

import time 
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities

创建浏览器对象

这里我用的是 Edge 浏览器

# get直接返回,不再等待界面加载完成
desired_capabilities = DesiredCapabilities.EDGE
desired_capabilities["pageLoadStrategy"] = "none"

# 设置 Edge 驱动器的环境
options = webdriver.EdgeOptions()
# 设置 Edge 不加载图片,提高速度
options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})

# 创建一个 Edge 驱动器
driver = webdriver.Edge(options=options)

传入 url 参数然后模拟对浏览器进行人为操作

适当的加入 time.sleep() 方法,等待页面加载完成

不然页面还没完全加载就执行下一步操作的话会报错

# 打开页面
driver.get("https://kns.cnki.net/kns8/AdvSearch")
time.sleep(2)
    
# 传入关键字
WebDriverWait(driver, 100).until(
        EC.presence_of_element_located((By.XPATH, '''//*[@id="gradetxt"]/dd[3]/div[2]/input'''))).send_keys(theme)
time.sleep(2)
    
# 点击搜索
WebDriverWait(driver, 100).until(
        EC.presence_of_element_located((By.XPATH, "/html/body/div[2]/div/div[2]/div/div[1]/div[1]/div[2]/div[2]/input"))).click()
time.sleep(3)

 # 点击切换中文文献
WebDriverWait(driver, 100).until(
        EC.presence_of_element_located((By.XPATH, "/html/body/div[3]/div[1]/div/div/div/a[1]"))).click()
time.sleep(3)

获取总文献数和页数

res_unm = WebDriverWait(driver, 100).until(EC.presence_of_element_located(
        (By.XPATH, "/html/body/div[3]/div[2]/div[2]/div[2]/form/div/div[1]/div[1]/span[1]/em"))).text

# 去除千分位的逗号
res_unm = int(res_unm.replace(",", '')) 
page_unm = int(res_unm / 20) + 1
print(f"共找到 {res_unm} 条结果, {page_unm} 页。")

对结果页进行解析

def crawl(driver, papers_need, theme):
    # 赋值序号, 控制爬取的文章数量
    count = 1

    # 当爬取数量小于需求时,循环网页页码
    while count <= papers_need:
        # 等待加载完全,休眠3S
        time.sleep(3)

        title_list = WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.CLASS_NAME, "fz14")))
        # 循环网页一页中的条目
        for i in range(len(title_list)):
            try:
                if count % 20 != 0:
                    term = count % 20  # 本页的第几个条目
                else:
                    term = 20
                title_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[2]"
                author_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[3]"
                source_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[4]"
                date_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[5]"
                database_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[6]"
                title = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, title_xpath))).text
                authors = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, author_xpath))).text
                source = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, source_xpath))).text
                date = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, date_xpath))).text
                database = WebDriverWait(driver, 10).until(
                    EC.presence_of_element_located((By.XPATH, database_xpath))).text
                # 点击条目
                title_list[i].click()
                # 获取driver的句柄
                n = driver.window_handles
                # driver切换至最新生产的页面
                driver.switch_to.window(n[-1])
                time.sleep(3)
                # 开始获取页面信息
                title = WebDriverWait(driver, 10).until(EC.presence_of_element_located(
                    (By.XPATH, "/html/body/div[2]/div[1]/div[3]/div/div/div[3]/div/h1"))).text
                authors = WebDriverWait(driver, 10).until(EC.presence_of_element_located(
                    (By.XPATH, "/html/body/div[2]/div[1]/div[3]/div/div/div[3]/div/h3[1]"))).text
                institute = WebDriverWait(driver, 10).until(EC.presence_of_element_located(
                    (By.XPATH, "/html/body/div[2]/div[1]/div[3]/div/div/div[3]/div/h3[2]"))).text
                abstract = WebDriverWait(driver, 10).until(
                    EC.presence_of_element_located((By.CLASS_NAME, "abstract-text"))).text
                try:
                    keywords = WebDriverWait(driver, 10).until(
                        EC.presence_of_element_located((By.CLASS_NAME, "keywords"))).text[:-1]
                    cssci = WebDriverWait(driver, 10).until(
                        EC.presence_of_element_located((By.XPATH, "/html/body/div[2]/div[1]/div[3]/div/div/div[1]/div[1]/a[2]"))).text
                except:
                    keywords = '无'
                    cssci = 'NULL'
                url = driver.current_url

                # 写入文件
                res = f"{count}\t{title}\t{authors}\t{cssci}\t{institute}\t{date}\t{source}\t{database}\t{keywords}\t{abstract}\t{url}".replace(
                        "\n", "") + "\n"
                print(res)
                with open(f'{theme}.tsv', 'a', encoding='gbk') as f:
                    f.write(res)
            except:
                print(f" 第{count} 条爬取失败\n")
                # 跳过本条,接着下一个
                continue
            finally:
                # 如果有多个窗口,关闭第二个窗口, 切换回主页
                n2 = driver.window_handles
                if len(n2) > 1:
                    driver.close()
                    driver.switch_to.window(n2[0])
                # 计数,判断篇数是否超出限制
                count += 1
        if count == papers_need:
            break
        else:
            # 切换到下一页
            WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//a[@id='PageNext']"))).click()

结果展示:

结果是一个以制表符分隔的表格文件(用 excel 打开),其中包含了论文的基本信息,包括:题目、作者、是否 CSSCI、来源、摘要

完整代码如下:

import time
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from urllib.parse import urljoin


def open_page(driver, theme):
    # 打开页面
    driver.get("https://www.cnki.net")
    # 传入关键字
    WebDriverWait(driver, 100).until(
        EC.presence_of_element_located((By.XPATH, '''//*[@id="txt_SearchText"]'''))).send_keys(theme)
    # 点击搜索
    WebDriverWait(driver, 100).until(
        EC.presence_of_element_located((By.XPATH, "/html/body/div[2]/div[2]/div/div[1]/input[2]"))).click()
    time.sleep(3)

    # 点击切换中文文献
    WebDriverWait(driver, 100).until(
        EC.presence_of_element_located((By.XPATH, "/html/body/div[3]/div[1]/div/div/div/a[1]"))).click()
    time.sleep(3)
    # 获取总文献数和页数
    res_unm = WebDriverWait(driver, 100).until(EC.presence_of_element_located(
        (By.XPATH, "/html/body/div[3]/div[2]/div[2]/div[2]/form/div/div[1]/div[1]/span[1]/em"))).text
    # 去除千分位里的逗号
    res_unm = int(res_unm.replace(",", ''))
    page_unm = int(res_unm / 20) + 1
    print(f"共找到 {res_unm} 条结果, {page_unm} 页。")
    return res_unm


def crawl(driver, papers_need, theme):
    # 赋值序号, 控制爬取的文章数量
    count = 1

    # 当爬取数量小于需求时,循环网页页码
    while count <= papers_need:
        # 等待加载完全,休眠3S
        time.sleep(3)

        title_list = WebDriverWait(driver, 10).until(EC.presence_of_all_elements_located((By.CLASS_NAME, "fz14")))
        # 循环网页一页中的条目
        for i in range(len(title_list)):
            try:
                term = count % 20  # 本页的第几个条目
                title_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[2]"
                author_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[3]"
                source_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[4]"
                date_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[5]"
                database_xpath = f"/html/body/div[3]/div[2]/div[2]/div[2]/form/div/table/tbody/tr[{term}]/td[6]"
                title = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, title_xpath))).text
                authors = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, author_xpath))).text
                source = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, source_xpath))).text
                date = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, date_xpath))).text
                database = WebDriverWait(driver, 10).until(
                    EC.presence_of_element_located((By.XPATH, database_xpath))).text

                # 点击条目
                title_list[i].click()
                # 获取driver的句柄
                n = driver.window_handles
                # driver切换至最新生产的页面
                driver.switch_to.window(n[-1])
                time.sleep(3)
                # 开始获取页面信息
                title = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH ,"/html/body/div[2]/div[1]/div[3]/div/div/div[3]/div/h1"))).text
                authors = WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH ,"/html/body/div[2]/div[1]/div[3]/div/div/div[3]/div/h3[1]"))).text
                institute = WebDriverWait(driver, 10).until(EC.presence_of_element_located(
                    (By.XPATH, "/html/body/div[2]/div[1]/div[3]/div/div/div[3]/div/h3[2]"))).text
                abstract = WebDriverWait(driver, 10).until(
                    EC.presence_of_element_located((By.CLASS_NAME, "abstract-text"))).text
                try:
                    keywords = WebDriverWait(driver, 10).until(
                        EC.presence_of_element_located((By.CLASS_NAME, "keywords"))).text[:-1]
                except:
                    keywords = '无'
                url = driver.current_url
                # 获取下载链接
                # link = WebDriverWait( driver, 10 ).until( EC.presence_of_all_elements_located((By.CLASS_NAME  ,"btn-dlcaj") ) )[0].get_attribute('href')
                # link = urljoin(driver.current_url, link)

                # 写入文件
                res = f"{count}\t{title}\t{authors}\t{institute}\t{date}\t{source}\t{database}\t{keywords}\t{abstract}\t{url}".replace(
                    "\n", "") + "\n"
                print(res)
                with open(f'CNKI_{theme}.tsv', 'a', encoding='gbk') as f:
                    f.write(res)

            except:
                print(f" 第{count} 条爬取失败\n")
                # 跳过本条,接着下一个
                continue
            finally:
                # 如果有多个窗口,关闭第二个窗口, 切换回主页
                n2 = driver.window_handles
                if len(n2) > 1:
                    driver.close()
                    driver.switch_to.window(n2[0])
                # 计数,判断需求是否足够
                count += 1
                if count == papers_need: break

        # 切换到下一页
        WebDriverWait(driver, 10).until(EC.presence_of_element_located((By.XPATH, "//a[@id='PageNext']"))).click()


if __name__ == "__main__":
    # get直接返回,不再等待界面加载完成
    desired_capabilities = DesiredCapabilities.CHROME
    desired_capabilities["pageLoadStrategy"] = "none"

    # 设置谷歌驱动器的环境
    options = webdriver.EdgeOptions()
    # 设置chrome不加载图片,提高速度
    options.add_experimental_option("prefs", {"profile.managed_default_content_settings.images": 2})
    # # 设置不显示窗口
    # options.add_argument('--headless')
    # 创建一个谷歌驱动器
    driver = webdriver.Edge(options=options)
    # 设置搜索主题
    theme = "python"
    # 设置所需篇数
    papers_need = 100

    res_unm = int(open_page(driver, theme))
    # 判断所需是否大于总篇数
    papers_need = papers_need if (papers_need <= res_unm) else res_unm
    crawl(driver, papers_need, theme)

    # 关闭浏览器
    driver.close()

踩过的坑

网页加载太慢导致元素查找出错

网络并不是可靠的,我在调试程序的时候往往出现网页加载过慢导致元素查找出错

  • 第一步:设置 get 直接返回,不需要等待页面加载完成
desired_capabilities = DesiredCapabilities.EDGE
desired_capabilities["pageLoadStrategy"] = "none"
  • 第二步:

在需要等待网页加载完全之后才能执行下一步骤的地方加上 time.sleep() 方法休眠几秒,既可以等待页面加载,也可以防止爬取太快被封IP

代码逻辑出错导致爬取不了 20 倍页数的第 20 条文献信息

刚开始写的时候,逻辑不够准确,导致第20页、40页、60页(20整数倍)的第20条文献爬取不了

后面加了层判断:

if count % 20 != 0:
    term = count % 20  # 本页的第几个条目
else:
    term = 20 # 本页的第20个条目
相关文章
|
1月前
|
数据采集 存储 JSON
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第27天】本文介绍了Python网络爬虫Scrapy框架的实战应用与技巧。首先讲解了如何创建Scrapy项目、定义爬虫、处理JSON响应、设置User-Agent和代理,以及存储爬取的数据。通过具体示例,帮助读者掌握Scrapy的核心功能和使用方法,提升数据采集效率。
99 6
|
6天前
|
数据采集 存储 JavaScript
网页爬虫技术全解析:从基础到实战
在信息爆炸的时代,网页爬虫作为数据采集的重要工具,已成为数据科学家、研究人员和开发者不可或缺的技术。本文全面解析网页爬虫的基础概念、工作原理、技术栈与工具,以及实战案例,探讨其合法性与道德问题,分享爬虫设计与实现的详细步骤,介绍优化与维护的方法,应对反爬虫机制、动态内容加载等挑战,旨在帮助读者深入理解并合理运用网页爬虫技术。
|
1月前
|
数据采集 前端开发 JavaScript
除了网页标题,还能用爬虫抓取哪些信息?
爬虫技术可以抓取网页上的各种信息,包括文本、图片、视频、链接、结构化数据、用户信息、价格和库存、导航菜单、CSS和JavaScript、元数据、社交媒体信息、地图和位置信息、广告信息、日历和事件信息、评论和评分、API数据等。通过Python和BeautifulSoup等工具,可以轻松实现数据抓取。但在使用爬虫时,需遵守相关法律法规,尊重网站的版权和隐私政策,合理控制请求频率,确保数据的合法性和有效性。
|
1月前
|
数据采集 Web App开发 前端开发
Python爬虫进阶:Selenium在动态网页抓取中的实战
【10月更文挑战第26天】动态网页抓取是网络爬虫的难点,因为数据通常通过JavaScript异步加载。Selenium通过模拟浏览器行为,可以加载和执行JavaScript,从而获取动态网页的完整内容。本文通过实战案例,介绍如何使用Selenium在Python中抓取动态网页。首先安装Selenium库和浏览器驱动,然后通过示例代码展示如何抓取英国国家美术馆的图片信息。
87 6
|
1月前
|
数据采集 Web App开发 JavaScript
爬虫策略规避:Python爬虫的浏览器自动化
爬虫策略规避:Python爬虫的浏览器自动化
|
1月前
|
数据采集 前端开发 中间件
Python网络爬虫:Scrapy框架的实战应用与技巧分享
【10月更文挑战第26天】Python是一种强大的编程语言,在数据抓取和网络爬虫领域应用广泛。Scrapy作为高效灵活的爬虫框架,为开发者提供了强大的工具集。本文通过实战案例,详细解析Scrapy框架的应用与技巧,并附上示例代码。文章介绍了Scrapy的基本概念、创建项目、编写简单爬虫、高级特性和技巧等内容。
67 4
|
1月前
|
存储 数据采集 数据库
用 Python 爬取淘宝商品价格信息时需要注意什么?
使用 Python 爬取淘宝商品价格信息时,需注意法律和道德规范,遵守法律法规和平台规定,避免非法用途。技术上,可选择 Selenium 和 Requests 库,处理反爬措施如 IP 限制、验证码识别和请求频率控制。解析页面数据时,确定数据位置并清洗格式。数据存储可选择 CSV、Excel、JSON 或数据库,定期更新并去重。还需进行错误处理和日志记录,确保爬虫稳定运行。
|
1月前
|
数据采集 Web App开发 iOS开发
如何利用 Python 的爬虫技术获取淘宝天猫商品的价格信息?
本文介绍了使用 Python 爬虫技术获取淘宝天猫商品价格信息的两种方法。方法一使用 Selenium 模拟浏览器操作,通过定位页面元素获取价格;方法二使用 Requests 和正则表达式直接请求页面内容并提取价格。每种方法都有详细步骤和代码示例,但需注意反爬措施和法律法规。
|
4月前
|
数据采集 存储 中间件
Python进行网络爬虫:Scrapy框架的实践
【8月更文挑战第17天】网络爬虫是自动化程序,用于从互联网收集信息。Python凭借其丰富的库和框架成为构建爬虫的首选语言。Scrapy作为一款流行的开源框架,简化了爬虫开发过程。本文介绍如何使用Python和Scrapy构建简单爬虫:首先安装Scrapy,接着创建新项目并定义爬虫,指定起始URL和解析逻辑。运行爬虫可将数据保存为JSON文件或存储到数据库。此外,Scrapy支持高级功能如中间件定制、分布式爬取、动态页面渲染等。在实践中需遵循最佳规范,如尊重robots.txt协议、合理设置爬取速度等。通过本文,读者将掌握Scrapy基础并了解如何高效地进行网络数据采集。
232 6
|
4月前
|
数据采集 存储 JSON
Python爬虫开发:BeautifulSoup、Scrapy入门
在现代网络开发中,网络爬虫是一个非常重要的工具。它可以自动化地从网页中提取数据,并且可以用于各种用途,如数据收集、信息聚合和内容监控等。在Python中,有多个库可以用于爬虫开发,其中BeautifulSoup和Scrapy是两个非常流行的选择。本篇文章将详细介绍这两个库,并提供一个综合详细的例子,展示如何使用它们来进行网页数据爬取。