資料解析之正則與BS4
阿新 • • 發佈:2020-09-09
1.資料解析就是應用一定的技術手段在響應資料中獲取目標資料
2.常用資料解析方式:
正則: 匹配高效, 但正則表示式書寫有難度
BS4: 解析資料速度慢, 但使用簡單
xpath: 解析速度快, 使用簡單
3.環境配置:
(1).正則: pip install re
(2).BS4:
pip install lxml
pip install BeautifulSoup4
(3).xpath:
pip install lxml
2.正則(複習)
# 1.元字元匹配 . 匹配任意字元,除了換行符 [] 用來表示一組字元,單獨列出:[abc] 匹配 'a','b'或'c' [^...] 匹配除了字元組中字元的所有字元 \d 匹配任意數字,等價於 [0-9]. \D 匹配任意非數字 \w 匹配字母數字及下劃線 \W 匹配非字母數字及下劃線 \s 匹配任意空白字元,等價於 [\t\n\r\f]. \S 匹配任意非空字元 # 2.字元組: 要求在一個位置匹配的字元可能出現很多種情況, 各種情況組成一個組 [0123456789]: 匹配0到9任意字元 [0-9]: 同上 [a-z]: 匹配a到z的任意小寫字母 [A-Z]: 匹配A到Z的任意大寫字母 [0-9a-fA-F]: 以上三種的組合, 匹配0-9任意陣列或a到f之間任意字母, 不區分大小寫 自定義字元組:[a3h5] ---> 代表匹配a, 3, h, 5等字元 # 量詞: * 重複零次或更多次 + 重複一次或更多次 ? 重複零次或一次 {n} 重複n次 {n,} 重複n次或更多次 {n,m} 重複n到m次 # 邊界修飾符 ^ 匹配開始 $ 匹配結尾 # 分組 在正則表示式中新增(), 就形成了一個分組, 在re模組中優先匹配顯示分組內容 import re s = "<a href='www.baidu.com'>正則匹配實驗</a>" res = re.findall("href='(.*)'>", s) print(res) # 匹配模式 re.S 單行模式 re.M 多行模式 re.I 忽略大小寫 # 貪婪匹配與非貪婪匹配 貪婪匹配是指: 在使用量詞: * , + 等時, 儘可能多的匹配內容 非貪婪匹配是指: 使用?對正則表示式進行修飾, 使量詞的匹配儘可能少, 如+代表匹配1次或多次, 在?的修飾下, 只匹配1次. # re模組 1.re.findall('正則表示式', '待匹配字串'): 返回所有滿足匹配條件的結果, 以列表形式返回 2.re.search('正則表示式', '帶匹配字串'): 匹配到第一個就返回一個物件, 該物件使用group()進行取值, 如果為匹配到則返回None 3.re.match('正則表示式', '待匹配字串'): 只從字串開始進行匹配, 如果匹配成功返回一個物件,同樣使用group()進行取值, 匹配不成功返回None 4.re.compile('正則表示式'): 將正則表示式編譯為物件, 但需要按該正則表示式匹配是可以在直接使用該物件呼叫以上方法即可.
# 示例:
s = "abcabc你好"
# findall方法演示
res_findall = re.findall('a', s)
print('findall匹配結果:', res_findall)
# search方法演示, 不確定是否能匹配出結果, 不可直接使用group進行取值, 需要判斷或進行異常處理
res_search = re.search("你", s)
if res:
print('search匹配結果', res.group())
else:
print('None')
# match方法演示:
res_match_1 = re.match(' abc', s)
res_match_2 = re.match('bc', s)
print('res_match_1結果:', res_match_1)
print('res_match_2結果:', res_match_2)
# compile方法演示:
re_obj = re.compile('ab')
res = re_obj.findall(s)
print(res)
# 利用正則表示式抓取校花網圖片
import re
import requests
# 請求url, 抓取頁面
url = 'http://www.xiaohuar.com/hua/'
headers = {
'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36'
}
res = requests.get(url=url, headers=headers)
html = res.text
# 利用正則匹配頁面中的img標籤, 獲取其src屬性值
all_src = re.findall('<img.*src="(.*jpg)"', html, re.M)
# 遍歷獲取到的圖片連線, 處理連線為完整的url, 再次請求抓取圖片二進位制流資料, 寫入檔案
for num, url in enumerate(all_src):
url = 'http://www.xiaohuar.com' + url
image = requests.get(url=url, headers=headers)
with open('%s.jpg' % num, 'wb') as f:
f.write(image.content)
3.BS4詳解
# BS4解析庫解析資料原理:
定位標籤節點 ---> 提取標籤節點內容或屬性值
# BS4基本使用步驟:
from BS4 import BeautifulSoup
...
soup = BeautifulSoup(text, 'lxml') # 網路檔案例項化物件
soup = BeautifulSoup(open('filename', 'lxml')
tag_element_or_attribute = soup.選擇器
# BS4可用選擇器:
(1).節點選擇器:
- 節點名單選: soup.div
- 巢狀選擇: soup.div.span
- 關聯選擇:
- 子節點:contents返回位元組子節點, 包含換行符, 返回型別是一個列表
children效果相同, 但返回一個生成器
- 子孫節點:descendants, 返回生成器遍歷取值
1).深度選擇, 從第一個子節點開始, 直至第一個子節點內的所有孫節點全部選擇到
2).如果兩個標籤在兩行上, 匹配第一個節點的所有深度後選擇換行符.
3).如果標籤中的文字是換行的, 則不單獨匹配換行符, 換行符包含在文字內
- 父節點:parent
- 祖先節點:parents
- 兄弟節點:
next_sibling: 當前節點的下一個兄弟節點
previous_sibling: 當前節點的上一個兄弟節點
(2).方法選擇器:
1).find_all(name, attrs, recursive, text, **kwargs)
- 標籤名選擇:soup.find_all(name='ul') # 選擇所有ul標籤
- 巢狀選擇:
for ul in soup.find_all(name='ul'): # 迴圈ul列表, 選擇每個ul中的li標籤
ul.find_all(name='li')
- 屬性值選擇:soup.find_all(attrs={'class': 'element'}) # 根據屬性選擇節點, 等效:soup.find_all(class='element')
- 文字正則選擇:soup.find_all(text=re.compile('link')) # 返回所有標籤中包含link字元的文字的物件
2).find():返回一個物件
(3).css選擇器:css選擇器需要呼叫select方法, 改方法返回一個列表
1).選擇:
soup.select('ul li') # 選擇ul下的li所有標籤
soup.select('.panel') # 選擇class的值為panel的標籤
soup.select('#item1') # 選擇id為item1的標籤
2).巢狀選擇:
for ul in soup.select('ul'):
ul.select('li')
3).獲取文字與屬性:
獲取屬性:
for li in soup.select('ul li'):
print(li.attrs['id'])
print(li['id']) # 上下兩種形式效果一致
獲取文字:
for li in soup.select('ul li'):
print(li.get_text())
print(li.string)
# 節點文字或屬性的獲取
tag_element.name # 獲取節點名稱
tag_element.attrs # 獲取節點所有屬性, 結果為字典形式
tag_element.attrs['name'] # 獲取節點的單個屬性值, 等效: tag_element['name'], 屬性多值是返回列表
tag_element.string # 獲取節點的文字內容
如果使用關聯選擇, 且結果為生成器可以先轉為列表再索引定位元素後在呼叫上面的獲取元素方法, 如:
list(p.parents)[0].attrs['name']