常用的提取網頁數據的工具有三種xpath、css選擇器、正則表達式
1.xpath
1.1在python中使用xpath必須要下載lxml模塊:
lxml官方文檔 :https://lxml.de/index.html
pip install lxml
然後導入:
from lxml import etree
使用:
selector = etree.HTML(html_str)
selector.xpath("xpath語法")
1.2xpath語法
w3c xpath語法:https://www.w3school.com.cn/xpath/index.asp
以上是獲取元素的語法,但我們最終的目的是獲取元素裏面的屬性信息或文本信息
from lxml import etree
html_str = '''
<div class="example1">
<a href="http://www.baidu.com/" data-id="12345678">百度</a>
</div>
<div class="example2">
<a href="https://cn.bing.com/" data-id="23456789">Bing</a>
</div>
'''
response = etree.HTML(html_str)
獲取屬性data-id信息:
data-id = selector.xpath("//div[@class='example1']/@data-id")[0]
獲取文本信息:
text = selector.xpath("//div[@class='example2']/a/text()")[0]
2.selector
2.1cssselect
使用css選擇器也是使用lxml這個模塊
使用:
selector = etree.HTML(html_str)
selector.cssselect("css語法")
異常:
有的人在使用selector.cssselect("css語法")的時候,會報錯cssselect沒有安裝,我們使用dir(selector)的時候,會發現selector的屬性方法中有cssselect這個方法,之所以報錯,是因爲這個
方法是依賴於cssselect這個模塊中,所以要安裝cssselect這個模塊
安裝:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple cssselect
2.2css語法
基本的父代、子代、後代、祖輩我們就不用說了,今天主要想記錄的是如何獲取屬性值和文本
from lxml import etree
html_str = '''
<div class="example1">
<a href="http://www.baidu.com/" data-id="12345678">百度</a>
</div>
<div class="example2">
<a href="https://cn.bing.com/" data-id="23456789">Bing</a>
</div>
'''
response = etree.HTML(html_str)
獲取文本:
# 獲取文本用text屬性
text = selector.cssselect("div.example1 a")[0].text
獲取屬性值:
# 獲取屬性值用get(attr)方法
link = selector.cssselect("div.example2 a")[0].get("href")
3.正則表達式
3.1在Python中使用正則表達式必須導入Python內置的re模塊
import re
3,2常用的匹配方法有re.match()、re.search()、re.findall()
re.match(pattern, string, flag)
re.search(pattern, string, flag)
re.findall(pattern, string, flag)
pattern 匹配的正則表達式
string 要匹配的字符串
flags 標誌位,用於控制正則表達式的匹配方式,如:是否區分大小寫、多行匹配等等(可選參數)
3.2.1pattern
^ 匹配字符串的開頭
$ 匹配字符串的結尾
. 匹配任意字符(包括\r),除了換行符
[...] 匹配一組字符中的任意一個字符
[^...] 匹配不在一組字符中的任意字符
* 匹配0個或多個表達式
+ 匹配1個或多個表達式
? 匹配0個或1個表達式,非貪婪方式
{n} 匹配n個前面的表達式
{n,}
{n,m} 匹配n到m次前面的表達式,貪婪模式
a|b 匹配a或b
() 對正則表達式分組並記住匹配的文本
\w 匹配字母數字下劃線(Python中包括漢字)
\W 匹配非字母數字下劃線
\s 匹配任意空白字符,等價於[\t\r\n\g]
\S 匹配任意非空字符
\d 匹配任意數字,等價於[0-9]
\D 匹配任意非數字
\A 匹配字符串開始
\Z 匹配字符串結束,如果存在換行,只匹配到換行前的結束字符串
\z 匹配字符串結束
\G 匹配最後匹配完成的位置
\b 匹配一個單詞的邊界,也就是指單詞和空格間的位置。例如:‘er\b’可以匹配never中的er,但是不能匹配到verb中的er
\B 匹配一個非單詞的邊界
\n、\t等 匹配一個換行符
\1...\9 匹配n個分組的內容
\10 匹配第n個分組的內容,如果他經匹配,否則指的是八進制字符碼的表達式
3.2.2string
該參數既可以爲字符型字符串,也可以爲二進制行字符串
3.2.2.1當string爲字符型字符串時
import re
string = "token:dsasdfva13421hb4v4b141jjb4bj1jbb漢字是世界上最難的文字"
# 匹配token值
re.findall(r"token:([0-9a-zA-Z]+)", string)
# 執行結果 ['dsasdfva13421hb4v4b141jjb4bj1jbb']
3.2.2.2當string爲二進制型字符串時
import re
import socket
# 發送圖片請求,並返回一個響應報文
s = socket.socket()
s.connect(("desk-fd.zol-img.com.cn", 80))
s.send("GET /t_s960x600c5/g5/M00/0E/08/ChMkJl3g7o-Ibp4nAAYg0I-lImUAAvfjgFkfbYABiDo544.jpg HTTP/1.1\r\nHost: desk-fd.zol-img.com.cn\r\n\r\n".encode())
res_content = s.recv(1024)
s.settimeout(2)
full_content = b''
while res_content:
try:
full_content += res_content
res_content = s.recv(1024)
except:
break
s.close()
# 從響應報文中提取出響應體(圖片二進制數字),需瞭解報文的結構
re.findall(b"\r\n\r\n(.+)", full_content)
3.2.3flags(可選參數)
正則表達式可以包含一些可選標誌修飾符來控制匹配的模式。修飾符被指定爲一個可選的標誌。多個標誌可以通過按位OR(|)它們來指定。如
re.I|re.M被設置成I和M標誌:
re.I 使匹配對大小寫不敏感
re.L 使本地化識別(locale-aware匹配)
re.M 多行匹配,影響^和$
re.S 使.匹配包括換行在內的所有字符
re.U 根據Unicode字符集。這個標誌影響\w,\W,\b,\B
re.X 該標誌通過給予你更靈活的格式以便你將正則表達式寫得更易於理解
3.3re.search()、re.match、re.findall()
3.3.1match(pattern,string,flags) 只匹配開頭
匹配不到返回:None
匹配到返回:
re.match("(\d)", "123")
<re.Match object; span=(0, 1), match='1'>
匹配到就使用group(num=0)、groups()取值
group(num=0)
# 序號0爲正則表達式匹配到的內容
re.match("\d\d\d", "12345566").group(0) # 默認num爲0
# '123'
# 序號1爲正則表達式第一個括號的內容
re.match("(\d)(\d)(\d)", "12345566").group(1)
# '1'
# 序號2爲正則表達式第二個括號的內容
re.match("(\d)(\d)(\d)", "12345566").group(2)
# '2'
# 序號3爲正則表達式第三個括號的內容
re.match("(\d)(\d)(\d)", "12345566").group(3)
# '3'
# 返回匹配到的內容所有分組元組
re.match("(\d)(\d)(\d)", "12345566").groups()
# ('1','2','3')
3.3.2search(pattern,string,flags)全文匹配一次
匹配不到返回:None
匹配到返回:
re.search("(\d)(\d)", "123")
<re.Match object; span=(0, 2), match='12'>
匹配到就使用group(num=0)、groups()取值
# 序號0爲正則表達式匹配到的內容
re.search("\d\d\d", "as12345566").group(0) # 默認num爲0
# '123'
# 序號1爲正則表達式第一個括號的內容
re.search("(\d)(\d)(\d)", "as12345566").group(1)
# '1'
# 序號2爲正則表達式第二個括號的內容
re.search("(\d)(\d)(\d)", "as12345566").group(2)
# '2'
# 序號3爲正則表達式第三個括號的內容
re.search("(\d)(\d)(\d)", "as12345566").group(3)
# '3'
# 返回匹配到的內容所有分組元組
re.search("(\d)(\d)(\d)", "as12345566").groups()
# ('1','2','3')
3.3.3findall(pattern,string,flags)全文匹配
匹配不到返回:[]
匹配到返回:一個列表(如果沒有分組,就返回匹配正則表達式的所有匹配到的項,如果分組就會返回匹配到的項中的分組組成的一個元組的所有項)
re.findall("\d\d", "123123432345")
# 結果 ['12', '31', '23', '43', '23', '45']
re.findall("(\d)(\d)", "123123432345")
# 結果 [('1', '2'), ('3', '1'), ('2', '3'), ('4', '3'), ('2', '3'), ('4', '5')]