网络爬虫开发

爬取思路: 爬取网页数据首先需要请求网站响应,需要安装requests库,读取网页内容需要安装xml库,调用xml库中的etree模块来处理XmlHtml数据,再使用Xpath解析数据.

掌握 XPath 后,你可以高效地从网页或 XML 数据中提取所需信息,是爬虫开发和数据处理的必备技能。

初识爬虫

1.requests请求库模块

Pycharm终端安装requests请求库

pip install requests

①.导入requests请求库

import requests

②.定义个变量要抓取的页面:

url = '网址'

③.构造请求头(伪装身份user-agent)

打开要抓取的网址,快捷键:F12→Network(网络)→Name(名称)→点一个文件→Header(标头)→user-agent(拉到底部就会看到)

#伪装成浏览器的身份进行抓取
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
}

④.发起请求并接收响应对象

response = requests.get(url = url, headers = headers)
encoding = 'utf-8'
bd_url = 'https://www.baidu.com/'  # 定义要请求的url数据包
response = requests.get(url=bd_url)  # 发起请求并且接收响应对象
response.encoding = 'utf-8'  # 设置编码格式
print(response)  # <Response [200]>  响应对象
print(response.text)  # 获取文本内容
print(response.content)  # 获取到进制格式的数据
print(response.headers)  # 获取响应头信息
print(response.request.headers)  # 获取请求头信息
print(response.status_code)  # 获取状态码
print(response.encoding)  # 获取当前编码格式
print(response.url)  # 获取到当前请求的url
print(response.cookies)  # 获取到用户身份对象
print(response.content.decode())  # .decode() 默认utf-8 进行解码 如果需要使用其他编码进行解码 传参即可

⑤.保存文件然后读取

#完整示例-爬图
import requests

url = 'https://pic.netbian.com/uploads/allimg/240924/081942-17271371826661.jpg'
response = requests.get(url=url)
# print(response.content)
with open('王楚然.jpg', 'wb') as f:
    f.write(response.content)
#完整示例-爬音频
import requests

url = 'https://m701.music.126.net/20240924221116/8ec6067b2ae956d58df4deff0bdddaf9/jdyyaac/obj/w5rDlsOJwrLDjj7CmsOj/31433228192/7100/62f1/fd29/3833d333ff983ce8eebe35872a9484ee.m4a'
response = requests.get(url=url)
with open('大人中.mp3', 'wb') as f:
    f.write(response.content)
#爬视频
import requests

cookies = {
    'PEAR_UUID': '7b913605-1037-4a28-b16a-46e7657e6f03',
    'Hm_lvt_9707bc8d5f6bba210e7218b8496f076a': '1727185861',
    'HMACCOUNT': '33EE743B756BCDEE',
    'Hm_lpvt_9707bc8d5f6bba210e7218b8496f076a': '1727185870',
}

headers = {
    'accept': '*/*',
    'accept-language': 'zh-CN,zh;q=0.9',
    'cache-control': 'no-cache',
    # 'cookie': 'PEAR_UUID=7b913605-1037-4a28-b16a-46e7657e6f03; Hm_lvt_9707bc8d5f6bba210e7218b8496f076a=1727185861; HMACCOUNT=33EE743B756BCDEE; Hm_lpvt_9707bc8d5f6bba210e7218b8496f076a=1727185870',
    'pragma': 'no-cache',
    'priority': 'i',
    'range': 'bytes=0-',
    'referer': 'https://www.pearvideo.com/',
    'sec-ch-ua': '"Google Chrome";v="129", "Not=A?Brand";v="8", "Chromium";v="129"',
    'sec-ch-ua-mobile': '?0',
    'sec-ch-ua-platform': '"Windows"',
    'sec-fetch-dest': 'video',
    'sec-fetch-mode': 'no-cors',
    'sec-fetch-site': 'same-site',
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/129.0.0.0 Safari/537.36',
}

response = requests.get(
    'https://video.pearvideo.com/mp4/short/20171004/cont-1165391-10964201-hd.mp4',
    cookies=cookies,
    headers=headers,
)
print(response.content)
with open('1.mp4', 'wb') as f:
    f.write(response.content)

Pycharm内置模块方法(不推荐使用)

from urllib.request import urlopen
url ='url'#要爬取的数据网页
html = urlopen(url)
#print(html.read().decode('utf-8'))
with open("爬取的文件.html", "w",encoding="utf-8") as f:
write = f.write(html.read().decode('utf-8'))

★用input方法的get请求

import requests
from urllib.parse import quote
import time

try:
    # 获取用户输入并进行URL编码
    content = input("请输入你要查询的内容:")
    encoded_content = quote(content)

    # 构建请求URL
    url = f"https://www.sogou.com/web?query={encoded_content}"

    # 设置请求头模拟浏览器
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36"
    }

    # 发送HTTP请求
    print(f"正在请求: {url}")
    resp = requests.get(url, headers=headers, timeout=10)  # 设置超时时间

    # 检查响应状态码
    resp.raise_for_status()  # 若状态码非200,抛出HTTPError

    # 保存响应内容到文件
    filename = f"爬取的{content[:10]}内容.html"  # 文件名包含部分查询内容
    with open(filename, "w", encoding="utf-8") as f:
        f.write(resp.text)  # 直接使用resp.text

    print(f"✅ 文件已成功保存为: {filename}")
    print(f"状态码: {resp.status_code}")

except requests.exceptions.HTTPError as e:
    print(f"HTTP请求错误: {e}")
except requests.exceptions.Timeout:
    print("请求超时,请重试")
except requests.exceptions.RequestException as e:
    print(f"网络请求异常: {e}")
except Exception as e:
    print(f"发生未知错误: {e}")
finally:
    # 控制请求频率
    time.sleep(1)  # 等待1秒后结束程序

用input方法的post请求

import requests
from urllib.parse import urlencode
import time
import json

try:
    # 获取用户输入
    keyword = input("请输入你要翻译的内容:")

    # 百度翻译API的POST接口
    url = "https://fanyi.baidu.com/sug"

    # 设置请求头
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
        "Content-Type": "application/x-www-form-urlencoded"
    }

    # 构建POST请求的表单数据(百度翻译API需要的参数)
    data = {
        "kw": keyword  # 百度翻译API使用'kw'参数传递要翻译的内容
    }

    # 发送POST请求
    print(f"正在请求: {url}")
    resp = requests.post(url, headers=headers, data=data, timeout=10) #发送post请求时,传参数用的是data.

    # 检查响应状态码
    resp.raise_for_status()

    # 解析JSON响应
    result = resp.json()

    # 保存响应内容到文件
    filename = f"翻译结果_{keyword[:10]}.json"
    with open(filename, "w", encoding="utf-8") as f:
        json.dump(result, f, ensure_ascii=False, indent=2)

    print(f"✅ 翻译结果已保存为: {filename}")
    print(f"状态码: {resp.status_code}")

    # 简单打印翻译结果
    if result.get('errno') == 0 and 'data' in result:
        print("\n翻译结果预览:")
        for item in result['data'][:3]:  # 只显示前3个结果
            print(f"{item.get('k', '')} -> {item.get('v', '')}")

except requests.exceptions.HTTPError as e:
    print(f"HTTP请求错误: {e}")
except requests.exceptions.Timeout:
    print("请求超时,请重试")
except requests.exceptions.RequestException as e:
    print(f"网络请求异常: {e}")
except json.JSONDecodeError:
    print("无法解析JSON响应,可能是请求格式不正确")
except Exception as e:
    print(f"发生未知错误: {e}")
finally:
    # 控制请求频率
    time.sleep(1)

参数请求与Cookie

 

import requests
url = "https://movie.douban.com/j/chart/top_list" #问号后面的参数可以用字典的形式传参数

data = {
    "type":"13",
    "interval_id":"100:90",
    "action": "",
    "start":"0",
    "limit":"20"
}
headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36",
        "Content-Type": "application/x-www-form-urlencoded"
}
response = requests.get(url, params=data, headers=headers)#发送get请求时,传参数用的是params
print(response.json())

2.xpath数据解析(XML Path Language):是一种用于在 XML 或 HTML 文档中定位节点的查询语言和选择特定元素的表达式语言。它就像是一个路径导航系统,能帮助开发者快速准确地找到文档中的特定节点或节点集合。在网页数据抓取、XML 处理等场景中广泛应用。

作用

  • 数据提取:在网页爬虫中,当需要从复杂的 HTML 页面结构中提取特定信息,如标题、正文、链接等时,XPath 可以精确定位到包含这些信息的 HTML 元素,然后方便地提取出所需数据。
  • 元素定位:在自动化测试中,需要操作网页上的各种元素,如点击按钮、填写表单等。XPath 可以帮助找到这些元素在页面中的位置,从而实现对它们的操作。

在编程语言中使用 XPath

pycharm终端安装lxml库:

pip install lxml
#或使用镜像下载安装
pip install lxml -i 镜像地址

pycharm终端查看当前环境下安装了哪些库

pip list

2.1.etree通常指的是lxml库的etree模块(ElementTree),它是 Python 中处理 XML 和 HTML 文档的强大工具。如果没有安装lxmlPython 标准库中也有一个功能类似的xml.etree.ElementTree模块,但lxml更高效且支持更多特性

主要功能

    • 解析 XML/HTML 文档:可以从字符串、文件或 URL 加载 XML/HTML 内容。
    • 构建文档:支持动态创建 XML/HTML 结构。
    • 元素操作:提供简洁的 API 来遍历、修改和查询文档节点。
    • XPath 支持:通过 XPath 表达式高效定位特定元素。

①.解析 XML 文档

2.2.etree.fromstring():可以将 XML/HTML 格式的字符串解析为树形结构(ElementTree 对象),以便于后续的遍历、查询和修改。

from lxml import etree

xml_str = '<book><title>Python编程</title><price>99.0</price></book>'
root = etree.fromstring(xml_str)

# 获取根节点标签
print(root.tag)  # 输出: book

# 查找子节点
title = root.find('title').text
price = root.find('price').text
print(f"书名: {title}, 价格: {price}")  # 输出: 书名: Python编程, 价格: 99.0

②.遍历元素

# 遍历所有子元素
for child in root:
    print(child.tag, child.text)  # 输出: element 内容

# 使用XPath查找元素
elements = root.xpath('//element')

③.修改文档

# 创建新元素
new_element = etree.Element('new_element')
new_element.text = '新内容'

# 添加到根节点
root.append(new_element)

# 修改现有元素
root.find('element').text = '更新后的内容'

# 删除元素
root.remove(new_element)

④.输出文档

# 转换为字符串
xml_output = etree.tostring(root, pretty_print=True, encoding='unicode')
print(xml_output)

# 写入文件
# etree.ElementTree(root).write('output.xml', pretty_print=True)

Python(使用lxml库)从lxml库中导入etree

from lxml import etree
#如果Pycharm报错可以考虑下面这种导入方式
from lxml imprt html
etree=html.etree
html = '<html><body><div class="content"><h1>Hello</h1></div></body></html>' tree = etree.HTML(html) # 选择所有<h1>元素的文本 titles = tree.xpath('//h1/text()') print(titles) # 输出: ['Hello'] # 选择class为"content"的<div>元素 divs = tree.xpath('//div[@class="content"]')

打开抓取的html文档然后读取

with open('抓取的文档.html', 'r', encoding='utf-8') as f:
    html = f.read()  # 读取
# print(html)  # 读取出来的文本内容
# print(type(html))  # <class 'str'>查看类开,字符串类型
tree = etree.HTML(html)  # 渲染html

JavaScript(浏览器环境)

// 选择第一个<h1>元素
const h1 = document.evaluate('//h1', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;

// 选择所有<a>元素的href属性
const links = document.evaluate('//a/@href', document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
for (let i = 0; i < links.snapshotLength; i++) {
  console.log(links.snapshotItem(i).value);
}

①.基本路径表达式

xpath 通过路径表达式从根节点/或当前节点.开始定位元素:

xpath方法返回值是列表  xpath语法规定索引值从1开始

  • 绝对路径:从根节点开始,使用 / 分隔节点名称。
xpath('/html/body/div/h1/')  # 选择HTML文档中的<h1>元素
xpath('/html/body/div/h1/text()')  # 选择HTML文档中的<h1>元素的数据
  • 相对路径从当前节点开始,使用 // 表示任意层级的子节点。
xpath('//div/p')  # 选择所有<div>下的<p>元素(不限层级)

②. 节点选择器

符号 作用
// 从任意位置开始选择节点。
. 选择当前节点。
.. 选择当前节点的父节点。
@ 选择属性。
* 通配符,匹配任意节点或属性
[ ] 条件表达式,用于筛选节点。
position 当前节点的位置(如 [1] 表示第一个匹配的节点)。

③.常用函数

  • 文本匹配:
xpath('//div[text()="Hello World"]')  # 选择文本内容为"Hello World"的<div>
xpath('//div[contains(text(), "World")]')  # 选择包含"World"的<div>

属性匹配:

xpath('//a[@href="https://example.com"]') # 选择href属性为指定值的<a>标签
xpath('//input[@type="text"]') # 选择包含"World"的<div>

位置筛选:

xpath('//li[position()=1]') # 选择第一个<li> 
xpath('//li[last()]') # 选择最后一个<li>.<li>

④.逻辑运算符

使用 andornot() 组合条件:

xpath('//div[@class="container" and @id="main"]') # 同时满足两个属性条件
xpath('//div[@class="item" or @class="product"] ') # 满足任一属性条件
xpath('//div[not(@class="ignore")]') # 排除特定属性的<div>

⑤.轴(Axes)选择

轴用于选择当前节点的相关节点(如父节点、兄弟节点等):

轴名称 作用 示例
parent:: 父节点 xpath('//h1/parent::div')
child:: 子节点(默认) xpath('//div/child::p')
descendant 所有后代节点(不限层级) xpath('//div/descendant::span')
ancestor:: 所有祖先节点(包括父节点的父节点) xpath('//p/ancestor::body')
following-sibling:: 后续兄弟节点 xpath('//li[1]/following-sibling::li')
preceding-sibling: 前续兄弟节点 xpath('//li[3]/preceding-sibling::li')

案例:

# 导入所需库
import requests # 用于发送HTTP请求,获取网页内容
from lxml import etree # 用于解析HTML文档,提取需要的信息

# 定义基本参数
# 目标网页URL(小说章节页面)
url = 'https://www.xs386.com/6661/32866513.html'

# 请求头信息,模拟浏览器访问
headers = {
    'user-agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
} # user-agent用于告诉服务器访问者的浏览器信息,避免被服务器识别为爬虫而拒绝访问。

# 发送GET请求获取网页内容
response = requests.get(url=url, headers=headers)

# 设置正确的编码,设置编码为utf-8,确保中文显示正常
response.encoding = 'utf-8'

# 将HTML文本解析为可操作的元素树对象
e = etree.HTML(response.text) # 将网页源代码转换为解析树,便于后续提取数据

# 使用XPath表达式提取id为"content"的div内的所有文本,注意返回的是列表
content_list = e.xpath('//div[@id="content"]//text()') # 查找整个文档中 id 属性为 "content" 的 div 元素并提取该 div 下所有层级的文本内容

# 判断是否成功提取到内容
if content_list:

    # 将列表内容合并为字符串
    content = '\n'.join(content_list)

    # 清理内容中的多余空格和换行
    content = content.strip()
    
    # 保存到文件(使用utf-8编码)
    with open('上门龙婿.txt', 'w', encoding='utf-8') as f:
        f.write(content)
    print("内容保存成功")
else:
    print("未找到指定内容")

 

3.JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,以易于人阅读和编写的文本格式存储和表示数据,同时也易于机器解析和生成,在前后端数据交互、配置文件等场景中被广泛使用。

一、JSON 的核心特点

  • 轻量级:相比 XML 等格式,JSON 的数据结构更简洁,文件体积更小,传输效率更高。
  • 跨语言兼容:几乎所有编程语言都支持 JSON 的解析和生成,便于不同系统间的数据交互。
  • 可读性强:采用键值对(key-value)结构,格式清晰,易于理解和维护。
  • 结构灵活:支持嵌套对象和数组,能表示复杂的数据结构。

二、JSON 的基本语法规则

数据类型与表示

  • 对象(Object):{}包裹,由键值对组成,键和值用:分隔,多个键值对用,分隔。
  • 数组(Array):[]包裹,元素可以是任意数据类型,元素间用,分隔。
  • 键值对:键必须是字符串,值可以是字符串、数字、布尔值、null、对象或数组。
{
  "id": 1001,
  "hobbies": ["阅读", "编程"],
  "isStudent": false,
  "address": null
}

格式要求

  • 键名必须用双引号""包裹(部分语言支持单引号,但标准 JSON 要求双引号)。
  • 逗号分隔时,最后一个键值对后不能有多余的逗号(部分环境支持,但可能导致兼容性问题)。

三、JSON 与 JavaScript 的关系

  • 起源:JSON 最初是 JavaScript 的子集,但现在已独立为跨语言的标准格式。
  • 在 JavaScript 中的使用:
    • 可以直接通过JSON.parse()将 JSON 字符串转换为 JavaScript 对象。
    • 通过JSON.stringify()将 JavaScript 对象转换为 JSON 字符串。
// 示例:JSON与JavaScript对象的转换
const jsonStr = '{"name":"张三","age":25}';
const obj = JSON.parse(jsonStr); // 转换为对象
console.log(obj.name); // 输出:张三

const data = { city: "北京", population: 2100 };
const jsonData = JSON.stringify(data); // 转换为JSON字符串
console.log(jsonData); // 输出:{"city":"北京","population":2100}

四、JSON 的应用场景

  • 前后端数据交互:Web 应用中,前端通过 API 请求从服务器获取 JSON 数据,或向服务器发送 JSON 格式的请求参数。
  • 配置文件:如项目配置文件(package.json)、系统配置等,用 JSON 存储结构化配置信息。
  • 数据存储:NoSQL 数据库(如 MongoDB)支持 JSON 格式的文档存储,部分关系型数据库也支持 JSON 字段。
  • API 接口规范:RESTful API 通常以 JSON 作为数据交换格式,例如 OpenAPI(Swagger)规范。

3.1.json.dump():用于将 Python 对象(如字典、列表)转换为 JSON 格式的字符串,并将其写入文件或类文件对象(如网络连接、内存缓冲区等)。这一过程也被称为序列化(Serialization)

json.dump(obj, fp, *, skipkeys=False, ensure_ascii=True, check_circular=True, 
          allow_nan=True, cls=None, indent=None, separators=None, 
          default=None, sort_keys=False, **kw)

参数说明:

  • obj需要序列化的 Python 对象(如字典、列表)。
  • fp文件对象(如open('data.json', 'w')返回的对象),用于写入 JSON 数据。
  • indent可选参数,指定缩进空格数,用于美化 JSON 格式(如indent=2)。
  • ensure_ascii是否确保所有非 ASCII 字符被转义(默认True,中文会被转义为\uXXXX)。
  • sort_keys是否按字典键排序(默认False)。
import json

data = {
    "name": "张三",
    "age": 30,
    "hobbies": ["阅读", "编程"],
    "address": {"city": "北京", "zipcode": "100000"}
}

# 将数据写入JSON文件
with open("data.json", "w", encoding="utf-8") as f:
    json.dump(data, f, ensure_ascii=False, indent=2)
  • ensure_ascii=False确保中文等非 ASCII 字符正确写入文件。
  • indent=2使用 2 个空格缩进,让 JSON 文件更易读。

若需将 JSON 数据打印到控制台或日志中,可结合sys.stdout使用:

import json
import sys

data = {"code": 200, "message": "操作成功", "data": [1, 2, 3]}
json.dump(data, sys.stdout, ensure_ascii=False, indent=2)

# 输出结果:
'''{
  "code": 200,
  "message": "操作成功",
  "data": [
    1,
    2,
    3
  ]
}'''

3.2.json.dumps():用于将 Python 对象(如字典、列表)转换为 JSON 格式的字符串。这一过程也被称为序列化(Serialization)。与json.dump()(写入文件)不同,json.dumps()返回字符串,适用于网络传输、打印输出或内存处理。

json.dumps(obj, *, skipkeys=False, ensure_ascii=True, check_circular=True,
           allow_nan=True, cls=None, indent=None, separators=None,
           default=None, sort_keys=False, **kw)
import json

data = {
    "name": "张三",
    "age": 30,
    "hobbies": ["阅读", "编程"],
    "is_student": False,
    "address": {"city": "北京", "zipcode": "100000"}
}

# 将Python对象转换为JSON字符串
json_str = json.dumps(data, ensure_ascii=False, indent=2)
print(json_str)

输出结果

{
  "name": "张三",
  "age": 30,
  "hobbies": ["阅读", "编程"],
  "is_student": false,
  "address": {
    "city": "北京",
    "zipcode": "100000"
  }
}

 网络数据传输

API 接口或网络通信中,常将数据序列化为 JSON 字符串后发送:

import requests
import json

data = {"user_id": 123, "action": "login"}
json_payload = json.dumps(data)

try:
    response = requests.post(
        "https://api.example.com/user",  # 替换为实际API地址
        data=json_payload,
        headers={"Content-Type": "application/json"}
    )
    response.raise_for_status()  # 检查请求是否成功(状态码200)
except requests.exceptions.ConnectionError as e:
    print(f"网络连接错误: {e}")
    print("请检查域名是否正确、网络是否正常连接。")
except requests.exceptions.Timeout:
    print("请求超时,请稍后重试。")
except requests.exceptions.HTTPError as e:
    print(f"HTTP错误: {e}")
except requests.exceptions.RequestException as e:
    print(f"请求发生异常: {e}")
else:
    print("请求成功!")
    print(response.json())  # 处理响应数据
函数名 功能描述
json.dumps() 将 Python 对象序列化为字符串(返回值为字符串,适用于网络传输)。
json.dump() 将 Python 对象序列化并写入文件(需传入文件对象,如open('data.json', 'w'))。
json.loads() 将 JSON 字符串反序列化为 Python 对象(解析字符串)。
json.load() 从文件中读取 JSON 数据并反序列化为 Python 对象
5.re正则表达式数据解析(Regular Expression,简称 Regex 或 RE)是一种强大的文本模式匹配工具,用于在字符串中查找、提取或替换符合特定模式的文本片段。正则表达式数据解析就是利用这种模式匹配能力,从复杂的文本数据中提取出结构化的信息。
正则表达式在数据解析中广泛应用于以下场景:
  • 文本提取:从网页、日志文件、配置文件等非结构化文本中提取特定信息
  • 数据验证:检查输入数据是否符合特定格式(如邮箱、URL、电话号码)
  • 文本替换:根据特定模式批量替换文本内容
  • 字符串分割:根据特定分隔符将字符串分割成多个部分
  • 日志分析:从系统日志中提取关键信息(如时间戳、错误代码)

4.正则表达式的基本语法re

正则表达式测试网站:https://tool.oschina.net/regex

  • 普通字符:匹配自身,如a匹配字符 "a"
  • 元字符:具有特殊含义的字符,如.匹配任意字符
  • 字符类:[]表示,匹配方括号内的任意一个字符,如[abc]匹配 "a"、"b" 或 "c"
  • 量词:控制匹配次数,如*表示 0 次或次,+表示 1 次或多次,?表示 0 次或 1
  • 边界符:^表示字符串开始,$表示字符串结束
  • 分组:()表示,可以捕获匹配的内容,如(abc)匹配并捕获 "abc"

①.基本元字符 

元字符 描述
. 匹配除换行符外的任意单个字符
^ 匹配字符串的开始位置
$  匹配字符串的结束位置
* 匹配前面的子表达式零次或多次
+ 匹配前面的子表达式一次或多次
? 匹配前面的子表达式零次或一次
{n} 匹配前面的子表达式恰好 n 次
{n,} 匹配前面的子表达式至少 n 次
{n,m} 匹配前面的子表达式至少 n 次,最多 m 次

②.字符类元字符

[] - 定义字符类,匹配方括号内的任意一个字符

  • 例如:[abc] 匹配 'a'、'b' 'c'
  • 可以使用连字符表示范围:[a-z] 匹配任意小写字母
  • 开头使用 ^ 表示取反:[^0-9] 匹配任意非数字字符

③.特殊字符类

特殊字符 描述
\d 匹配任意数字,等价于 [0-9]
\D  匹配任意非数字,等价于 [^0-9]
\w 匹配任意字母、数字或下划线,等价于 [a-zA-Z0-9_]
\W 匹配任意非字母、数字或下划线,等价于 [^a-zA-Z0-9_]
\s 匹配任意空白字符,包括空格、制表符、换页符等
\S 匹配任意非空白字符

④.分组和引用

  • () - 分组,将多个字符视为一个单元

例如:(ab)+ 匹配一个或多个连续的 "ab"

可以捕获匹配的内容,用于后续引用

  • \1\2, ... - 反向引用,引用前面捕获的分组

例如:(\w+) \1 匹配重复的单词,如 "hello hello"

⑤.或操作符

| - 逻辑或,匹配两个或多个模式中的任意一个

例如:cat|dog 匹配 "cat" 或 "dog"

⑥.转义字符

\ - 转义字符,用于取消元字符的特殊含义

例如:\. 匹配字面意义的点号,也用于表示特殊字符类,如 \d\s 等

⑦.断言(零宽度断言)

(?=...) - 正向肯定预查,匹配后面紧跟指定模式的位置

例如:\w+(?=\d) 匹配后面跟数字的单词

(?!...) - 正向否定预查,匹配后面不紧跟指定模式的位置

例如:\w+(?!\d) 匹配后面不跟数字的单词

(?<=...) - 反向肯定预查,匹配前面是指定模式的位置

例如:(?<=\$)\d+ 匹配前面是美元符号的数字

✍(?<!...) - 反向否定预查,匹配前面不是指定模式的位置

例如:(?<!\$)\d+ 匹配前面不是美元符号的数字

⑧.其他元字符

\b - 匹配单词边界,如 \bcat\b 只匹配独立的 "cat" 单词

\B - 匹配非单词边界

(?:...) - 非捕获组,分组但不捕获匹配的内容

⑨.贪婪匹配和惰性匹配

贪婪匹配:.* 意思是尽可能的去匹配结果

惰性匹配:.*? 意思是尽可能的去匹配结果

⑩.常用的正则表达式格式

手机号校验
中国大陆手机号一般是 11 位数字,以 1 开头,第二位可能是 3、4、5、6、7、8、9 等
正则表达式:/^1[3456789]\d{9}$/
解释:
^ 表示匹配字符串的开始。
1 表示手机号以 1 开头。
[3456789] 表示第二位是 3 到 9 中的任意一个数字。
\d{9} 表示后面跟着 9 位数字。
$ 表示匹配字符串的结束。
邮箱校验
正则表达式:/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/
解释:
^ 匹配字符串开始。
[a-zA-Z0-9._%+-]+ 表示用户名部分,可以包含字母、数字、点、下划线、百分号、加号和减号,且至少有一个字符。
@ 匹配 @ 符号。
[a-zA-Z0-9.-]+ 表示域名部分,包含字母、数字、点和减号。
\. 匹配实际的点号(因为点号在正则中有特殊含义,所以需要转义)。
[a-zA-Z]{2,} 表示顶级域名,至少有两个字母。
$ 匹配字符串结束。
身份证号码校验(18 位)
正则表达式:/^[1-9]\d{5}(18|19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[0-9Xx]$/
解释:
^ 匹配字符串开始。
[1-9]\d{5} 表示前 6 位地址码,第一位不能是 0。
(18|19|20)\d{2} 表示年份,限定在 1800 到 2099 之间。
(0[1-9]|1[0-2]) 表示月份,01 到 12。
(0[1-9]|[12]\d|3[01]) 表示日期,根据月份合理取值。
\d{3} 表示顺序码。
[0-9Xx] 表示校验码,X 可以是大写或小写。
$ 匹配字符串结束。
密码强度校验(至少包含字母和数字,长度 8 到 20 位)
正则表达式:/^(?=.*[a-zA-Z])(?=.*\d)[a-zA-Z\d]{8,20}$/大小写皆可  或  /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)[a-zA-Z\d]{8,}$/至少包含一个大写字母。
解释:
^ 匹配字符串开始。
(?=.*[a-zA-Z]) 是一个正向预查,表示字符串中至少有一个字母。
(?=.*\d) 是一个正向预查,表示字符串中至少有一个数字。
[a-zA-Z\d]{8,20} 表示由字母和数字组成,长度在 8 到 20 位之间。
$ 匹配字符串结束。
URL校验
正则表达式:/^(https?|ftp):\/\/([a-zA-Z0-9.-]+(\:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2})(:\d+)?(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/
解释:
^ 匹配字符串开始。
(https?|ftp) 匹配协议,可以是 http、https 或 ftp。
:\/\/ 匹配 :// 分隔符。
后面复杂的部分是对域名、IP 地址、端口、路径等的匹配和限定。
$ 匹配字符串结束。
整数校验
正则表达式:/^-?\d+$/
解释:
^ 匹配字符串开始。
-? 表示可以有一个可选的负号。
\d+ 表示一个或多个数字。
$ 匹配字符串结束。
正整数校验
正则表达式:/^[1-9]\d*$/
解释:
^ 匹配字符串开始。
[1-9] 表示第一位不能是 0,必须是 1 到 9 中的一个数字。
\d* 表示后面可以跟零个或多个数字。
$ 匹配字符串结束。
浮点数校验
正则表达式:/^-?\d+(\.\d+)?$/
解释:
^ 匹配字符串开始。
-? 表示可以有一个可选的负号。
\d+ 表示整数部分,至少一个数字。
(\.\d+)? 表示小数部分是可选的,\. 匹配点号,\d+ 表示至少一个数字。
$ 匹配字符串结束。
日期校验(YYYY-MM-DD 格式)
正则表达式:/^((((1[6-9]|[2-9]\d)\d{2})(0[13578]|1[02])(0[1-9]|[12]\d|3[01]))|(((1[6-9]|[2-9]\d)\d{2})(0[13456789]|1[012])(0[1-9]|[12]\d|30))|(((1[6-9]|[2-9]\d)\d{2})02(0[1-9]|1\d|2[0-8]))|((((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26]))|((16|[2468][048]|[3579][26])00))0229))$/
解释:
^ 表示匹配字符串的开始。
整体是多个分组的或关系(|)。
((((1[6-9]|[2-9]\d)\d{2})(0[13578]|1[02])(0[1-9]|[12]\d|3[01])) 匹配大月(1、3、5、7、8、10、12 月)有 31 天的日期,年份部分 (1[6-9]|[2-9]\d)\d{2} 表示 1600 年到 9999 年。
(((1[6-9]|[2-9]\d)\d{2})(0[13456789]|1[012])(0[1-9]|[12]\d|30)) 匹配小月(除 2 月外有 30 天的月份)的日期。
(((1[6-9]|[2-9]\d)\d{2})02(0[1-9]|1\d|2[0-8])) 匹配平年 2 月(28 天)的日期。
((((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26]))|((16|[2468][048]|[3579][26])00))0229)) 匹配闰年 2 月(29 天)的日期,闰年的判断条件是能被 4 整除但不能被 100 整除,或者能被 400 整除。
$ 表示匹配字符串的结束。
车牌号校验(以中国大陆为例,普通民用汽车号牌)
正则表达式:/^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$/
解释:
^ 匹配字符串开始。
[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1} 表示省份简称或特殊标识(如使、领),是一个字符。
[A-Z]{1} 表示地区代码,为一个大写字母。
[A-Z0-9]{4} 表示 4 位字母或数字。
[A-Z0-9挂学警港澳]{1} 表示最后一位可能是字母、数字,或者特殊标识(挂、学、警、港、澳)。
$ 匹配字符串结束。
邮政编码校验(中国大陆)
正则表达式:/^[1-9]\d{5}$/
解释:
^ 匹配字符串开始。
[1-9] 表示第一位不能为 0,是 1 到 9 中的一个数字。
\d{5} 表示后面跟着 5 位数字。
$ 匹配字符串结束。
中文姓名校验(一般情况,假设姓名为 2 到 4 个汉字)
正则表达式:/^[\u4e00-\u9fa5]{2,4}$/
解释:
^ 匹配字符串开始。
[\u4e00-\u9fa5] 表示匹配一个汉字,\u4e00 到 \u9fa5 是 Unicode 中汉字的编码范围。
{2,4} 表示匹配 2 到 4 个这样的汉字。
$ 匹配字符串结束。
英文单词校验(由字母组成,可包含连字符,长度不限)
正则表达式:/^[a-zA-Z-]+$/
解释:
^ 匹配字符串开始。
[a-zA-Z-] 表示可以是大写或小写字母,也可以是连字符。
+ 表示一个或多个这样的字符。
$ 匹配字符串结束。
IPv4 地址校验
正则表达式:/^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
解释:
^ 匹配字符串开始。
(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?) 表示每个部分(0 到 255 的数字)的匹配规则,分别处理了不同范围的数字情况。
\. 匹配实际的点号(转义)。
重复四次上述部分来匹配 IPv4 地址的四个字节。
$ 匹配字符串结束。
银行卡号校验(一般 16 到 19 位数字)
正则表达式:/^\d{16,19}$/
解释:
^ 匹配字符串开始。
\d{16,19} 表示由 16 到 19 位数字组成。
$ 匹配字符串结束。

4.1.re.findall(“正则表达式”,"原字符串","可选参数") Python 中 re 模块提供的一个重要函数,用于在字符串中查找所有匹配正则表达式的非重叠子串,并返回一个包含所有匹配结果的列表。如果没有找到匹配的内容,则返回空列表。

re.findall(pattern, string, flags=0)
  • pattern:正则表达式模式字符串
  • string:需要被搜索的原始字符串
  • flags:可选参数,用于指定匹配模式(如忽略大小写、多行匹配等)

re.findall()的返回结果取决于正则表达式中是否包含捕获组:

  • 无捕获组:直接返回所有匹配的子字符串列表
  • 有一个捕获组:返回每个匹配中捕获组的内容列表
  • 有多个捕获组:返回元组列表,每个元组包含一个匹配中所有捕获组的内容
import re

# 示例1:无捕获组,匹配所有数字
text1 = "hello 123 world 456"
result1 = re.findall(r'\d+', text1)
print(f"示例1结果: {result1}")  # 输出: ['123', '456']

# 示例2:有一个捕获组,提取所有括号内的内容
text2 = "apple (red), banana (yellow), cherry (red)"
result2 = re.findall(r'\((.*?)\)', text2)
print(f"示例2结果: {result2}")  # 输出: ['red', 'yellow', 'red']

# 示例3:有多个捕获组,提取所有水果和颜色
text3 = "apple=red, banana=yellow, cherry=red"
result3 = re.findall(r'(\w+)=(\w+)', text3)
print(f"示例3结果: {result3}")  # 输出: [('apple', 'red'), ('banana', 'yellow'), ('cherry', 'red')]

# 示例4:使用标志参数忽略大小写
text4 = "Hello World, HELLO PYTHON"
result4 = re.findall(r'hello', text4, re.IGNORECASE)
print(f"示例4结果: {result4}")  # 输出: ['Hello', 'HELLO']

# 示例5:多行匹配模式
text5 = "Line 1\nLine 2\nLine 3"
result5 = re.findall(r'^Line \d', text5, re.MULTILINE)
print(f"示例5结果: {result5}")  # 输出: ['Line 1', 'Line 2', 'Line 3']    

注意事项

贪婪匹配与非贪婪匹配:

正则表达式中的 * 和 + 是贪婪的,会尽可能多地匹配字符

在量词后加 ? 可以使其变为非贪婪模式,尽可能少地匹配字符

边界处理

使用 ^ 和 $ 匹配字符串的开始和结束

使用 \b 匹配单词边界,避免部分匹配

转义字符

正则表达式中的特殊字符(如 .*+ 等)需要使用 \ 进行转义

可以使用原始字符串(在字符串前加 r)避免 Python 字符串转义的干扰

捕获组的使用

不需要提取内容时,可以使用非捕获组 (?:...) 提高效率

多个捕获组的结果按元组形式返回,注意顺序和数量

常见应用场景

  • 提取数据:从日志、HTML、JSON 等文本中提取关键信息
  • 数据验证:检查输入是否符合特定格式(如邮箱、手机号)
  • 文本分割:根据特定模式分割字符串
  • 替换操作:配合 re.sub() 进行复杂的文本替换

 4.2.re.sub("正则表达式","替换的字符串","原字符串","可选参数"):用于在字符串中替换所有匹配正则表达式的子串。这是处理文本数据时非常常用的功能,比如清洗数据、格式化文本等。

re.sub(pattern, repl, string, count=0, flags=0)
  • pattern正则表达式模式字符串,用于匹配需要替换的子串
  • repl替换字符串或可调用对象,用于替换匹配到的内容
  • string需要被处理的原始字符串
  • count可选参数,指定最多替换次数(默认为 0,表示全部替换)
  • flags可选参数,用于指定匹配模式(如忽略大小写、多行匹配等)

替换字符串的特殊语法

当 repl 是字符串时,可以使用以下特殊语法:

  • \g<group>:引用指定编号或名称的捕获组
  • \1\2, ...:引用第 1、2 等捕获组
  • \g<0>:引用整个匹配的子串
import re

# 示例1:简单替换,将所有数字替换为X
text1 = "hello 123 world 456"
result1 = re.sub(r'\d', 'X', text1)
print(f"示例1结果: {result1}")  # 输出: hello XXX world XXX

# 示例2:使用捕获组,交换姓名顺序
text2 = "Zhang San, Li Si, Wang Wu"
result2 = re.sub(r'(\w+) (\w+)', r'\2 \1', text2)
print(f"示例2结果: {result2}")  # 输出: San Zhang, Si Li, Wu Wang

# 示例3:使用命名捕获组,格式化日期
text3 = "2023-10-01"
result3 = re.sub(r'(?P<year>\d{4})-(?P<month>\d{2})-(?P<day>\d{2})', 
                 r'\g<month>/\g<day>/\g<year>', text3)
print(f"示例3结果: {result3}")  # 输出: 10/01/2023

# 示例4:使用函数进行动态替换,将数字乘以2
def double_number(match):
    return str(int(match.group(0)) * 2)

text4 = "A1B2C3"
result4 = re.sub(r'\d', double_number, text4)
print(f"示例4结果: {result4}")  # 输出: A2B4C6

# 示例5:限制替换次数
text5 = "apple apple apple"
result5 = re.sub(r'apple', 'orange', text5, count=2)
print(f"示例5结果: {result5}")  # 输出: orange orange apple

# 示例6:忽略大小写替换
text6 = "Hello World, hello Python"
result6 = re.sub(r'hello', 'hi', text6, flags=re.IGNORECASE)
print(f"示例6结果: {result6}")  # 输出: hi World, hi Python    

使用可调用对象作为替换参数

当 repl 是一个函数时,每次匹配成功后,该函数会被调用,传入匹配对象(match)作为参数,并返回替换字符串。这在需要根据匹配内容进行动态替换时非常有用。

 

例如,将字符串中的所有数字替换为其平方值:
def square_number(match):
    num = int(match.group(0))
    return str(num ** 2)

text = "a1b2c3"
result = re.sub(r'\d', square_number, text)
print(result)  # 输出: a1b4c9

常见应用场景

  • 数据清洗:移除或替换特殊字符、多余空格等
  • 文本格式化:将日期、电话号码等转换为统一格式
  • 敏感信息处理:替换或掩码化敏感数据(如身份证号、信用卡号)
  • 语法转换:在不同编程语言或格式之间转换语法
  • 模板替换:根据规则动态生成文本内容

注意事项

  • 正则表达式转义

正则表达式中的特殊字符需要使用 \ 进行转义

可以使用原始字符串(如 r'\d+')避免 Python 字符串转义的干扰

  • 捕获组的使用

使用 () 定义捕获组,可以在替换字符串中引用

使用非捕获组 (?:...) 避免不必要的捕获,提高效率

  • 替换字符串中的反斜杠

替换字符串中的反斜杠需要转义(如 \\ 表示一个反斜杠)

  • 可以使用原始字符串简化处理(如 r'\\section'
通过合理使用 re.sub(),可以高效地完成各种复杂的文本替换任务。

4.3.re.finditer("正则表达式","原字符串","可选参数"):函数来查找字符串中的所有数字。这个函数与 re.findall() 类似,但返回的是一个迭代器(iterator),每次迭代返回一个匹配对象(match object)。

re.finditer(pattern, string, flags=0)
  • group(0):返回整个匹配的字符串
  • start():返回匹配开始的位置
  • end():返回匹配结束的位置
  • span():返回一个元组 (start, end)
result = re.finditer(r"\d+", "我今年42岁,我有2000000元")
for item in result:
    print(item.group(0))
#输出结果:42    2000000
import re

# 使用 re.finditer() 查找所有数字
result = re.finditer(r"\d+", "我今年42岁,我有2000000元")

# 遍历迭代器并打印匹配结果
for match in result:
    print(f"匹配到的数字: {match.group(0)}")
    print(f"位置: {match.start()} - {match.end()}")
    print(f"原始字符串中的片段: {match.string[match.start():match.end()]}")
    print("-" * 20)

"""输出结果:
匹配到的数字: 42
位置: 3 - 5
原始字符串中的片段: 42
--------------------
匹配到的数字: 2000000
位置: 8 - 15
原始字符串中的片段: 2000000
--------------------"""

与findall()的对比

  • re.findall():返回所有匹配的字符串列表
  • re.finditer():返回一个迭代器,每次迭代返回一个匹配对象
当处理大量数据时,re.finditer() 更节省内存,因为它不需要一次性存储所有匹配结果。
4.4.re.search("正则表达式","原字符串","可选参数"):用于在字符串中搜索第一个匹配正则表达式的位置。与 re.findall() 和 re.finditer() 不同,re.search() 只返回第一个匹配结果,如果没有找到则返回 None
re.search(pattern, string, flags=0)
  • pattern:正则表达式模式字符串
  • string:需要被搜索的原始字符串
  • flags:可选参数,用于指定匹配模式(如忽略大小写、多行匹配等)

返回值

如果找到匹配项,返回一个 匹配对象(match object),否则返回 None。匹配对象包含以下常用方法和属性:
  • group(0):返回整个匹配的字符串
  • group(1), group(2), ...:返回第 1、2 等捕获组的内容
  • start():返回匹配开始的位置
  • end():返回匹配结束的位置
  • span():返回一个元组 (start, end)
import re

# 示例1:查找第一个数字
text1 = "hello 123 world 456"
match1 = re.search(r'\d+', text1)
if match1:
    print(f"找到数字: {match1.group(0)}")  # 输出: 123
    print(f"位置: {match1.start()} - {match1.end()}")  # 输出: 6 - 9
else:
    print("未找到匹配项")

# 示例2:使用捕获组提取信息
text2 = "我的邮箱是 example@domain.com,请查收"
match2 = re.search(r'(\w+)@(\w+\.\w+)', text2)
if match2:
    print(f"完整邮箱: {match2.group(0)}")  # 输出: example@domain.com
    print(f"用户名: {match2.group(1)}")  # 输出: example
    print(f"域名: {match2.group(2)}")  # 输出: domain.com

# 示例3:忽略大小写查找
text3 = "Hello World"
match3 = re.search(r'world', text3, re.IGNORECASE)
if match3:
    print(f"找到匹配: {match3.group(0)}")  # 输出: World

# 示例4:多行模式下的搜索
text4 = "第一行\n第二行\n第三行"
match4 = re.search(r'^第(.*)行$', text4, re.MULTILINE)
if match4:
    print(f"匹配行内容: {match4.group(0)}")  # 输出: 第一行
    print(f"捕获内容: {match4.group(1)}")  # 输出: 一    

常见应用场景

  • 检查字符串中是否存在特定模式:
import re

# 通过用户输入获取密码
password = input("请输入密码:")

if re.search(r'^[a-zA-Z0-9]+$', password):
    print("密码格式有效")
else:
    print("密码格式无效,只能包含字母和数字")
  • 提取特定信息:
import re

# 定义要搜索的文本
text = "这是一个测试,商品价格:99.99元"

match = re.search(r'价格:(\d+\.\d+)元', text)
if match:
    price = float(match.group(1))
    print(f"商品价格: {price} 元")
else:
    print("未找到价格信息")
#输出结果:商品价格: 99.99 元

#从用户输入获取 text
import re

# 从用户输入获取文本
text = input("请输入包含价格的文本(例如:商品价格:99.99元):")

# 修改正则表达式,允许匹配整数或小数价格
match = re.search(r'价格:(\d+(?:\.\d+)?)元', text)

if match:
    try:
        price = float(match.group(1))
        print(f"成功提取价格: {price} 元")
    except ValueError:
        print("错误:无法将提取的内容转换为价格")
else:
    print("未找到符合格式的价格信息")
    print("提示:请确保输入的文本包含如'价格:99.99元'或'价格:100元'的格式")

import re

# 从文件读取文本
try:
    with open('input.txt', 'r', encoding='utf-8') as file:
        text = file.read()
except FileNotFoundError:
    print("文件未找到,请检查文件路径")
    exit()

match = re.search(r'价格:(\d+\.\d+)元', text)
if match:
    price = float(match.group(1))
    print(f"商品价格: {price} 元")
else:
    print("未找到价格信息")
  • 验证输入格式:
import re

# 通过用户输入获取电话号码
phone_number = input("请输入电话号码:")

if re.search(r'^\d{11}$', phone_number):
    print("手机号码格式正确")
else:
    print("手机号码格式错误,必须是11位数字"

注意事项

  • 匹配对象的判断:
    • 由于 re.search() 可能返回 None,在使用前应先检查是否为 None
    • 示例:match = re.search(pattern, string) → if match:
  • 捕获组的编号:
    • 捕获组从 1 开始编号,group(0) 表示整个匹配
    • 嵌套捕获组的编号按照左括号出现的顺序确定
  • 正则表达式的边界:
    • 使用 ^ 和 $ 来限制匹配的范围
    • 例如:re.search(r'^hello$', text) 要求整个字符串必须是 "hello"
  • 通过合理使用 re.search(),可以高效地完成各种文本搜索和提取任务。

4.5.re.match("正则表达式","原字符串","可选参数"):主要用于在字符串的起始位置对正则表达式进行匹配。要是匹配成功,它会返回一个匹配对象;要是匹配失败,则返回None

re.match(pattern, string, flags=0)

参数说明

  • pattern:此为必选参数,代表的是要进行匹配的正则表达式。
  • string:同样是必选参数,指的是需要被匹配的字符串。
  • flags:这是可选参数,用于指定匹配模式,像忽略大小写、多行匹配等。该参数的值可以是多个标志的按位或运算结果(例如re.I | re.M)。

核心特性

  • 仅匹配开头re.match只会从字符串的起始位置开始尝试匹配。若起始位置不符合正则表达式,即便后续部分有符合的内容,也会匹配失败。
  • 返回匹配对象:一旦匹配成功,就会返回一个包含匹配信息的对象。借助这个对象,你可以获取匹配的内容、位置等信息。

匹配对象的常用方法

re.match返回匹配对象后,可以使用以下方法获取匹配信息:
  • group([group1, ...])用于获取一个或多个分组的匹配内容。若不指定参数,则返回整个匹配结果。
  • groups()返回一个包含所有分组匹配结果的元组。
  • start([group])返回指定分组匹配的起始位置。若未指定分组,则返回整个匹配的起始位置。
  • end([group])返回指定分组匹配的结束位置(不包含该位置的字符)。
  • span([group])返回一个元组,包含指定分组匹配的起始和结束位置。

re.matchre.search的区别

  • re.match只在字符串的起始位置进行匹配。
  • re.search会在整个字符串中进行搜索匹配,只要找到第一个匹配项就会返回。
import re

string = "Hello, world!"
match = re.match("world", string)  # 匹配失败,返回None
search = re.search("world", string)  # 匹配成功,返回匹配对象

print(match)  # 输出:None
print(search.group())  # 输出:world

注意事项

  • 正则表达式的转义:在正则表达式里,像.*+这类字符有特殊含义。如果要匹配它们的字面意思,需要使用反斜杠\进行转义。
  • 原始字符串:编写正则表达式时,建议使用原始字符串(即在字符串前加r),这样可以避免 Python 本身的转义问题。例如,匹配反斜杠本身时,使用r'\\''\\\\'更清晰。

4.6.re.compile("正则表达式","可选参数"):用于将正则表达式模式编译为一个正则表达式对象。编译后的对象可以多次使用,叫做预加载,提高匹配效率,尤其在需要重复匹配相同模式的场景下性能更优。

re.compile(pattern, flags=0)
  • pattern要编译的正则表达式模式。
  • flags可选参数,用于指定正则表达式的匹配模式,如忽略大小写、多行匹配等。
import re

# 编译正则表达式
pattern = re.compile(r'\d+')  # 匹配一个或多个数字

# 使用编译后的对象进行匹配
text = "Hello 123 World 456"

# 查找所有匹配
matches = pattern.findall(text)
print(f"所有匹配: {matches}")  # 输出: ['123', '456']

# 使用 search 方法查找第一个匹配
match = pattern.search(text)
if match:
    print(f"第一个匹配: {match.group(0)}, 位置: {match.start()}")  # 输出: 第一个匹配: 123, 位置: 6

与直接使用模块函数的比较

直接使用 re 模块的函数如 re.findall()re.search()时,Python 会在内部自动编译正则表达式。而使用 re.compile 可以显式编译正则表达式,避免重复编译,提高性能。

不使用 compile(自动编译)

import re
text = "Hello 123 World 456"
matches = re.findall(r'\d+', text)  # 每次调用都编译一次正则表达式
print(matches)#输出:['123','456']

使用 compile(预编译)

import re
pattern = re.compile(r'\d+')  # 编译一次
matches1 = pattern.findall("Hello 123")  # 直接使用已编译的对象
matches2 = pattern.findall("World 456")  # 再次使用,无需重新编译
print(matches1)  # 输出: ['123']
print(matches2)  # 输出: ['456']

编译后的对象支持的方法

编译后的正则表达式对象支持与 re 模块相同的方法,例如:
  • pattern.match(string[, pos[, endpos]])从字符串的起始位置开始匹配。
  • pattern.search(string[, pos[, endpos]])在字符串中搜索第一个匹配项。
  • pattern.findall(string[, pos[, endpos]])返回所有匹配的字符串列表。
  • pattern.finditer(string[, pos[, endpos]])返回匹配对象的迭代器。
  • pattern.sub(repl, string[, count])替换匹配的子串。

性能优化场景

import re

# 预编译正则表达式
email_pattern = re.compile(r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b')

# 待验证的邮箱列表
emails = ["test@example.com", "invalid.email", "user.name+tag@domain.co.uk"]

# 循环验证邮箱
for email in emails:
    if email_pattern.match(email):
        print(f"{email} 是有效的邮箱地址")
    else:
        print(f"{email} 不是有效的邮箱地址")

编译标志(Flags)

re.compile 支持与 re 模块函数相同的标志参数,例如:
  • re.IGNORECASE 或 re.I忽略大小写。
  • re.MULTILINE 或 re.M多行匹配模式。
  • re.DOTALL 或 re.S使 . 匹配包括换行符在内的所有字符。
import re  # 添加模块导入语句
pattern = re.compile(r'hello', re.IGNORECASE)  # 忽略大小写的匹配
print(pattern.search("Hello World").group(0))  # 输出: Hello

总之,re.compile 是优化正则表达式性能的重要工具,尤其适用于需要重复使用同一模式的场景。

4.7.re.S: 匹配模式修饰符,也可以写作 re.DOTALL

它的作用是改变 .(点号)的匹配行为:
    • 默认情况下,. 可以匹配除换行符 \n 之外的任意字符
    • 当使用 re.S 模式后,. 可以匹配包括换行符 \n 在内的任意字符
import re

text = "hello\nworld"

# 不使用 re.S,无法匹配换行符
print(re.findall("hello.world", text))  # 输出: []

# 使用 re.S,点号可以匹配换行符
print(re.findall("hello.world", text, re.S))  # 输出: ['hello\nworld']

这个修饰符在需要匹配多行文本时非常有用,比如处理包含换行的字符串或多行文本内容。re.DOTALL 是更具描述性的别名,功能与 re.S 完全相同。

 5.CSV文件写入(内置模块)

5.1. 基础写入方法:使用csv.writer可以写入列表数据到 CSV 文件:

import csv

# 数据
data = [
    ["姓名", "年龄", "城市"],
    ["张三", 25, "北京"],
    ["李四", 30, "上海"],
    ["王五", 28, "广州"]
]

# 写入CSV文件
with open("people.csv", "w", newline="", encoding="utf-8") as file:
    writer = csv.writer(file)
    # 写入多行
    writer.writerows(data)
    # 也可以单行写入
    # writer.writerow(["赵六", 35, "深圳"])

5.2. 字典方式写入:如果数据以字典形式存在,可以使用csv.DictWriter

import csv

# 数据
data = [
    {"姓名": "张三", "年龄": 25, "城市": "北京"},
    {"姓名": "李四", "年龄": 30, "城市": "上海"},
    {"姓名": "王五", "年龄": 28, "城市": "广州"}
]

# 字段名(表头)
fieldnames = ["姓名", "年龄", "城市"]

with open("people_dict.csv", "w", newline="", encoding="utf-8") as file:
    writer = csv.DictWriter(file, fieldnames=fieldnames)
    # 写入表头
    writer.writeheader()
    # 写入数据
    writer.writerows(data)
    # 单行写入
    # writer.writerow({"姓名": "赵六", "年龄": 35, "城市": "深圳"})

5.3. 自定义分隔符:默认情况下,CSV 使用逗号作为分隔符,你可以通过delimiter参数自定义

import csv

with open("custom_sep.csv", "w", newline="", encoding="utf-8") as file:
    # 使用制表符作为分隔符(类似TSV文件)
    writer = csv.writer(file, delimiter="\t")
    writer.writerows([["a", "b", "c"], ["1", "2", "3"]])

注意事项:

  • 使用newline=""参数可以避免在 Windows 系统中出现多余的空行
  • 指定encoding="utf-8"可以确保中文正常显示
  • 写入完成后,文件会自动关闭(因为使用了with语句)
这些方法可以满足大多数 CSV 文件写入需求,根据你的数据格式选择合适的方式即可。

5.4.enumerate(迭代对象,[0]:):枚举对象转换:用于将一个可迭代对象(如列表、元组、字符串等)转换为一个枚举对象,在遍历过程中同时返回「元素的索引」和「元素的值」

enumerate(iterable, start=0)
  • iterable需要遍历的可迭代对象(如列表、字符串等)
  • start可选参数,指定索引的起始值,默认从 0 开始

作用和优势:

当你需要遍历一个序列,同时又需要知道每个元素在序列中的位置(索引)时,enumerate() 可以简化代码。

例如,不使用 enumerate() 时,可能需要这样写:

fruits = ['apple', 'banana', 'cherry']
index = 0
for fruit in fruits:
    print(f"Index: {index}, Fruit: {fruit}")
    index += 1  # 手动维护索引

而使用 enumerate() 后,代码更简洁:

fruits = ['apple', 'banana', 'cherry']
for index, fruit in enumerate(fruits):
    print(f"Index: {index}, Fruit: {fruit}")
'''
输出结果:
Index: 1, Fruit: apple
Index: 2, Fruit: banana
Index: 3, Fruit: cherry
'''

如果从第二个数据(banana)开始迭代,可以先对列表进行切片处理,从索引 1 开始(因为 Python 列表索引从 0 开始,第二个元素的索引是 1),再传入enumerate()函数中。

fruits = ['apple', 'banana', 'cherry']

# 从索引1开始切片,只处理'banana'和'cherry'
for index, fruit in enumerate(fruits[1:]):
    # 注意:此时的index是相对于切片后列表的索引(从0开始)
    # 如果需要显示原列表中的真实索引,需要加上偏移量1
    print(f"原索引: {index + 1}, Fruit: {fruit}")
'''
输出结果:
原索引: 1, Fruit: banana
原索引: 2, Fruit: cherry
'''

简单说,enumerate() 的核心作用就是在遍历过程中同时获取元素及其位置信息,让代码更简洁高效。

自动爬虫

1.selemium:是一套用于Web应用程序自动化测试的工具集,同时也广泛用于网络数据爬取(合规场景下)、自动化操作浏览器(如自动填写表单、模拟用户交互等)。它的核心能力是直接控制浏览器(如 Chrome、Firefox),模拟真实用户的操作行为(点击、输入、跳转等),是 Web 自动化领域最流行的工具之一。

一、Selenium 核心组件

Selenium 并非单一工具,而是由多个组件构成,不同组件适用于不同场景:
组件名称核心作用适用场景
Selenium WebDriver 核心组件,提供编程语言接口(如 Python、Java),直接控制浏览器执行操作 主流自动化场景(测试、爬虫)
Selenium IDE 浏览器插件(支持 Chrome/Firefox),可视化录制 / 回放用户操作,无需写代码 快速生成简单自动化脚本
Selenium Grid 分布式测试工具,支持在多台机器、多个浏览器上同时执行自动化脚本 大规模测试任务(提高效率)

二、Selenium 核心原理

Selenium 实现浏览器控制的核心是 “WebDriver + 浏览器驱动” 的协作模式,流程如下:
  • 开发者编写代码:通过 Python/Java 等语言调用 Selenium WebDriver 的 API(如 click()send_keys())。
  • WebDriver 转发指令:WebDriver 将代码中的操作指令(如 “点击按钮”)转换为浏览器能识别的协议(如 W3C WebDriver 协议)。
  • 浏览器驱动执行:需提前安装对应浏览器的 “驱动程序”(如 Chrome 对应 chromedriver),驱动接收指令后,控制浏览器执行具体操作。
  • 浏览器返回结果:操作结果(如页面渲染、元素状态变化)通过驱动反馈给 WebDriver,最终可在代码中获取(如获取页面源码、元素文本)。
简单来说:代码 → WebDriver → 浏览器驱动 → 浏览器,形成一套完整的 “指令下发 - 执行 - 反馈” 闭环。

三、Selenium 关键特性

  • 多浏览器支持
    兼容主流浏览器,无需修改核心代码,只需更换对应的浏览器驱动即可:
    • Chrome(需 chromedriver
    • Firefox(需 geckodriver
    • Edge(需 msedgedriver
    • Safari(macOS 自带驱动,需手动开启)
  • 多编程语言支持
    提供丰富的语言绑定,开发者可使用熟悉的语言编写脚本:
    • 主流语言:Python、Java、JavaScript(Node.js)、C#、Ruby
    • 小众语言:Perl、PHP
  • 模拟真实用户操作
    支持几乎所有用户在浏览器上的操作,覆盖复杂交互场景:
    • 基础操作:点击click()、输入文本send_keys()、清空内容clear()、刷新页面refresh()
    • 进阶操作:鼠标悬停ActionChains、下拉滚动execute_script() 执行 JS)、切换窗口 /iframeswitch_to、处理弹窗alert
    • 等待机制:支持 “显式等待”(等待特定元素加载完成)和 “隐式等待”(全局等待超时),解决页面异步加载导致的元素定位失败问题。
  • 元素定位能力
    提供 8 种主流元素定位方式,可精准定位页面中的任何元素(如按钮、输入框、列表)
定位方式语法(Python 示例)说明(基于 HTML 属性)
ID 定位 driver.find_element(By.ID, "username") 通过元素的 id 属性定位(唯一优先)
Name 定位 driver.find_element(By.NAME, "password") 通过元素的 name 属性定位
Class Name 定位 driver.find_element(By.CLASS_NAME, "btn") 通过元素的 class 属性定位
Tag Name 定位 driver.find_element(By.TAG_NAME, "input") 通过 HTML 标签名定位(如 input
Link Text 定位 driver.find_element(By.LINK_TEXT, "登录") 定位超链接(完全匹配文本)
Partial Link Text 定位 driver.find_element(By.PARTIAL_LINK_TEXT, "登") 定位超链接(模糊匹配文本)
XPath 定位 driver.find_element(By.XPATH, "//input[@id='username']") 基于 XML 路径语法,支持复杂定位(最灵活)
CSS Selector 定位 driver.find_element(By.CSS_SELECTOR, "#username") 基于 CSS 选择器,定位效率比 XPath 高

四、Python 环境下的 Selenium 基础使用

以最常用的 Python + Chrome 组合为例,演示核心流程:

①.环境准备

步骤 1:安装 Selenium 库

通过 pip 安装 Python 版本的 Selenium:
pip install selenium

步骤 2:下载浏览器驱动

    • 查看本地 Chrome 版本:打开 Chrome → 右上角三个点 → 帮助 → 关于 Google Chrome(如版本 141.0.7390.54)。
    • 官方地址:ChromeDriver - WebDriver for Chrome
      (注意:驱动版本需与 Chrome 版本完全匹配,或兼容匹配,否则无法启动浏览器)。
    • 配置驱动:将下载的 chromedriver.exe(Windows)或 chromedriver(macOS/Linux)放在 Python 安装目录,或添加到系统环境变量。

image

image

②. 基础示例:打开网页并操作

下面代码实现 “打开百度 → 输入关键词 → 点击搜索 → 获取搜索结果” 的自动化流程:
#1.导包
from selenium import webdriver
import time
from selenium.webdriver.common.by import By #定义类
from selenium.webdriver.common.keys import Keys #定义键盘类
web = webdriver.Chrome()
web.maximize_window() #窗口最大化
web.get('https://www.baidu.com/')
print(web.title) #获取百度网站标题 百度一下,你就知道
ipt = web.find_element(By.ID,"chat-textarea")
ipt.send_keys('美女',Keys.ENTER) #自动回车
time.sleep(5)
web.quit()
#2.导包
from selenium.webdriver import Chrome
import time
from selenium.webdriver.common.by import By #定义类
from selenium.webdriver.common.keys import Keys #定义键盘类
web = webdriver.Chrome()
web.maximize_window() #窗口最大化
web.get('https://www.baidu.com/')
ipt = web.find_element(By.ID,"chat-textarea")
ipt.send_keys('美女')
web.find_element(By.ID,"chat-submit-button").click()#自动点击
time.sleep(5)
web.quit()

③.其它功能

web.minimize_window()  # 窗口最小化
web.set_window_size(1000, 500) #设置固定的窗口大小
ipt = web.find_element(By.CLASS_NAME, 's_ipt')  # 如果需要用CLASS_NAME 需要注意 只适用于class值只有一个的情况
ipt = web.find_element(By.XPATH, '//*[@id="chat-textarea"]') #Xpath正则表达式
web.find_element(By.XPATH, '//div[@class="c-container"]').screenshot('截图.png')  # screenshot截图方法 传参截图存储路径
web.save_screenshot('浏览器窗口.png')  # 截图浏览器内部窗口图片
ps = web.page_source  # 获取元素面板的html结构数据
b = web.find_element(By.LINK_TEXT, '贴吧')  # 通过文本值去定位 文本值需要是标签里面的全部文本
print(b.text)  # 获取标签文本内容
print(b.get_attribute('href'))  # 获取属性值 传参属性名
print(b.location)  # 获取标签左上角点相对于浏览器窗口原点的 坐标  {'x': 191, 'y': 19}
print(b.size)  # 标签宽高尺寸  {'height': 23, 'width': 26}
b = web.find_element(By.PARTIAL_LINK_TEXT, '')  # 通过文本值去定位 文本值可以是部分文本
# print(b)

④.规避检测

from selenium import webdriver
from selenium.webdriver.common.by import By  # 定位类
import time

web = webdriver.Chrome() # 实例化浏览器对象
web.maximize_window()  # 窗口最大化
web.implicitly_wait(3)  # 隐式等待  轮询
web.get('https://antispider1.scrape.center/')
time.sleep(1900)
web.quit()  # 关闭浏览器

显示结果:

image

 添加规避检测代码后会正常显示

from selenium import webdriver
from selenium.webdriver.common.by import By  # 定位类
import time

web = webdriver.Chrome() # 实例化浏览器对象
web.maximize_window()  # 窗口最大化
web.implicitly_wait(3)  # 隐式等待  轮询
web.execute_cdp_cmd("Page.addScriptToEvaluateOnNewDocument", {
             "source": """
             Object.defineProperty(navigator, 'webdriver', {
             get: () => false
             })"""
         })  # 规避检测  当获取navigator下面的webdriver的时候 篡改为false
web.get('https://antispider1.scrape.center/')
time.sleep(1900)
web.quit()  # 关闭浏览器

image

 ⑤.浏览器自动关闭与保持打开的3种方案

from selenium import webdriver
from selenium.webdriver.chrome.options import Options  # 用于detach配置
import time
# ==============================
# 方案1:固定时间等待(脚本暂停N秒后关闭,适合自动测试)
# ==============================
print("=== 运行方案1:固定时间等待 ===")
# 初始化浏览器
web1 = webdriver.Chrome()
web1.maximize_window()
web1.get('https://www.baidu.com/')
# 关键:固定等待10秒(可修改数字调整等待时长)
time.sleep(10)
# 等待结束后关闭当前浏览器(避免多窗口干扰)
web1.quit()
print("方案1执行完毕,浏览器已关闭\n")

# ==============================
# 方案2:用户输入等待(按回车键才关闭,适合手动操作浏览器)
# ==============================
print("=== 运行方案2:用户输入等待 ===")
# 初始化浏览器
web2 = webdriver.Chrome()
web2.maximize_window()
web2.get('https://www.baidu.com/')
# 关键:等待用户在控制台按回车键(按前浏览器保持打开)
input("方案2提示:请在控制台按【回车键】关闭浏览器...")
# 用户按回车后关闭浏览器
web2.quit()
print("方案2执行完毕,浏览器已关闭\n")

# ==============================
# 方案3:detach配置(脚本结束后浏览器仍保持打开,需手动关闭)
# ==============================
print("=== 运行方案3:detach配置(浏览器将保持打开) ===")
# 1. 创建Chrome配置对象,添加“进程分离”参数
chrome_options = Options()
chrome_options.add_experimental_option("detach", True)  # 核心配置:浏览器不随脚本关闭
# 2. 初始化浏览器时传入配置
web3 = webdriver.Chrome(options=chrome_options)
web3.maximize_window()
web3.get('https://www.baidu.com/')
print("方案3执行完毕,浏览器已启动(需手动点击窗口右上角关闭)")

 

五、Selenium 的应用场景

  • Web 自动化测试
    这是 Selenium 的核心场景:开发者 / 测试工程师编写脚本,自动验证 Web 应用的功能(如登录、支付、表单提交),替代人工测试,提高效率并减少重复工作。例如:验证 “用户输入错误密码时,登录按钮是否提示错误”。
  • 合规网络爬虫
    对于动态渲染的网页(如 JavaScript 加载的内容,普通爬虫无法获取),Selenium 可模拟浏览器加载完整页面,再提取数据。注意:需遵守网站的 robots.txt 协议,避免非法爬取。
  • 浏览器自动化操作
    • 自动填写重复性表单(如每日打卡、报表提交);
    • 自动监控网页内容变化(如商品价格、公告更新);
    • 批量执行浏览器操作(如批量下载文件、批量截图)。

六、常见问题与注意事项

①.驱动版本不匹配
报错表现:SessionNotCreatedException
解决:确保浏览器版本与驱动版本完全一致(或查看驱动官网的 “兼容版本说明”)。

②.元素定位失败
常见原因:

  • 页面未加载完成就定位元素:使用 “显式等待”(WebDriverWait)替代 “强制等待”(time.sleep());
  • 元素在 iframe 中:需先通过 driver.switch_to.frame(iframe元素) 切换到 iframe,再定位元素;
  • 元素是动态生成的(如点击后才出现):确保操作顺序正确,先触发元素生成,再定位。

③.浏览器无头模式
若不需要可视化浏览器窗口(如服务器环境),可开启 “无头模式”(Headless),减少资源占用:

from selenium.webdriver.chrome.options import Options

chrome_options = Options()
chrome_options.add_argument("--headless=new")  # 开启无头模式(Chrome 112+ 推荐)
driver = webdriver.Chrome(options=chrome_options)

七、Selenium 与其他工具的对比

工具核心优势核心劣势适用场景
Selenium 模拟真实浏览器,支持复杂交互,多语言 / 浏览器 资源占用高,速度较慢 动态页面、复杂交互(测试 / 爬虫)
BeautifulSoup 轻量,解析 HTML 速度快 无法处理 JavaScript 动态内容,无自动化能力 静态页面数据提取
Playwright 微软开发,内置驱动(无需手动下载),速度快 生态不如 Selenium 成熟 新一代 Web 自动化(替代 Selenium 的选择)
总之,SeleniumWeb 自动化领域的 “瑞士军刀”,灵活性高、功能全面,尤其适合需要模拟真实用户交互的场景。掌握它需要熟悉 “元素定位”“等待机制” 这两个核心点,再结合具体场景扩展即可。
Beautiful Soup(简称 BS4)爬虫
BS4是 Python 中一个强大的 HTML 和 XML 解析库,它能够从网页中提取数据,处理不规范标记(“-tag soup”),并将复杂的 HTML 文档转换为一个易于遍历的树形结构,方便开发者快速定位和提取所需信息。

核心功能

  1. 解析 HTML/XML 文档
    支持多种解析器(如 Python 内置的 html.parserlxmlhtml5lib 等),能处理不完整或格式混乱的标记。
  2. 遍历和搜索节点
    提供直观的 API 用于查找、遍历 HTML 元素(如标签、属性、文本)。
  3. 修改文档结构
    支持添加、删除、修改标签或属性。
 BS4的安装方法:
pip install beautifulsoup4

通常搭配解析器 lxml(速度快,支持 XML)或 html5lib(兼容性好)使用:

pip install lxml  # 推荐
#
pip install html5lib

实例化Beautiful Soup对象:

#导包
from bs4 import BeautifulSoup
# 解析字符串#本地Html文档加载
#将此Html文档保存为Html文件到本地
"""
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>测试BS4</title>
</head>
<body>
<div>
    <p>百里守约</p>
</div>
<div class="song">
    <p>李清照</p>
    <p>王安石</p>
    <p>苏轼</p>
    <p>柳宗元</p>
  <a href="https://song.com" title="赵匡胤" target="_self">
    <span>this is span </span>
    宋朝是最强大的王朝,不是军队的强大,而是经济很强大,国民都很有钱</a>
  <a href="" class="du">总为浮动能蔽日,长安不见使人愁</a>
  <img src="http://baidu.com/meiny.jpg" alt=""/>
</div>
<div class="tang">
<ul>
  <li><a href="http://baidu.com" title="qing">清明时节雨纷纷,路上行人欲断魂,借问酒家何处有,牧童遥指杏花村</a></li>
  <li><a href="http://163.com" title="qin">秦时明月汉时关,万里长征人未还,但使龙城飞将在,不叫胡马度阴山</a></li>
  <li><a href="http://126.com" title="qi">岐王宅里寻常见,崔九堂前几度闻,正是江南好风景,落花时节又逢春</a></li>
  <li><a href="http://sina.com" title="du">杜甫</a></li>
  <li><a href="http://baidu.com" title="du">杜牧</a></li>
<li><b>杜小月/b></li>
<li><i>度蜜月</i></li>
 <li><a href="http://haha.com" id="feng">凤凰台上凤凰游,凤去台空江自流,吴空花草埋幽径,晋代衣冠成古丘</a></li>
</ul>
</div>
</body>
</html>
 """ 
with open(r"E:\Python code\cls32爬虫\BS4\BS4.html", "r", encoding="utf-8") as fp: 
soup = BeautifulSoup(fp, 'lxml') print(soup)

定位查找解析标签和属性的方法:

print(soup.div)#标签定位
print(soup.find('div',class_ = 'tang')) """标签和属性的定位查找 
class是Python中的关键字,不能直接作为参数名使用。在Beautiful Soup 中,当需要按类名查找元素时,应该使用class_(后面加一个下划线)来代替class
"""
print(soup.find_all('a')) #定位查找所有包含的标签,返回的是列表。
print(soup.select('.tang'))#选择器定位查找。
print(soup.select('.tang > ul > li > a')[0])#选择器层级定位查找 >表示的是一个层级
print(soup.select('.tang > ul a')[0])#选择器层级定位查找 空格表示的是多个层级,也就是跨层级

获取标签之间的文本数据

print(soup.a.text) #
print(soup.a.get_text()) #可以获取某个标签中所有的文本内容
print(soup.a.string) #只可以获取该标签下真系的文本内容
print(soup.select('.tang > ul > li a')[0]['href']) #获取标签中的属性值的文本内容

 

 

 
 
 
 
 
 

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 
posted @ 2025-05-12 10:19  自学小天才  阅读(61)  评论(0)    收藏  举报