利用 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 etreetext = ''' <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 etreetext = ''' <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']=titleitem['href']=hrefprint(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 etreetext = ''' <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 Noneitem["title"] = li.xpath("./a/text()")[0] if len(li.xpath("./a/text()")) > 0 else Noneprint(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 jsonpathinfo = {"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 jsonpathinfo = {"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 BeautifulSouphtml = """
<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 BeautifulSouphtml = """
<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 refrom bs4 import BeautifulSouphtml = """
<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 refrom bs4 import BeautifulSouphtml = """
<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 rehtml = """
<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 BeautifulSoupurl='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 restring ='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 restring2="ab12cd34ef56"
pattern2=r'\d+'
result2=re.findall(pattern2,string2)
print('匹配結果:',result2)
4.re.sub(pattern,repl,string)
該函數會把字符串中所有匹配模式 pattern 的部分用指定的字符串 repl 進行替換,并返回替換后的新字符串。
import restring3='Hello,World!'
pattern3=r'World'
repl='Python'
result3=re.sub(pattern,repl,string3)
print('替換后的字符串:',result3)
5. re.split(pattern,string)
此函數根據正則表達式 pattern 對字符串進行分割,并將分割后的所有字符串放在一個列表中返回。
import restring4='apple,banana;cherry grape'
pattern4=r'[;, ]'
result4=re.split(pattern4,string4)
print('分割后的列表:',result4)
8.2 正則表達式的元字符
元字符由特殊符號組成,是正則表達式強大的關鍵所在。元字符可以定義字符集合、子組匹配以及模式重復次數,使得正則表達式不僅能匹配單個字符串,還能匹配字符串集合。
1.字符匹配
英文句號.
匹配除換行符 \n 之外的任意一個字符。
import restring_1='abc\nn123'
pattern_1=r'a.c'
result_1=re.findall(pattern_1,string_1)
print('匹配結果:',result_1)# 輸出的結果
# 匹配結果: ['abc']
中括號 [ ]
匹配包含在中括號內部的任意一個字符。
import restring_2='abc123'
pattern_2=r'[abc]'
result_2=re.findall(pattern_2,string_2)
print('匹配結果:',result_2)# 輸出結果:
#匹配結果: ['a', 'b', 'c']
管道符 |
用于在兩個正則表達式之間進行或操作,只要滿足其中一個模式即可匹配
import restring_3='apple banana cherry'
pattern_3=r'apple|cheery'
result_3=re.findall(pattern_3,string_3)
print('匹配結果:',result_3)# 輸出結果:
# 匹配結果: ['apple']
乘方符號^
匹配字符串的起始內容
import restring_4='Hello,World!'
pattern_4=r'^Hello'
result_4=re.findall(pattern_4,string_4)
print('匹配結果:',result_4)# 輸出結果:
# 匹配結果: ['Hello']
貨幣符號 $
匹配字符串的結束位置的內容
import restring_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 res1='color colour'
p1=r'colou?r'
r1=re.findall(p1,s1)
print('匹配結果:',r1)# 輸出結果
# 匹配結果: ['color', 'colour']
*:前面的元素會被匹配0次或多次
import res2='ab abb abbb'
p2=r'ab*'
r2=re.findall(p2,s2)
print('匹配結果:',r2)# 輸出結果:
# 匹配結果: ['ab', 'abb', 'abbb']
+:前面的元素會被匹配1次或多次
import res3='ab abb abbb'
p3=r'ab+'
r3=re.findall(p3,s3)
print('匹配結果:',r3)# 輸出結果:
# 匹配結果: ['ab', 'abb', 'abbb']
{n}:前面的元素會正好匹配n次
import res4='123 1234 12345'
p4=r'\d{3}'
r4=re.findall(p4,s4)
print('匹配結果:',r4)# 輸出結果:
# 匹配結果: ['123', '123', '123']
{n,}:前面的元素至少會被匹配n次
import res5='123 1234 12345'
p5=r'\d{3,}'
r5=re.findall(p5,s5)
print('匹配結果:',r5)# 輸出結果:
# 匹配結果: ['123', '1234', '12345']
{m,n}:前面的元素至少匹配n次
import res6='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 res_2='abc123_!@#'
p_2=r'\W'
r_2=re.findall(p_2,s_2)
print('匹配結果:',r_2)# 輸出結果:
# 匹配結果: ['!', '@', '#']
\s:匹配空白字符,如空格、制表符、換行符等。
import res_3='Hello World\n'
p_3=r'\s'
r_3=re.findall(p_3,s_3)
print('匹配結果:',r_3)# 輸出結果:
# 匹配結果: [' ', '\n']
\S:匹配非空白字符。
import res_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 res_5='abc123'
p_5=r'\d'
r_5=re.findall(p_5,s_5)
print('匹配結果:',r_5)# 輸出結果:
# 匹配結果: ['1', '2', '3']
\D:匹配非數字字符
import res_6='abc123'
p_6=r'\D'
r_6=re.findall(p_6,s_6)
print('匹配結果:',r_6)# 輸出結果:
# 匹配結果: ['a', 'b', 'c']
\b:匹配單詞邊界
import res_7='hello world'
p_7=r'\bhello\b'
r_7=re.findall(p_7,s_7)
print('匹配結果:',r_7)# 輸出結果:
# 匹配結果: ['hello']
\B:匹配非單詞邊界
import res_8='helloworld'
p_8=r'\Bworld\B'
r_8=re.findall(p_8,s_8)
print('匹配結果:',r_8)# 輸出結果:
# 匹配結果: []