國(guó)務(wù)院政府網(wǎng)站集約化建設(shè)seo排名優(yōu)化工具
文章目錄
- 1. Xpath
- 2. jsonpath
- 3. BeautifulSoup
- 4. 正則表達(dá)式
- 4.1 特殊符號(hào)
- 4.2 特殊字符
- 4.3 限定符
- 4.3 常用函數(shù)
- 4.4 匹配策略
- 4.5 常用正則
爬蟲將數(shù)據(jù)爬取到后,并不是全部的數(shù)據(jù)都能用,我們只需要截取里面的一些數(shù)據(jù)來(lái)用,這也就是解析爬取到的信息,在解析方面,我們常用的手段有三個(gè),Xpath,jsonpath以及BeautifulSoup,接下來(lái)我將對(duì)其分別進(jìn)行介紹。
1. Xpath
Chrome有Xpath的插件,可以在應(yīng)用商店里面下載一個(gè),而Python中使用Xpath解析的話需要安裝 lxml
庫(kù)。
lxml
庫(kù)的方法很多,這么只講一些涉及到爬蟲解析的方法,即主要使用的是 lxml
中的 etree
。Xpath的主要使用語(yǔ)句如下:
語(yǔ)句 | 含義 |
---|---|
// | 查找所有子孫結(jié)點(diǎn),忽略層級(jí) |
/ | 只查找子結(jié)點(diǎn) |
@ | 選取屬性 |
.. | 選取當(dāng)前節(jié)點(diǎn)的父節(jié)點(diǎn) |
* | 通配符 |
[] | 選取子元素,如 [1] 表示選取第一個(gè)元素 |
div[@id] | 查找有 id 屬性的 div 標(biāo)簽 |
div[@id="name"] | 查找 id="name" 屬性的 div 標(biāo)簽 |
div[contains(@id, "name")] | 查找 id 中包含 name 的 div 標(biāo)簽 |
div[starts-with(@id, "name")] | 查找 id 中以 name 開頭的 div 標(biāo)簽 |
div[@id="name" and @class="name1"] | 邏輯與查詢 |
div[@id="name"] | div[@class="name1"] | 邏輯或操作,邏輯或不能寫成 div[@id="name" | @class="name1"] |
我們查詢的HTML 代碼如下所示:
<!DOCTYPE html>
<html lang="en" ><head><meta charset="UTF-8"/><title></title></head>
<body><div class="wrapper"><a href="www.biancheng.net/product/" id="site">website product</a><ul id="sitename"><li><a href="http://www.biancheng.net/" title="編程幫" id="1">編程</a></li><li><a href="http://world.sina.com/" title="編程幫" id="2">微博</a></li><li><a href="http://www.baidu.com" title="百度">百度貼吧</a></li><li><a href="http://www.taobao.com" title="淘寶">天貓?zhí)詫?span id="ieo6y2aa" class="token tag"></a></li><li><a href="http://www.jd.com/" title="京東">京東購(gòu)物</a></li><li><a href="http://c.bianchneg.net/" title="C語(yǔ)言中文網(wǎng)">編程</a></li><li><a href="http://www.360.com" title="360科技">安全衛(wèi)士</a></li><li><a href="http://www.bytesjump.com/" title="字節(jié)">視頻娛樂</a></li><li><a href="http://bzhan.com/" title="b站">年輕娛樂</a></li><li><a href="http://hao123.com/" title="瀏覽器Chrome">搜索引擎</a></li></ul></div>
</body>
</html>
下面是我們的代碼查詢示例:
from lxml import etreetree = etree.parse('test.html')# 找到ul下所有的a標(biāo)簽的文本內(nèi)容
find1 = tree.xpath('//ul//a/text()')
print('find1 = {}'.format(find1))# 找到ul下所有的a標(biāo)簽中title="編程幫"的標(biāo)簽內(nèi)容
find2 = tree.xpath('//a[@title="編程幫"]/text()')
print('find2 = {}'.format(find2))# 找到ul下所有的a標(biāo)簽中title="編程幫"且id="1"的標(biāo)簽內(nèi)容
find3 = tree.xpath('//a[@id="1" and @title="編程幫"]/text()')
print('find3 = {}'.format(find3))# 找到ul下所有的a標(biāo)簽中title="淘寶"或id="1"的標(biāo)簽內(nèi)容
find4 = tree.xpath('//a[@id="1"]/text() | //a[@title="淘寶"]/text()')
print('find4 = {}'.format(find4))# 找到ul下所有的a標(biāo)簽中title包含"C"的標(biāo)簽內(nèi)容
find5 = tree.xpath('//a[contains(@title, "C")]/text()')
print('find5 = {}'.format(find5))# 找到ul下所有的a標(biāo)簽的以"C"開頭的title的標(biāo)簽內(nèi)容
find6 = tree.xpath('//a[starts-with(@title, "C")]/text()')
print('find6 = {}'.format(find6))# 找到ul下所有的a標(biāo)簽中title="淘寶"或id="1"的標(biāo)簽的爺爺結(jié)點(diǎn)的id
find7 = tree.xpath('//a[starts-with(@title, "C")]/../../@id')
print('find7 = {}'.format(find7))
輸出如下:
find1 = [‘編程’, ‘微博’, ‘百度貼吧’, ‘天貓?zhí)詫殹? ‘京東購(gòu)物’, ‘編程’, ‘安全衛(wèi)士’, ‘視頻娛樂’, ‘年輕娛樂’, ‘搜索引擎’]
find2 = [‘編程’, ‘微博’]
find3 = [‘編程’]
find4 = [‘編程’, ‘天貓?zhí)詫殹痌
find5 = [‘編程’, ‘搜索引擎’]
find6 = [‘編程’]
find7 = [‘sitename’]
接下來(lái)我們講講最開始安裝的Xpath的插件有什么作用。在這里我們以百度首頁(yè)為例,按下 Ctrl+Shift+x
,并按下F12喚出開發(fā)者工具,頁(yè)面如下。
上面的黑框就是插件所起的效果,比如說我們獲取上圖的 點(diǎn)擊一下,了解更多
字樣,那我們就可以在黑框中輸入我們的Xpath查詢語(yǔ)句,黑框右邊就會(huì)出現(xiàn)語(yǔ)句查詢得到的結(jié)果,非常實(shí)用。
當(dāng)然,如果你不想這樣看得眼花繚亂的去寫Xpath也行,只需要在F12開發(fā)者模式下右擊你想要查詢的元素,選擇復(fù)制Xpath即可,如下圖所示,實(shí)際上,我們一般選擇的是這種方法,雖然有的時(shí)候復(fù)制的并不是最簡(jiǎn)的Xpath語(yǔ)法,但是這確實(shí)有效。
2. jsonpath
jsonpath使用前必須安裝jsonpath的包,jsonpath 是參照xpath表達(dá)式來(lái)解析xml文檔的方式,jsonpath的入門可以參考這篇文章,在這里,我將主要的點(diǎn)進(jìn)行一下說明。
學(xué)會(huì)了Xpath的語(yǔ)法,那么jsonpath的語(yǔ)法其實(shí)可以對(duì)照著進(jìn)行學(xué)習(xí)。
Xpath | jsonpath | 說明 |
---|---|---|
/ | $ | 表示根元素 |
. | @ | 當(dāng)前元素 |
/ | . or [] | 子元素 |
… | 父元素 | |
// | … | 遞歸下降,JSONPath是從E4X借鑒的。 |
* | * | 通配符,表示所有的元素 |
@ | 屬性訪問字符 | |
[] | [] | 子元素操作符 |
| | [,] | 邏輯或。jsonpath允許name或者 [start:end:step] 的數(shù)組分片索引 |
[] | ?() | 應(yīng)用過濾表示式 |
為了更加直觀一點(diǎn),我把文章的例子搞過來(lái),大家可以對(duì)照著看。
測(cè)試數(shù)據(jù)如下:
{ "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}}
}
XPath | JSONPath | 結(jié)果 |
---|---|---|
/store/book/author | $.store.book[*].author | 書點(diǎn)所有書的作者 |
//author | $..author | 所有的作者 |
/store/* | $.store.* | store的所有元素。所有的bookst和bicycle |
/store//price | $.store..price | store里面所有東西的price |
//book[3] | $..book[2] | 第三個(gè)書 |
//book[last()] | $..book[(@.length-1)] | 最后一本書 |
//book[position() < 3] | $..book[0,1] $..book[:2] | 前面的兩本書。 |
//book[isbn] | $..book[?(@.isbn)] | 過濾出所有的包含isbn的書。 |
//book[price<10] | $..book[?(@.price<10)] | 過濾出價(jià)格低于10的書。 |
//* | $..* | 所有元素。 |
3. BeautifulSoup
BeautifulSoup
庫(kù)也是解析爬蟲的一大利器,BeautifulSoup
庫(kù)的解析速度比 Xpath
相對(duì)來(lái)說更慢一些,但是使用方法簡(jiǎn)單了不少。這里將BeautifulSoup
常用的方法例舉如下:
方法 | 含義 |
---|---|
soup.a | 找到第一個(gè) a 標(biāo)簽 |
soup.a.name | 找到第一個(gè) a 標(biāo)簽,并輸出標(biāo)簽名 |
soup.a.attrs | 找到第一個(gè) a 標(biāo)簽,并將標(biāo)簽屬性以字典列出 |
soup.a.get_text() | 找到第一個(gè) a 標(biāo)簽,并輸出其內(nèi)容 |
soup.find('a', class_='name1') | 找到第一個(gè) class='name1' 的 a 標(biāo)簽 |
soup.find_all('a') | 找到全部的 a 標(biāo)簽,并以列表形式返回 |
soup.find_all(['a', 'span']) | 找到全部的 a 標(biāo)簽和 span 標(biāo)簽 |
soup.find_all('a', limit=2) | 找到前兩個(gè) a 標(biāo)簽 |
soup.select('a') | 找到全部的 a 標(biāo)簽,并以列表返回 |
soup.select('.a1') | 找到全部的 class='a1' 的標(biāo)簽 |
soup.select('#a1') | 找到全部的 id=a1 的標(biāo)簽 |
soup.select('a[class]') | 找到全部的擁有 class 屬性的 a 標(biāo)簽 |
soup.select('a[class='name1']') | 找到全部的 class='name1' 的 a 標(biāo)簽 |
soup.select('a', 'span') | 找到全部的 a 標(biāo)簽和 span 標(biāo)簽 |
soup.select('div li') | 找到 div 后代中的所有 li 標(biāo)簽 |
soup.select('div > li') | 找到 div 子代(即下面第一層級(jí))中的所有 li 標(biāo)簽 |
我們?nèi)砸缘谝还?jié)中 Xpath 的HTML代碼為例進(jìn)行查詢。
from bs4 import BeautifulSoup# 使用lxml解析器
soup = BeautifulSoup(open("test.html", encoding='utf-8'), "lxml")find1 = soup.a
print('find1 = {}'.format(find1.get_text()))
print('find1.name = {}'.format(find1.name))
print('find1.attrs = {}'.format(find1.attrs))# 找到id=site 的標(biāo)簽
find2 = soup.select('#site')
print('find2 = {}'.format(find2))# 找到前兩個(gè) a 標(biāo)簽和 li 標(biāo)簽
find3 = soup.find_all(['a', 'li'], limit=2)
print('find3 = {}'.format(find3))# 找到class=th 的 a 標(biāo)簽
find4 = soup.select('a[class="th"]')
print('find4 = {}'.format(find4))
4. 正則表達(dá)式
最后我們來(lái)講一講正則表達(dá)式。爬蟲解析數(shù)據(jù)中最出名的當(dāng)屬正則表達(dá)式了,但是正則表達(dá)式并不是僅僅在爬蟲中才有使用,很多的搜索功能或者篩選功能都支持正則表達(dá)式。聊到Python正則表達(dá)式的支持,首先肯定會(huì)想到re庫(kù),這是一個(gè)Python處理文本的標(biāo)準(zhǔn)庫(kù),下面我就來(lái)詳細(xì)講講re庫(kù)的使用。
4.1 特殊符號(hào)
正則表達(dá)式中存在這一些特殊符號(hào),這些符號(hào)的使用能夠帶來(lái)不同的匹配作用,介紹如下:
模式 | 描述 |
---|---|
. | 匹配除換行符 \n 之外的任何 單字符。要匹配 . ,請(qǐng)使用 \. 。 |
* | 匹配前面的 子表達(dá)式 零次或多次。要匹配 * 字符,請(qǐng)使用 \*。 |
+ | 匹配前面的 子表達(dá)式 一次或多次。要匹配 + 字符,請(qǐng)使用 \+。 |
? | 匹配前面的 子表達(dá)式 零次或一次,或指明一個(gè)非貪婪限定符。要匹配 ? 字符,請(qǐng)使用 \?。 |
^ | 匹配輸入字符串的開始位置,在方括號(hào)表達(dá)式中使用時(shí),表示不接受該字符集合。要匹配 ^ 字符本身,請(qǐng)使用 \^。 |
$ | 匹配字符串的結(jié)尾位置。如果設(shè)置了Multiline 屬性,則 $ 也匹配 \n 或 \r。要匹配 $ 字符本身,請(qǐng)使用 \$。 |
\ | 轉(zhuǎn)義字符。將下一個(gè)字符標(biāo)記為或特殊字符、或原義字符、或向后引用、或八進(jìn)制轉(zhuǎn)義符。 |
| | 關(guān)系符號(hào)“或”。指明兩項(xiàng)之間的一個(gè)選擇。要匹配 |,請(qǐng)使用 \|。 |
4.2 特殊字符
正則表達(dá)式中還存在一些以轉(zhuǎn)義字符開頭的特殊字符,這些符號(hào)也能表示一些匹配的規(guī)則。
模式 | 描述 |
---|---|
\d | 匹配數(shù)字:[0-9] |
\D | 匹配非數(shù)字 |
\s | 匹配任何空白字符 |
\S | 匹配非空白字符 |
\w | 匹配字母數(shù)字及下劃線 |
\W | 匹配非字母數(shù)字及下劃線 |
\A | 僅匹配字符串開頭,同^ |
\Z | 僅匹配字符串結(jié)尾,同$ |
\b | 匹配一個(gè)單詞邊界,也就是指單詞和空格間的位置。例如, ‘er\b’ 可以匹配"never" 中的 ‘er’,但不能匹配 “verb” 中的 ‘er’ |
\B | 匹配非單詞邊界?!甧r\B’ 能匹配 “verb” 中的 ‘er’,但不能匹配 “never” 中的 ‘er’ |
4.3 限定符
限定符用來(lái)指定正則表達(dá)式的一個(gè)給定 子字符串 必須要出現(xiàn)多少次才能滿足匹配。
字符 | 描述 |
---|---|
( ) | 標(biāo)記一個(gè) 子表達(dá)式 的開始和結(jié)束位置。子表達(dá)式可以獲取供以后使用。要匹配這些字符,請(qǐng)使用 \( 和 \)。 |
[ ] | 標(biāo)記一個(gè) 中括號(hào)表達(dá)式 的開始和結(jié)束位置。表示字符集合。要匹配這些字符,請(qǐng)使用 \[ 和 \]。 |
{ } | 標(biāo)記 限定符表達(dá)式 的開始和結(jié)束位置。要匹配這些字符,請(qǐng)使用 \{ 和 \}。 |
{n} | n 是一個(gè)非負(fù)整數(shù)。它前面的 字符 或 子字符串 匹配確定的 n 次。 |
{n,} | n 是一個(gè)非負(fù)整數(shù)。它前面的 字符 或 子字符串 至少匹配n 次。 |
{,m} | m 為非負(fù)整數(shù),它前面的 字符 或 子字符串 最多匹配 m 次。 |
{n,m} | n 和 m 均為非負(fù)整數(shù),其中n <= m。它前面的 字符 或 子字符串 最少匹配 n 次且最多匹配 m 次。 |
4.3 常用函數(shù)
接下來(lái)介紹 re
庫(kù)中的常用函數(shù)。
-
match
re.match
嘗試從字符串的起始位置匹配一個(gè)模式,如果不是起始位置匹配成功的話,match()
就返回None
,只匹配一個(gè)。
函數(shù)的使用如下:re.match(pattern, string, flags=0)
其中
pattern
是要匹配的正則表達(dá)式,string
是要匹配的字符串,flags
是正則表達(dá)式匹配的模式。 -
search
re.search
掃描整個(gè)字符串并返回第一個(gè)成功的匹配。
函數(shù)的使用如下:re.search(pattern, string, flags=0)
其中
pattern
是要匹配的正則表達(dá)式,string
是要匹配的字符串,flags
是正則表達(dá)式匹配的模式。示例如下:import res = 'Red里面香克斯帥爆了好吧' pattern = r'香克斯.' print('re.search結(jié)果:{}'.format(re.search(pattern, s).group())) print('re.match結(jié)果:{}'.format(re.match(pattern, s)))
輸出結(jié)果為
re.search結(jié)果:香克斯帥
re.match結(jié)果:None -
sub
re.sub
為re
模塊的替換函數(shù)。
函數(shù)的使用如下:re.sub(pattern, repl, string, count=0, flags=0)
其中
pattern
是要匹配的正則表達(dá)式,repl
是要替換成什么字符串,string
原始字符串,count
表示模式匹配后替換的最大次數(shù),默認(rèn) 0 表示替換所有的匹配,flags
是正則表達(dá)式匹配的模式。 -
compile
re.compile
函數(shù)用于編譯正則表達(dá)式,生成一個(gè)正則表達(dá)式(Pattern)對(duì)象,供match()
和search()
這兩個(gè)函數(shù)使用。
函數(shù)的使用如下:re.compile(pattern[, flags])
其中
pattern
是要匹配的正則表達(dá)式,flags
可選,是正則表達(dá)式匹配的模式。示例如下。import res = 'Red里面香克斯帥爆了好吧' pattern = re.compile(r'香克斯.') m = pattern.search(s) print('匹配結(jié)果:{}'.format(m.group())) print('替換結(jié)果:{}'.format(re.sub('香克斯', '路飛', s)))
輸出結(jié)果如下:
匹配結(jié)果:香克斯帥
替換結(jié)果:Red里面路飛帥爆了好吧 -
findall
re.findall
在字符串中找到正則表達(dá)式所匹配的所有子串,并返回一個(gè)列表,如果有多個(gè)匹配模式,則返回元組列表,如果沒有找到匹配的,則返回空列表。
函數(shù)的使用如下:findall(string[, pos[, endpos]])
其中
string
是字符串,pos
是可選參數(shù),表示匹配開始的位置,endpos
是可選參數(shù),表示匹配結(jié)束的位置。 -
split
re.split
split 方法按照能夠匹配的子串將字符串分割后返回列表。
函數(shù)的使用如下:re.split(pattern, string[, maxsplit=0, flags=0])
其中
pattern
是匹配的正則表達(dá)式,string
是要匹配的字符串,maxsplit
分隔次數(shù),maxsplit=1 分隔一次,默認(rèn)為 0,不限制次數(shù),flags
是匹配模式。示例如下:import res = 'Red里面香克斯帥爆了好吧,香克斯牛皮' pattern = re.compile(r'香克斯.') m = pattern.findall(s) print('匹配結(jié)果:{}'.format(m)) print('切分結(jié)果:{}'.format(re.split('香克斯.', s)))
最后,對(duì)于 search()
以及 match()
函數(shù)匹配后返回的 Match 對(duì)象,都有以下的方法可以使用:
方法 | 描述 |
---|---|
group([group1, …]) | 獲得一個(gè)或多個(gè)分組匹配的字符串,當(dāng)要獲得整個(gè)匹配的子串時(shí),可直接使用 group() 或 group(0) |
start([group]) | 獲取分組匹配的子串在整個(gè)字符串中的起始位置 |
end([group]) | 獲取分組匹配的子串在整個(gè)字符串中的結(jié)束位置 |
span([group]) | 方法返回 (start(group), end(group)) |
4.4 匹配策略
正則表達(dá)式中常用的匹配策略有四種,可以在各函數(shù)的flag中進(jìn)行模式指定。
-
IGNORECASE
可以使用re.IGNORECASE
或re.I
標(biāo)志。進(jìn)行忽略大小寫的匹配。示例如下:import res = 'Red里面香克斯帥爆了好吧,香克斯牛皮' pattern1 = re.compile(r'r') pattern2 = re.compile(r'r', re.I) print('默認(rèn)情況:{}'.format(pattern1.findall(s))) print('改變后為:{}'.format(pattern2.findall(s)))
輸出結(jié)果為
默認(rèn)情況:[]
改變后為:[‘R’] -
ASCII
可以使用re.ASCII
或re.A
標(biāo)志。讓\w
,\W
,\b
,\B
,\d
,\D
,\s
和\S
只匹配ASCII,而不是Unicode。示例如下:import res = 'Red里面香克斯帥爆了好吧\n香克斯牛皮' pattern1 = re.compile(r'\w+') pattern2 = re.compile(r'\w+', re.A) print('默認(rèn)情況:{}'.format(pattern1.findall(s))) print('改變后為:{}'.format(pattern2.findall(s)))
輸出結(jié)果為
默認(rèn)情況:[‘Red里面香克斯帥爆了好吧’, ‘香克斯牛皮’]
改變后為:[‘Red’] -
DOTALL
可以使用re.DOTALL
或re.S
標(biāo)志。DOT表示.
,ALL表示所有,連起來(lái)就是.
匹配所有,包括換行符\n。默認(rèn)模式下.是不能匹配行符\n的。示例如下:import res = 'Red里面香克斯帥爆了好吧\n香克斯牛皮' pattern1 = re.compile(r'香克斯.*') pattern2 = re.compile(r'香克斯.*', re.S) print('默認(rèn)情況:{}'.format(pattern1.findall(s))) print('改變后為:{}'.format(pattern2.findall(s)))
輸出結(jié)果為
默認(rèn)情況:[‘香克斯帥爆了好吧’, ‘香克斯牛皮’]
改變后為:[‘香克斯帥爆了好吧\n香克斯牛皮’] -
MULTILINE
可以使用re.MULTILINE
或re.M
標(biāo)志。多行模式,當(dāng)某字符串中有換行符\n,默認(rèn)模式下是不支持換行符特性的,影響 ^ 和 $。示例如下:import res = '香克斯Red里面帥爆了好吧\n香克斯牛皮' pattern1 = re.compile(r'^香克斯') pattern2 = re.compile(r'^香克斯', re.M) print('默認(rèn)情況:{}'.format(pattern1.findall(s))) print('改變后為:{}'.format(pattern2.findall(s)))
輸出結(jié)果為
默認(rèn)情況:[‘香克斯’]
改變后為:[‘香克斯’, ‘香克斯’] -
VERBOSE
可以使用re.VERBOSE
或re.X
標(biāo)志。詳細(xì)模式,可以在正則表達(dá)式中加注解,會(huì)忽略正則表達(dá)式中的空格和#
后面的注釋。示例如下:import res = '香克斯Red里面帥爆了好吧香克斯牛皮' pattern1 = re.compile(r'^香克斯 #四皇之一') pattern2 = re.compile(r'^香克斯 #四皇之一', re.X) print('默認(rèn)情況:{}'.format(pattern1.findall(s))) print('改變后為:{}'.format(pattern2.findall(s)))
輸出結(jié)果為
默認(rèn)情況:[]
改變后為:[‘香克斯’]
4.5 常用正則
正則表達(dá)式 | 解釋 |
---|---|
[\u4e00-\u9fa5]+ | 匹配漢字 |
這里我給一個(gè)就行,其他的有什么功能都可以去查。