三、数据提取
利用 requests 可以获取网站页面数据,但是 requests 返回的数据中包含了一些冗余数据,我们需要在这些数据集中提取自己需要的信息。所以我们要学会在数据集中提取自己需要的数据。
需要掌握的知识点如下:
- json 数据提取
- jsonpath 语法
- 静态页面数据提取
- xpath语法
- bs4 模块使用
- 正则表达式的使用
1.数据提取的概念和数据分类
在爬虫爬取的数据中有很多不同类型的数据,我们需要了解数据的不同类型来有规律的提取和解析数据。
- 结构化数据:json、xml
- 处理方式:直接转化为 python 数据类型
- 非结构化数据:HTML
- 处理方式:正则表达式、xpath、bs4
1.1 结构化数据
json
xml
1.2 非结构化数据
2.结构化数据提取-json
什么是 json
JSON(JavaScript Object Notation)是一种轻量级的数据交换格式,它使得人们很容易的进行阅读和编写,同时也方便了机器进行解析和生成,适用于进行数据交互的场景,比如网站前端和后端之间的数据交互。
json 模块方法回顾
# json.dumps 实现python类型转化为json字符串
# indent实现缩进格式
# ensure_ascii=False实现让中文写入的时候保持为中文
json_str = json.dumps(mydict, indent=2, ensure_ascii=False)
# json.loads 实现json字符串转化为python的数据类型
my_dict = json.loads(json_str)
代码示例
import json
import requests
# 网站地址: http://www.cninfo.com.cn/new/commonUrl?url=disclosure/list/notice#szse
# 获取公告信息
url = 'http://www.cninfo.com.cn/new/disclosure'
# 定义请求头,模拟浏览器
headers = {
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",
}
# 定义表单数据
post_data = {
'column': 'szse_latest',
'pageNum': 1,
'pageSize': 30,
'sortName': '',
'sortType': '',
'clusterFlag': 'true'
}
# 请求json数据
r = requests.post(url, headers=headers, data=post_data)
# 解码
json_str = r.content.decode()
# 把json格式字符串转换成python对象
json_dict = json.loads(json_str)
print(json_dict)
print("\n\n")
# 还可以使用json()方法, 免去了自己手动编解码的步骤
print(r.json())
3. xpath语法
3.1 什么是xpath
Xpath(XML Path Language) 即 XML路径语言,在最初时它主要在 xml 文档中查找需要的信息,而现在它也适用于 HTML 文档的搜索。
W3School 官方文档:
http://www.w3school.com.cn/xpath/index.asp
xpath 可以很轻松的选择出想要的数据,提供了非常简单明了的路径选择表达式,几乎想要任何定位功能,xpath 都可以很轻松的实现。
所以在之后的静态网站数据提取中会经常使用 xpath 语法完成。
3.2 xpath 节点
每个标签我们都称之为 节点,其中最顶层的节点称为 根节点。
辅助工具
- Chrome 浏览器插件: XPath Helper
- Firefox 浏览器插件:XPath Finder
注意:这些工具是用来学习xpath语法的,当熟练掌握 xpath 的语法后就可以直接在代码中编写 xpath 而不一定非要用此工具。
3.3 语法规则
Xpath 使用路径表达式来选取文档中的节点或者节点集。这些路径表达式和我们在常规的电脑文件系统中看到的表达式非常相似。
表达式 | 描述 |
---|---|
nodename | 选中该元素 |
/ | 从根节点选取、或者是元素和元素间的过渡 |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑它们的位置 |
. | 选取当前节点 |
.. | 选取当前节点的父节点 |
@ | 选取属性 |
text() | 选取文本 |
路径表达式
路径表达式 | 结果 |
---|---|
/bookstore | 选取根元素 bookstore。注释:假如路径起始于斜杠(/),则此路径始终代表到某元素的绝对路径 |
bookstore/book | 选取属于 bookstore 之下的所有book元素 |
//book | 选取所有book子元素,而不管它们在文档中的位置 |
bookstore//book | 选择属于bookstore元素的后代的所有book元素,而不管它们位于bookstore之下的什么位置 |
//book/title/@lang | 选择所有的 book 下面的 title 中的 lang 属性的值 |
//book/title/text() | 选择所有的 book 下面的 title 的文本 |
查询特定节点
路径表达式 | 结果 |
---|---|
//title[@lang="eng"] | 选择 lang 属性值为 eng 的所有 title 元素 |
/bookstore/book[1] | 选取属于 bookstore 子元素的第1个book元素 |
/bookstore/book[last()] | 选取属于 bookstore 子元素的最后 1 个book元素 |
/bookstore/book[last()-1] | 选取属于 bookstore 子元素的倒数第 2 个book元素 |
/bookstore/book[position()>1] | 选择 bookstore 下面的 book 元素,从第 2 个开始选择 |
/bookstore/book[position()>1 and position()<4] | 选择 bookstore 下面的 book 元素,从第2个开始取到第 4 个元素 |
//book/title[text()='Harry Potter'] | 选择所有 book 下的 title 元素,仅仅选择文本为 Harry Poter的 title 元素 |
注意点:在 xpath 中,第一个元素的位置是 1 ,最后一个元素的位置是 last(),倒数第二个是last()-1
语法练习
接下来对豆瓣2电影 top250 的页面来练习上述语法:
https://movie.douban.com/top250
- 选择所有的 h1 下的文本
//h1/text()
- 获取电影信息的href信息
//div[@class='item']/div[1]/a/@href
- 获取电影的评价人数
//div[@class='star']/span[last()]/text()
总结
- xpath 的概述: Xpath(XML Path Language),解析查找提取信息的语言
- xml 是和服务器交互的数据格式和 json 的作用一致
- html 是浏览器解析标签数据显示给用户
- Xpath 的重点语法获取任意节点://
- Xpath 的重点语法根据数据获取节点:标签[@属性='值']
- Xpath 的获取节点属性值:@属性值
- Xpath 的获取节点文本值:text()
4.使用lxml模块中的xpath语法提取非结构化数据
前面学习的 xpath 知识主要的作用是:学会怎样通过 xpath 语法找到需要的数据,想要的代码中使用 xpath 进行处理,就需要学习另外一个新模块 lxml
4.1 模块的安装
pip install lxml -i https://pypi.tuna.tsinghua.edu.cn/simple
4.2 lxml 的使用
1.使用 lxml 转化为 Element 对象
from lxml import etree
text = ''' <div> <ul>
<li class="item-1"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul> </div> '''
# 利用etree.HTML,将字符串转化为Element对象,Element对象具有Xpath的方法
html=etree.HTML(text)
print(type(html))
#将Element对象转化为字符串
handled_html_str=etree.tostring(html).decode()
print(handled_html_str)
2.使用 lxml 中的 xpath 语法提取数据
提取 a标签 属性和文本
from lxml import etree
text = ''' <div> <ul>
<li class="item-1"><a href="link1.html">first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul> </div> '''
html=etree.HTML(text)
# 获取href的列表和title的列表
href_list=html.xpath("//li[@class='item-1']/a/@href")
title_list=html.xpath("//li[@class='item-1']/a/text()")
for title,href in zip(title_list,href_list):
item=dict()
item['title']=title
item['href']=href
print(item)
以上代码必须确保标签中的数据是一一对应的,如果有些标签中不存在指定的属性或文本则会匹配混乱。
输出结果为:
/Users/poppies/python_envs/base/bin/python3 /Users/poppies/Documents/spider_code/1.py
{'title': 'first item', 'href': 'link2.html'}
{'title': 'second item', 'href': 'link4.html'}
3. xpath 分次提取
前面我们取到属性,或者是文本的时候,返回字符串 但是如果我们取到的是一个节点,返回什么呢?
返回的是 element 对象,可以继续使用xpath方法
对此我们可以在后面的数据提取过程中:先根据某个xpath规则进行提取部分节点,然后再次使用xpath进行数据的提取
示例如下:
from lxml import etree
text = ''' <div> <ul>
<li class="item-1"><a>first item</a></li>
<li class="item-1"><a href="link2.html">second item</a></li>
<li class="item-inactive"><a href="link3.html">third item</a></li>
<li class="item-1"><a href="link4.html">fourth item</a></li>
<li class="item-0"><a href="link5.html">fifth item</a>
</ul> </div> '''
html = etree.HTML(text)
li_list = html.xpath("//li[@class='item-1']")
print(li_list)
# 在每一组中继续进行数据的提取
for li in li_list:
item = dict()
item["href"] = li.xpath("./a/@href")[0] if len(li.xpath("./a/@href")) > 0 else None
item["title"] = li.xpath("./a/text()")[0] if len(li.xpath("./a/text()")) > 0 else None
print(item)
总结:
- lxml 库的安装:pip install lxml
- lxml的导包:from lxml import etree
- lxml转换解析类型的方法:etree.HTML(text)
- lxml解析数据的方法:data.xpath("//div/text()")
- 需要注意 lxml 提取完毕数据的数据类型都是列表类型
- 如果数据比较复杂:先提取大节点,然后再进行小节点操作
5.案例:通过xpath提取豆瓣电影评论
爬取豆瓣电影的评论,地址链接:
https://movie.douban.com/subject/1292052/comments?status=P
代码示例:
import requests
from lxml import etree
# 1.通过requests发送请求获取豆瓣返回的内容
url='https://movie.douban.com/subject/1292052/comments?status=P'
headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36'}
res=requests.get(url,headers=headers)
# 2. 将返回的内容通过etree.HTML转换为Element对象
html=etree.HTML(res.text)
# 3. 对Element对象使用XPath提取数据
comment=html.xpath("//span[@class='short']//text()")
for i in comment:
print(i)
6.jsonpath 模块
JsonPath 是一种可以快速解析 json 数据的方式,JsonPath 对于JSON来说,相当于 XPath 对于XML,JsonPath 用来解析多层嵌套的json数据。
官网:https://goessner.net/articles/JsonPath/
想要在Python 编程语音中使用 JsonPath 对 json 数据快速提前,需要安装jsonpath模块。
pip install jsonpath -i https://pypi.tuna.tsinghua.edu.cn/simple
6.1 jsonpath 常用语法
代码示例
import jsonpath
info = {
"error_code": 0,
"stu_info": [
{
"id": 2059,
"name": "小白",
"sex": "男",
"age": 28,
"addr": "河南省济源市北海大道xx号",
"grade": "天蝎座",
"phone": "1837830xxxx",
"gold": 10896,
"info": {
"card": 12345678,
"bank_name": '中国银行'
}
},
{
"id": 2067,
"name": "小黑",
"sex": "男",
"age": 28,
"addr": "河南省济源市北海大道xx号",
"grade": "天蝎座",
"phone": "87654321",
"gold": 100
}
]
}
""" 未使用jsonpath时,提取dict时的方式"""
res=info['stu_info'][0]['name']
print(res)
res1=info['stu_info'][1]['name']
print(res1)
print("--------分割线--------")
"""使用jsonpath时,提取dict时的方式"""
res2=jsonpath.jsonpath(info,"$.stu_info[0].name")# $表示最外层的{}, . 表示子节点的意思
print(res2)
res3=jsonpath.jsonpath(info,"$.stu_info[1].name")
print(res3)
res4=jsonpath.jsonpath(info,'$..name') # 嵌套n层也能取得的所有学生姓名信息,$表示最外层的{},...表示模糊匹配
print(res4)
res5=jsonpaht.jsonpath(info,'$..bank_name')
print(res5)
6.2 jsonpath 对比 xpath:
Xpath | JSONPath | 结果 |
---|---|---|
/store/book/author | $.store.book[*].author | 商店里所有书籍的作者 |
//author | $..author | 所有作者 |
/store/* | $.store.* | 商店里的所有东西,都是一些书和一辆红色的自行车 |
/store//price | $.store..price | 商店里一切的价格 |
//book[3] | $..book[2] | 第三本书 |
//book[last()] | $..book[(@.length-1)] | 最后一本书 |
//book[position()<3] | $..book[0,1] $..book[:2] | 前两本书 |
//book[isbn] | $..book[?(@.isbn)] | 使用isbn number过滤所有书籍 |
//book[price<10] | $..book[?(@.price<10)] | 过滤所有便宜10以上的书籍 |
//* | $..* | XML文档中的所有元素。JSON结构的所有成员 |
练习代码:
import jsonpath
info = {
"store": {
"book": [
{"category": "reference",
"author": "Nigel Rees",
"title": "Sayings of the Century",
"price": 8.95
},
{"category": "fiction",
"author": "Evelyn Waugh",
"title": "Sword of Honour",
"price": 12.99
},
{"category": "fiction",
"author": "Herman Melville",
"title": "Moby Dick",
"isbn": "0-553-21311-3",
"price": 8.99
},
{"category": "fiction",
"author": "J. R. R. Tolkien",
"title": "The Lord of the Rings",
"isbn": "0-395-19395-8",
"price": 22.99
}
],
"bicycle": {
"color": "red",
"price": 19.95
}
}
}
# 1. 提取第1本书的title
print('提取第1本书的title')
ret1=jsonpath.jsonpath(info,"$.store.book[0].title")
print(ret1)
ret1=jsonpath.jsonpath(info,"$['store']['book'][0]['title']")
print(ret1)
# 2. 提取2、3、4本书的标题
print('提取2、3、4本书的标题')
ret2=jsonpath.jsonpath(info,"$.store.book[1,2,3].title")
print(ret2)
ret2=jsonpath.jsonpath(info,"$.store.book[1:4].title")
print(ret2)
# 3. 提取1、3本书的标题
print('提取1、3本书的标题')
ret3=jsonpath.jsonpath(info,"$.store.book[0,2].title")
print(ret3)
# 4. 提取最后一本书的标题
print('提取最后一本书的标题')
ret4=jsonpath.jsonpath(info,"$.store.book[-1:].title")
print(ret4)
ret4=jsonpath.jsonpath(info,"$.store.book[(@.length-1)].title")
print(ret4)
# 5. 提取价格小于10的书的标题
print('提取价格小于10的书的标题')
ret5=jsonpath.jsonpath(info,"$.store.book[?(@.price<10)].title")
print(ret5)
# 6. 提取价格小于或者等于20的所有商品的价格
print('提取价格小于或者等于20的所有商品的价格')
ret6=jsonpath.jsonpath(info,"$.store.book[?(@.price<=20)].title")
print(ret6)
ret6=jsonpath.jsonpath(info,"$..[?(@.price<=20)].title")
print(ret6)
# 7. 获取所有书的作者
print('获取所有书的作者')
ret7=jsonpath.jsonpath(info,"$.store.book[*].author")
print(ret7)
# 8. 获取所有作者
print('获取所有作者')
ret8=jsonpath.jsonpath(info,"$..author")
print(ret8)
# 9. 获取在store中的所有商品(包括书、自行车)
print('获取在store中的所有商品(包括书、自行车)')
ret9=jsonpath.jsonpath(info,"$..store")
print(ret9)
# 10. 获取所有商品(包括书、自行车)的价格
print('获取所有商品(包括书、自行车)的价格')
ret10=jsonpath.jsonpath(info,"$.store..price")
print(ret10)
# 11. 获取带有isbn的书
print('获取带有isbn的书')
ret11=jsonpath.jsonpath(info,"$..book[?(@.isbn)]")
print(ret11)
# 12. 获取不带有isbn的书
print('获取不带有isbn的书')
ret12=jsonpath.jsonpath(info,"$..book[?(!@.isbn)]")
print(ret12)
# 13. 获取价格在5~10之间的书
print('获取价格在5~10之间的书')
ret13=jsonpath.jsonpath(info,"$..book[?(@.price>=5&&@.price<=10)]")
print(ret13)
# 14. 获取价格不在5~10之间的书
print('获取价格不在5~10之间的书')
ret14=jsonpath.jsonpath(info,"$..book[?(@.price<5||@.price>10)]")
print(ret14)
# 15. 获取所有的元素
print('获取所有的元素')
ret15=jsonpath.jsonpath(info,"$..")
for i in ret15:
print(i)
7.非结构化数据提取-bs4
7.1 bs4介绍与安装
介绍
BeautifulSoup4 简称 BS4,和使用 lxml模块一样,Beautiful Soup 也是一个HTML/XML 的解析器,主要的功能也是解析和提取 HTML/XML 数据。
Beautiful Soup 是基于 HTML DOM 的,会载入整个文档,解析整个DOM树,因此时间和内存开销都会大很多,所以性能要低于 lxml 模块。
BeautifulSoup 用来解析 HTML 比较简单,API 非常人性化,支持CSS选择器、Python标准库中的HTML 解析器,也支持 lxml 模块 的XML解析器。
安装
pip install bs4 -i https://mirrors.aliyun.com/pypi/simple
官方文档:http://beautifulsoup.readthedocs.io/zh_CN/v4.4.0
抓取工具 | 速度 | 使用 |
---|---|---|
正则 | 最快 | 困难 |
BeautifulSoup | 慢 | 最简单 |
lxml | 快 | 简单 |
7.2 bs4 基本使用示例
from bs4 import BeautifulSoup
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><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>
"""
# 创建Beautiful Soup对象
soup=BeautifulSoup(html,'lxml')
# 格式化输出html代码
print(soup.prettify())
7.3 find_all搜索文档树中标签、内容、属性
find_all 方法中的参数:
def find_all(self,name=None,attrs={},recursive=True,string=None,limit=None,**kwargs)...
1.name 参数:
当前参数可以传递标签名称字符串,根据传递的标签名称搜索对应标签
# 1.创建soup对象
soup=BeautifulSoup(html_obj,'lxml')
#2.根据标签名称搜索标签
ret_1=soup.find_all('b')
ret_2=soup.find_all('a')
print(ret_1,ret_2)
除了传递标签名称字符串之外也可传递正则表达式,如果传入正则表达式作为参数,Beautiful Soup 会通过正则表达式的 match()来匹配内容。下面例子中找出所有以 b 开头的标签。
soup=BeautifulSoup(html_obj,'lxml')
for tag in soup.find_all(re.compile('^b'));
print(tag.name)
如果传递是一个列表,则 Beautiful Soup 会将与 列表中任一元素匹配的内容返回。
soup=BeautifulSoup(html_obj,'lxml')
ret=soup.find_all(['a','b'])
print(ret)
2.attrs参数:可以根据标签属性搜索对应标签
from bs4 import BeautifulSoup
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><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>
"""
soup=BeautifulSoup(html,'lxml')
ret_1=soup.find_all(attrs={'class':'sister'})
print(ret_1)
print('-' *30)
# 简写方式
ret_2=soup.find_all(class_='sister')
print(ret_2)
print('-' *30)
# 查询id属性为link2的标签
ret_3=soup.find_all(id='link2')
print(ret_3)
3.string参数:通过string参数可以搜索文档中的字符串内容,与name参数的可选值一样,string参数接受字符串、正则表达式、列表
import re
from bs4 import BeautifulSoup
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><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>
"""
soup=BeautifulSoup(html,'lxml')
ret_1=soup.find_all(string='Elsie')
print(ret_1)
ret_2=soup.find_all(string=['Tillie','Elisie','Lacie'])
print(ret_2)
ret_3=soup.find_all(string=re.compile('Dormouse'))
print(ret_3)
find_all方法的使用:
import re
from bs4 import BeautifulSoup
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><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>
"""
# 创建Beautiful Soup对象
soup=BeautifulSoup(html,'lxml')
# 格式化输出html代码
# print(soup.prettify())
# 一、name参数
# 1.根据标签名搜索标签
ret1=soup.find_all('a')
print(ret1)
# 2.传递正则表达式
for i in soup.find_all(re.compile('^b')):
print(i.name)
# 3.传递一个列表
ret3=soup.find_all(['a','b'])
print(ret3)
# 二、attrs参数:可以根据标签属性搜索对应标签
ret4=soup.find_all(attrs={'class':'title'})
print(ret4)
ret4=soup.find_all(class_='sister')
print(ret4)
# 查询id属性为link2的标签
ret5=soup.find_all(id='link2')
print(ret5)
# 三、string参数:通过string参数可以搜索文档中的字符串内容,与name参数的可选值一样,string参数接受字符串、正则表达式、列表
ret6=soup.find_all(string='Elsie')
print(ret6)
ret7=soup.find_all(string=['Tillie','Elisie','Lacie'])
print(ret7)
ret8=soup.find_all(string=re.compile('Dormouse'))
print(ret8)
find 方法
find 的用法与 find_all 一样,区别在于find 返回第一个符合匹配结果,find_all则返回所有匹配结果的列表。
7.4 文档搜索树中的 css 选择器
另一种与 find_all 方法有异曲同工之妙的查找方法,也是返回所有匹配结果的列表。
css 选择器编写注意事项:
- 标签名称不加任何修饰
- 类名前加 .
- id属性名称前加#
css 选择器编写方式与编写 css 样式表的语法大致相同。在bs4中可以直接使用 soup.select()方法进行筛选,返回值类型是一个列表。
css选择器的使用:
from bs4 import BeautifulSoup
import re
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><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>
"""
soup=BeautifulSoup(html,'lxml')
# 1.标签选择器
print(soup.select('title'))
print(soup.select('a'))
print(soup.select('b'))
print('-'*30)
# 2.类选择器
print(soup.select('.sister'))
print('-'*30)
# 3.id选择器
print(soup.select('#link1'))
print('-'*30)
# 4.层选择器
print(soup.select('p #link1'))
print('-'*30)
# 5.属性选择器
print(soup.select('a[class="sister"]'))
print('-'*30)
print(soup.select('a[href="http://example.com/elsie"]'))
print('-'*30)
# 6.get_text()方法:获取文本内容
# select返回的是列表对象,需要使用for循环遍历列表元素再使用get_text方法获取文本数据
for title in soup.select('title'):
print(title.get_text())
# 7.get()方法:获取属性
for attr in soup.select('a'):
print(attr.get('href'))
案例:使用bs4抓取搜狗微信下的所有文章标题
https://weixin.sogou.com/weixin?_sug_type_=1&type=2&query=python
import requests
from bs4 import BeautifulSoup
url='https://weixin.sogou.com/weixin?_sug_type_=1&type=2&query=python'
headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/133.0.0.0 Safari/537.36'}
res=requests.get(url,headers=headers)
soup=BeautifulSoup(res.text,'lxml')
title_list=soup.select('div[class="txt-box"] a')
for i in title_list:
title=i.get_text()
print(title)
8.正则表达式
正则表达式 Regular Expression ,通常缩写为 RegExp 或 Regex,是一种用于匹配、搜索和操作文本字符串的强大工具。它是由模式 pattern 和相关的匹配规则组成的表达式。
正则表达式的模式由一系列字符和特殊字符组成,用于描述所需匹配的文本模式。它可以用于各种编辑语言和文本编辑器中,例如Python、JavaScript、Perl等。
正则表达式提供了一种灵活的方式来匹配和处理字符串。它可以用以下情况:
- 搜索和替换:可以使用正则表达式来搜索文本中符合特定模式的字符串,并进行替换或其他操作。
- 验证数据:可以使用正则表达式来验证用户输入的数据是否符合特定的格式要求,例如验证电子邮件地址、电话号码、日期等
- 提取信息:可以使用正则表达式从文本中提取特定的信息,例如提取URL、提取网页中的所有链接等。
正则表达式中的特殊字符和语法规则很多,包括字符类 character class、量词 quantifier、分组grouping、转义字符 escape character等。这些特殊字符和规则可以组合使用,构成复杂的匹配模式。
正则表达式在线验证工具:https://regexr-cn.com
在这个工具中我们可以快速验证自己编写的正则表达式是否存在语法错误。
8.1 正则表达式常用函数
1.re.match(pattern,string)
此函数用于检测字符串的开头位置是否与指定的模式 pattern 相匹配。如果匹配成功,则返回一个匹配对象;否则返回 None
import re
string ='Hello,World!'
pattern=r'Hello'
result=re.match(pattern,string)
if result:
print('匹配成功:',result.group())
else:
print('匹配失败')
2.re.search(pattern,string)
该函数会在整个字符串内查找并返回第一个成功匹配模式 pattern 的结果。若匹配成功,返回一个匹配对象;若未找到匹配项,则返回 None。
import re
string1='Hello,World!'
pattern1=r'World'
result1=re.search(pattern1,string1)
if result1:
print('匹配成功:',result1.group())
else:
print('匹配失败')
3.re.findall(pattern,string)
此函数会返回字符串中所有匹配模式 pattern 的结果列表。如果没有找到匹配项,则返回一个空列表。
import re
string2="ab12cd34ef56"
pattern2=r'\d+'
result2=re.findall(pattern2,string2)
print('匹配结果:',result2)
4.re.sub(pattern,repl,string)
该函数会把字符串中所有匹配模式 pattern 的部分用指定的字符串 repl 进行替换,并返回替换后的新字符串。
import re
string3='Hello,World!'
pattern3=r'World'
repl='Python'
result3=re.sub(pattern,repl,string3)
print('替换后的字符串:',result3)
5. re.split(pattern,string)
此函数根据正则表达式 pattern 对字符串进行分割,并将分割后的所有字符串放在一个列表中返回。
import re
string4='apple,banana;cherry grape'
pattern4=r'[;, ]'
result4=re.split(pattern4,string4)
print('分割后的列表:',result4)
8.2 正则表达式的元字符
元字符由特殊符号组成,是正则表达式强大的关键所在。元字符可以定义字符集合、子组匹配以及模式重复次数,使得正则表达式不仅能匹配单个字符串,还能匹配字符串集合。
1.字符匹配
英文句号.
匹配除换行符 \n 之外的任意一个字符。
import re
string_1='abc\nn123'
pattern_1=r'a.c'
result_1=re.findall(pattern_1,string_1)
print('匹配结果:',result_1)
# 输出的结果
# 匹配结果: ['abc']
中括号 [ ]
匹配包含在中括号内部的任意一个字符。
import re
string_2='abc123'
pattern_2=r'[abc]'
result_2=re.findall(pattern_2,string_2)
print('匹配结果:',result_2)
# 输出结果:
#匹配结果: ['a', 'b', 'c']
管道符 |
用于在两个正则表达式之间进行或操作,只要满足其中一个模式即可匹配
import re
string_3='apple banana cherry'
pattern_3=r'apple|cheery'
result_3=re.findall(pattern_3,string_3)
print('匹配结果:',result_3)
# 输出结果:
# 匹配结果: ['apple']
乘方符号^
匹配字符串的起始内容
import re
string_4='Hello,World!'
pattern_4=r'^Hello'
result_4=re.findall(pattern_4,string_4)
print('匹配结果:',result_4)
# 输出结果:
# 匹配结果: ['Hello']
货币符号 $
匹配字符串的结束位置的内容
import re
string_5='Hello,World!'
pattern_5=r'World!$'
result_5=re.findall(pattern_5,string_5)
print('匹配结果:',result_5)
# 输出结果:
# 匹配结果: ['World!']
量化符号 ?、*、+、{n}、{n,}、{m,n}
?:前面的元素是可选的,并且最多能匹配1次
import re
s1='color colour'
p1=r'colou?r'
r1=re.findall(p1,s1)
print('匹配结果:',r1)
# 输出结果
# 匹配结果: ['color', 'colour']
*:前面的元素会被匹配0次或多次
import re
s2='ab abb abbb'
p2=r'ab*'
r2=re.findall(p2,s2)
print('匹配结果:',r2)
# 输出结果:
# 匹配结果: ['ab', 'abb', 'abbb']
+:前面的元素会被匹配1次或多次
import re
s3='ab abb abbb'
p3=r'ab+'
r3=re.findall(p3,s3)
print('匹配结果:',r3)
# 输出结果:
# 匹配结果: ['ab', 'abb', 'abbb']
{n}:前面的元素会正好匹配n次
import re
s4='123 1234 12345'
p4=r'\d{3}'
r4=re.findall(p4,s4)
print('匹配结果:',r4)
# 输出结果:
# 匹配结果: ['123', '123', '123']
{n,}:前面的元素至少会被匹配n次
import re
s5='123 1234 12345'
p5=r'\d{3,}'
r5=re.findall(p5,s5)
print('匹配结果:',r5)
# 输出结果:
# 匹配结果: ['123', '1234', '12345']
{m,n}:前面的元素至少匹配n次
import re
s6='12 123 1234 12345'
p6=r'\d{2,4}'
r6=re.findall(p6,s6)
print('匹配结果:',r6)
# 输出结果:
# 匹配结果: ['12', '123', '1234', '1234']
2.转义字符
\w:匹配字母、数字和下划线
s_1='abc123_!@#'
p_1=r'\w'
r_1=re.findall(p_1,s_1)
print('匹配结果:',r_1)
# 输出结果:
# 匹配结果: ['a', 'b', 'c', '1', '2', '3', '_']
\W:与\w相反,匹配非字母、数字和下划线的字符。
import re
s_2='abc123_!@#'
p_2=r'\W'
r_2=re.findall(p_2,s_2)
print('匹配结果:',r_2)
# 输出结果:
# 匹配结果: ['!', '@', '#']
\s:匹配空白字符,如空格、制表符、换行符等。
import re
s_3='Hello World\n'
p_3=r'\s'
r_3=re.findall(p_3,s_3)
print('匹配结果:',r_3)
# 输出结果:
# 匹配结果: [' ', '\n']
\S:匹配非空白字符。
import re
s_4='Hello World\n'
p_4=r'\S'
r_4=re.findall(p_4,s_4)
print('匹配结果:',r_4)
# 输出结果:
# 匹配结果: ['H', 'e', 'l', 'l', 'o', 'W', 'o', 'r', 'l', 'd']
\d:匹配数字。
import re
s_5='abc123'
p_5=r'\d'
r_5=re.findall(p_5,s_5)
print('匹配结果:',r_5)
# 输出结果:
# 匹配结果: ['1', '2', '3']
\D:匹配非数字字符
import re
s_6='abc123'
p_6=r'\D'
r_6=re.findall(p_6,s_6)
print('匹配结果:',r_6)
# 输出结果:
# 匹配结果: ['a', 'b', 'c']
\b:匹配单词边界
import re
s_7='hello world'
p_7=r'\bhello\b'
r_7=re.findall(p_7,s_7)
print('匹配结果:',r_7)
# 输出结果:
# 匹配结果: ['hello']
\B:匹配非单词边界
import re
s_8='helloworld'
p_8=r'\Bworld\B'
r_8=re.findall(p_8,s_8)
print('匹配结果:',r_8)
# 输出结果:
# 匹配结果: []