一、正則表達式解析
https://www.cnblogs.com/Zzbj/p/9622310.html
https://www.cnblogs.com/Zzbj/p/9629299.html
https://www.cnblogs.com/Zzbj/p/9630218.html
1、簡單介紹
單字符: . 匹配除換行符以外的任意字符 \w 匹配字母或數字或下划線 \d 匹配數字 \s 匹配任意的空白符 \W 匹配非字母或數字或下划線 \D 匹配非數字 \S 匹配非空白符 \n 匹配一個換行符 \t 匹配一個制表符(Tab) \b 匹配一個邊界(其前后必須是不同類型的字符, 可以組成單詞的字符為一種類型, 不可組成單詞的字符(包括字符串的開始和結束)為另一種類型) a|b 匹配字符a或字符b () 匹配括號內的表達式,一個組表示一個整體 [...] 匹配字符組中的字符 [^...] 匹配除了字符組中字符的所有字符 數量修飾: * : 任意多次 >=0 + : 至少1次 >=1 ? : 可有可無 0次或者1次 {m} :固定m次 hello{3,} {m,} :至少m次 {m,n} :m到n次 邊界: $ : 以某某結尾 ^ : 以某某開頭 貪婪模式: .* 非貪婪(惰性)模式: .*? re模塊: 1.匹配方法 re.findall(正則, 字符串) # 返回一個匹配到所有結果的列表 re.search(正則, 字符串) # 返回一個對象,用group()取值,只匹配一個結果 re.match(正則, 字符串) # 返回一個對象,用group()取值,只匹配一個結果,且默認正則有 ^ 以什么開頭 re.finditer(正則, 字符串) # 返回一個存放匹配結果的迭代器(節省內存) 例如: ret = re.finditer('\d+','qwe123abc456') print(ret) # <callable_iterator object at 0x000001CA560DF860> print(ret.__next__().group()) # 123 print(next(ret).group()) # 456 # 或者單獨使用for for i in ret: print(i.group()) # 123 456 compile:將正則表達式編譯成為一個正則表達式對象 com = re.compile('\d+') print(com) # re.compile('\\d+') ret = com.search('qwe123abc456') print(ret.group()) # 123 ret = com.findall('qwe123abc456') print(ret) # ['123', '456'] ret = com.finditer('qwe123abc456') for i in ret: print(i.group()) # 123 456 2.其他內容 優先顯示分組內容() ?的應用 ? 表示量詞,零次或者一次 (?:正則表達式) 表示取消優先顯示功能 (?P<組名>正則表達式) 表示給這個組起一個名字 (?P=組名) 表示引用之前組的名字,引用部分匹配到的內容必須和之前那個組中的內容一模一樣 re.I : 忽略大小寫 re.M :多行匹配 re.S :單行匹配 re.sub(正則表達式, 替換內容, 字符串) re.split(正則表達式, 字符串)
二、Xpath解析
1、什么是Xpath
XPath 是一門在 XML 文檔中查找信息的語言。XPath 用於在 XML 文檔中通過元素和屬性進行導航。
XPath 使用路徑表達式在 XML 文檔中進行導航
XPath 包含一個標准函數庫
XPath 是 XSLT 中的主要元素
XPath 是一個 W3C 標准
2、Xpath的使用
1.下載:pip install lxml 2.導包:from lxml import etree 3.將html文檔或者xml文檔轉換成一個etree對象,然后調用對象中的方法查找指定的節點 3.1 本地文件:tree = etree.parse(文件名) tree.xpath("xpath表達式") 3.2 網絡數據:tree = etree.HTML(網頁內容字符串) tree.xpath("xpath表達式")
三、Xpath的語法
摘抄自w3school教程:http://www.w3school.com.cn/xpath/xpath_syntax.asp
1、xpath語法
表達式 | 描述 |
---|---|
nodename | 選取此節點的所有子節點。 |
/ | 從根節點選取。 |
// | 從匹配選擇的當前節點選擇文檔中的節點,而不考慮它們的位置。 |
. | 選取當前節點。 |
.. | 選取當前節點的父節點。 |
@ | 選取屬性。 |
以下面這個xml為例子
<?xml version="1.0" encoding="ISO-8859-1"?> <bookstore> <book> <title lang="eng">Harry Potter</title> <price>29.99</price> </book> <book> <title lang="eng">Learning XML</title> <price>39.95</price> </book> </bookstore>
xml.xpath(“bookstore”) 表示選取 bookstore 元素的所有子節點
xml.xpath(“/bookstore”) 表示選取根元素 bookstore。
xml.xpath(“bookstore/book”) 選取屬於 bookstore 的子元素的所有 book 元素。
xml.xpath(“//book”) 選取所有 book 子元素,而不管它們在文檔中的位置。
xml.xpath(“bookstore//book”) 選擇屬於 bookstore 元素的后代的所有 book 元素,而不管它們位於 bookstore 之下的什么位置。
xml.xpath(“//@lang”) 選取名為 lang 的所有屬性。
2、謂語
路徑表達式 | 結果 |
---|---|
/bookstore/book[1] | 選取屬於 bookstore 子元素的第一個 book 元素。 |
/bookstore/book[last()] | 選取屬於 bookstore 子元素的最后一個 book 元素。 |
/bookstore/book[last()-1] | 選取屬於 bookstore 子元素的倒數第二個 book 元素。 |
/bookstore/book[position()<3] | 選取最前面的兩個屬於 bookstore 元素的子元素的 book 元素。 |
//title[@lang] | 選取所有擁有名為 lang 的屬性的 title 元素。 |
//title[@lang=’eng’] | 選取所有 title 元素,且這些元素擁有值為 eng 的 lang 屬性。 |
/bookstore/book[price>35.00] | 選取 bookstore 元素的所有 book 元素,且其中的 price 元素的值須大於 35.00。 |
/bookstore/book[price>35.00]/title | 選取 bookstore 元素中的 book 元素的所有 title 元素,且其中的 price 元素的值須大於 35.00。 |
3、選取未知節點
通配符 | 描述 |
---|---|
* | 匹配任何元素節點。 |
@* | 匹配任何屬性節點。 |
node() | 匹配任何類型的節點。 |
例子:
路徑表達式 | 結果 |
---|---|
/bookstore/* | 選取 bookstore 元素的所有子元素。 |
//* | 選取文檔中的所有元素。 |
//title[@*] | 選取所有帶有屬性的 title 元素。 |
4、選取若干路徑
通過在路徑表達式中使用“|”運算符,您可以選取若干個路徑。
路徑表達式 | 結果 |
---|---|
//book/title | //book/price | 選取 book 元素的所有 title 和 price 元素。 |
//title | //price | 選取文檔中的所有 title 和 price 元素。 |
/bookstore/book/title | //price | 選取屬於 bookstore 元素的 book 元素的所有 title 元素,以及文檔中所有的 price 元素。 |
5、軸
軸可定義相對於當前節點的節點集。
軸名稱 | 結果 |
---|---|
ancestor | 選取當前節點的所有先輩(父、祖父等)。 |
ancestor-or-self | 選取當前節點的所有先輩(父、祖父等)以及當前節點本身。 |
attribute | 選取當前節點的所有屬性。 |
child | 選取當前節點的所有子元素。 |
descendant | 選取當前節點的所有后代元素(子、孫等)。 |
descendant-or-self | 選取當前節點的所有后代元素(子、孫等)以及當前節點本身。 |
following | 選取文檔中當前節點的結束標簽之后的所有節點。 |
namespace | 選取當前節點的所有命名空間節點。 |
parent | 選取當前節點的父節點。 |
preceding | 選取文檔中當前節點的開始標簽之前的所有節點。 |
preceding-sibling | 選取當前節點之前的所有同級節點。 |
self | 選取當前節點。 |
步的語法:
軸名稱::節點測試[謂語]
例1:
例子 | 結果 |
---|---|
child::book | 選取所有屬於當前節點的子元素的 book 節點。 |
attribute::lang | 選取當前節點的 lang 屬性。 |
child::* | 選取當前節點的所有子元素。 |
attribute::* | 選取當前節點的所有屬性。 |
child::text() | 選取當前節點的所有文本子節點。 |
child::node() | 選取當前節點的所有子節點。 |
descendant::book | 選取當前節點的所有 book 后代。 |
ancestor::book | 選擇當前節點的所有 book 先輩。 |
ancestor-or-self::book | 選取當前節點的所有 book 先輩以及當前節點(如果此節點是 book 節點) |
child::*/child::price | 選取當前節點的所有 price 孫節點。 |
例2:
原文:https://blog.csdn.net/qq_37059367/article/details/83819828
<div> <a id="1" href="www.baidu.com">我是第1個a標簽</a> <p>我是p標簽</p> <a id="2" href="www.baidu.com">我是第2個a標簽</a> <a id="3" href="www.baidu.com">我是第3個a標簽</a> <a id="4" href="www.baidu.com">我是第4個a標簽</a> <p>我是p標簽</p> <a id="5" href="www.baidu.com">我是第5個a標簽</a> </div>
獲取第三個a標簽的下一個a標簽:"//a[@id='3']/following-sibling::a[1]" 獲取第三個a標簽后面的第N個標簽:"//a[@id='3']/following-sibling::*[N]" 獲取第三個a標簽的上一個a標簽:"//a[@id='3']/preceding-sibling::a[1]" 獲取第三個a標簽的前面的第N個標簽:"//a[@id='3']/preceding-sibling::*[N]" 獲取第三個a標簽的父標簽:"//a[@id=='3']/.."
6、功能函數
使用功能函數能夠更好的進行模糊搜索
函數 用法 解釋 starts-with xpath('//div[starts-with(@id,"ma")]') 選取id值以ma開頭的div節點 contains xpath('//div[contains(@id,"ma")]') 選取id值包含ma的div節點 and xpath('//div[contains(@id,"ma") and contains(@id,"in")]') 選取id值包含ma和in的div節點 text() xpath('//div[contains(text(),"ma")]') 選取節點文本包含ma的div節點
7、Element對象
from lxml.etree import _Element for obj in ret: print(obj) print(type(obj)) # from lxml.etree import _Element # 用xpath語法查詢出來的列表,里面的對象都是Element對象 Element對象的內置方法如下: class xml.etree.ElementTree.Element(tag, attrib={}, **extra) tag:string,元素代表的數據種類。 text:string,元素的內容。 tail:string,元素的尾形。 attrib:dictionary,元素的屬性字典。 #針對屬性的操作 clear():清空元素的后代、屬性、text和tail也設置為None。 get(key, default=None):獲取key對應的屬性值,如該屬性不存在則返回default值。 items():根據屬性字典返回一個列表,列表元素為(key, value)。 keys():返回包含所有元素屬性鍵的列表。 set(key, value):設置新的屬性鍵與值。 #針對后代的操作 append(subelement):添加直系子元素。 extend(subelements):增加一串元素對象作為子元素。#python2.7新特性 find(match):尋找第一個匹配子元素,匹配對象可以為tag或path。 findall(match):尋找所有匹配子元素,匹配對象可以為tag或path。 findtext(match):尋找第一個匹配子元素,返回其text值。匹配對象可以為tag或path。 insert(index, element):在指定位置插入子元素。 iter(tag=None):生成遍歷當前元素所有后代或者給定tag的后代的迭代器。#python2.7新特性 iterfind(match):根據tag或path查找所有的后代。 itertext():遍歷所有后代並返回text值。 remove(subelement):刪除子元素。
8、常用xpath查詢示例

html_document = ''' <html lang="en"> <head> <meta charset="UTF-8" /> <title>測試bs4</title> </head> <body> <div> <p>百里守約</p> </div> <div class="song"> <p>李清照</p> <p>王安石</p> <p>蘇軾</p> <p>柳宗元</p> <a href="http://www.song.com/" title="趙匡胤" target="_self"> <span>this is span</span> 宋朝是最強大的王朝,不是軍隊的強大,而是經濟很強大,國民都很有錢</a> <a href="" class="du">總為浮雲能蔽日,長安不見使人愁</a> <img src="http://www.baidu.com/meinv.jpg" alt="" /> </div> <div class="tang"> <ul> <li><a href="http://www.baidu.com" title="qing">清明時節雨紛紛,路上行人欲斷魂,借問酒家何處有,牧童遙指杏花村</a></li> <li><a href="http://www.163.com" title="qin">秦時明月漢時關,萬里長征人未還,但使龍城飛將在,不教胡馬度陰山</a></li> <li><a href="http://www.126.com" alt="qi">岐王宅里尋常見,崔九堂前幾度聞,正是江南好風景,落花時節又逢君</a></li> <li><a href="http://www.sina.com" class="du">杜甫</a></li> <li><a href="http://www.dudu.com" class="du">杜牧</a></li> <li><b>杜小月</b></li> <li><i>度蜜月</i></li> <li><a href="http://www.haha.com" id="feng">鳳凰台上鳳凰游,鳳去台空江自流,吳宮花草埋幽徑,晉代衣冠成古丘</a></li> </ul> </div> </body> </html> '''
from lxml import etree tree = etree.HTML(html_document) # 找到class屬性值為song的div標簽 ret = tree.xpath("//div[@class='song']") # 找到class屬性值為tang的div的直系子標簽ul下的第二個子標簽li下的直系子標簽a ret = tree.xpath("//div[@class='tang']/ul/li[2]/a") # 找到href屬性值為空且class屬性值為du的a標簽 ret = tree.xpath("//a[@class='du' and @href='']") # 模糊匹配,找到class屬性包含'ng'的div標簽 ret = tree.xpath("//div[contains(@class,'ng')]") # 模糊匹配,找到class屬性以'ta'開頭的div標簽 ret = tree.xpath("//div[starts-with(@class,'ta')]") print(ret) # ret是所有符合的Element對象組成的列表 for ele in ret: print(ele) print(ele.tag) print(ele.text) # 還可以直接在找到的Element對象后面使用一些方法 ret = tree.xpath("//div[starts-with(@class,'ta')]/ul/li[1]/a/text()") ret = tree.xpath("//div[starts-with(@class,'ta')]/ul/li[1]/a/@href") for i in ret: print(i)
小結:
// 是不管節點在哪,都會去全局找
/ 是在當節點向下找一層,就是只在當前節點的兒子里面找
四、BeautifulSoup
1、簡介
Beautiful Soup提供一些簡單的、python式的函數用來處理導航、搜索、修改分析樹等功能。
它是一個工具箱,通過解析文檔為用戶提供需要抓取的數據,因為簡單,所以不需要多少代碼就可以寫出一個完整的應用程序。
總而言之,Beautiful Soup是python的一個庫,最主要的功能是從網頁抓取數據
它能夠通過你喜歡的轉換器實現慣用的文檔導航,查找,修改文檔的方式,Beautiful Soup能為我們節省工作時間
需要注意的是Beautiful Soup 3 目前已經停止開發,官網推薦在現在的項目中使用Beautiful Soup 4。
2、安裝
1. 安裝beautifulsoup模塊
pip3 install beautifulsoup4
2. 安裝解析器
Beautiful Soup支持Python標准庫中的HTML解析器,還支持一些第三方的解析器,如果我們不安裝它,則 Python 會使用 Python默認的解析器,lxml 解析器更加強大,速度更快,推薦安裝。
pip3 install lxml
另一個可供選擇的解析器是純Python實現的 html5lib , html5lib的解析方式與瀏覽器相同
pip install html5lib
3. 解析器性能對比
4. 官方文檔
https://beautifulsoup.readthedocs.io/zh_CN/latest/
3、BS使用流程
導包:from bs4 import BeautifulSoup 使用方式:可以將一個html文檔,轉化為BeautifulSoup對象,然后通過對象的方法或者屬性去查找指定的節點內容 1. 轉化本地文件: soup = BeautifulSoup(open('本地文件'), 'lxml') 2. 轉化網絡文件: soup = BeautifulSoup('字符串類型或者字節類型', 'lxml') 3. soup對象即為html文件中的內容
五、BeautifulSoup的使用方法
html文檔:下面的例子都以這個文檔作為查詢的根本

html_doc =""" <html> <head> <title> The Dormouse's story </title> </head> <body> <p class="title" id="myp"> <b class="boldest" id="bbb">The Dormouse's story</b> <span><a href="" class="ming">xiaoming</a></span> <a href="">wanglin</a> </p> <p class="story"> Once upon a time there were three little sisters; and their names were <a class="sister brother" href="http://example.com/elsie" id="link1">Elsie</a> <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a> <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a> and they lived at the bottom of a well. </p> <p class="story"> .... </p> </body> </html> """
from bs4 import BeautifulSoup soup = BeautifulSoup(html_doc, 'lxml') BeautifulSoup找到的都是標簽對象
print(soup.prettify()) # 自動幫我們格式化補全改錯文檔
1、通過Tag(標簽名)找對象
soup.a # 找到第一個a標簽 soup.body.b # 找到第一個body標簽里面的b標簽 soup.find_all('a') # 找到所有a標簽
2、attrs獲取tag的屬性
tag的屬性操作方法與字典一樣 soup.a.attrs # 獲取第一個a標簽的所有屬性,返回一個字典 soup.a.attrs['class'] # 獲取第一個a標簽的class屬性 soup.a.attrs.get('class') # 獲取第一個a標簽的class屬性 soup.a['class'] # 也可簡寫為這種形式 soup.a.name # 獲取第一個a標簽的標簽名
3、添加/刪除/修改tag的屬性
soup.a['href'] = 'www.baidu.com' # 修改屬性 print(soup.a['href']) # www.baidu.com soup.a['id'] = 1 # 增加屬性 print(soup.a.attrs['id']) # 1 del soup.a['class'] # 刪除屬性 print(soup.a.attrs.get('class')) # None
4、獲取標簽的內容
# 當p標簽下只有一個文本時(不嵌套其他標簽),可以取到內容,否則為None print(soup.p.string) # 拿到一個生成器對象, 取到p下所有的文本內容,有嵌套標簽也可以取 print(soup.p.strings) for item in soup.p.strings: print(item) # 取到p下所有的文本內容,有嵌套標簽也可以取 print(soup.p.text) # 跟上面的text一樣 print(soup.p.get_text()) # 取到文檔的所有文本內容,且去掉空白 for line in soup.stripped_strings: print(line)
5、遍歷文檔樹
# 1、嵌套選擇 print(soup.head.title.string) print(soup.body.a.string) # 2、子節點、子孫節點 print(soup.p.contents) # p下所有子節點 print(soup.p.children) # 得到一個迭代器,包含p下所有子節點 for i, child in enumerate(soup.p.children): print(i, child) print(soup.p.descendants) # 獲取子孫節點,p下所有的標簽都會選擇出來 for i, child in enumerate(soup.p.descendants): print(i, child) # 3、父節點、祖先節點 print(soup.a.parent) # 獲取a標簽的父節點 print(soup.a.parents) # 找到a標簽所有的祖先節點,父親的父親,父親的父親的父親... # 4、兄弟節點 print(soup.a.next_sibling) # 下一個兄弟 print(soup.a.previous_sibling) # 上一個兄弟 print(soup.a.next_siblings) # 下面的兄弟們=>生成器對象 print(soup.a.previous_siblings) # 上面的兄弟們=>生成器對象
6、5種過濾器
五種過濾器: 字符串、正則表達式、列表、True、方法 # 1、字符串:即標簽名 print(soup.find_all('b')) # 2、正則表達式 import re print(soup.find_all(re.compile('^b'))) #找出b開頭的標簽,結果有body和b標簽 # 3、列表:如果傳入列表參數,Beautiful Soup會將與列表中任一元素匹配的內容返回.下面代碼找到文檔中所有<a>標簽和<b>標簽: print(soup.find_all(['a','b'])) # 4、True:可以匹配任何值,下面代碼查找到所有的tag,但是不會返回字符串節點 print(soup.find_all(True)) for tag in soup.find_all(True): print(tag.name) # 5、方法:如果沒有合適過濾器,那么還可以定義一個方法,方法只接受一個元素參數 ,如果這個方法返回 True 表示當前元素匹配並且被找到,如果不是則反回 False def has_class_but_no_id(tag): return tag.has_attr('class') and not tag.has_attr('id') print(soup.find_all(has_class_but_no_id))
7、find_all()
find_all()是查找所有符合的標簽 # 1、按照屬性去查找 print(soup.find_all("a", href="http://example.com/lacie")) print(soup.find_all("a", id="link3")) # 1-2、特殊的屬性class:因為class是python的關鍵字,因此這里要查找class屬性要寫成:class_ # 注意關鍵字是class_,class_=value,value可以是五種選擇器之一 print(soup.find_all('a', class_='sister')) # 查找類為sister的a標簽 print(soup.find_all('a',class_='sister brother')) # 查找類為sister和brother的a標簽,順序錯誤也匹配不成功 print(soup.find_all(class_=re.compile('^sis'))) # 查找類為sis開頭的所有標簽 # 2、attrs print(soup.find_all('p', attrs={'class': 'story'})) # 3、text: 值可以是:字符,列表,True,正則 print(soup.find_all(text='Elsie')) print(soup.find_all('a', text='Elsie')) # 4、limit參數:如果文檔樹很大那么搜索會很慢.如果我們不需要全部結果,可以使用 limit 參數限制返回結果的數量.效果與SQL中的limit關鍵字類似,當搜索到的結果數量達到 limit 的限制時,就停止搜索返回結果 print(soup.find_all('a', limit=2)) # 5、recursive:調用tag的find_all()方法時,Beautiful Soup會檢索當前tag的所有子孫節點,如果只想搜索tag的直接子節點,可以使用參數 recursive=False . print(soup.html.find_all('a')) print(soup.html.find_all('a', recursive=False)) ''' 像調用 find_all() 一樣調用tag find_all() 幾乎是Beautiful Soup中最常用的搜索方法,所以我們定義了它的簡寫方法. BeautifulSoup 對象和 tag 對象可以被當作一個方法來使用, 這個方法的執行結果與調用這個對象的 find_all() 方法相同,下面兩行代碼是等價的: soup.find_all("a") soup("a") 這兩行代碼也是等價的: soup.title.find_all(text=True) soup.title(text=True) '''
8、find()
find()是查找第一個符合的標簽,find_all的所有方法find也有一模一樣的。
print(soup.find("a", id="link1")) find_all() 方法將返回文檔中符合條件的所有tag,盡管有時候我們只想得到一個結果.比如文檔中只有一個<body>標簽,那么使用 find_all() 方法來查找<body>標簽就不太合適, 使用 find_all 方法並設置 limit=1 參數不如直接使用 find() 方法.下面兩行代碼是等價的: soup.find_all('title', limit=1) soup.find('title') 唯一的區別是 find_all() 方法的返回結果是值包含一個元素的列表,而 find() 方法直接返回結果. find_all() 方法沒有找到目標是返回空列表, find() 方法找不到目標時,返回 None . print(soup.find("abcdefg")) # None soup.head.title 是 tag的名字方法的簡寫.這個簡寫的原理就是多次調用當前tag的 find() 方法: print(soup.head.title) # <title>The Dormouse's story</title> soup.find("head").find("title") # <title>The Dormouse's story</title> find和find_all的簡寫方式: soup.find("a") 等價於 soup.a soup.find_all("a") 等價於 soup("a") 特別需要注意的是,支持鏈式查找 soup.find("p").find_all("a") 等價於 soup.p("a")
9、css選擇器
CSS篩選器的語法是:標簽名不加任何修飾,類名前加點,id名前加 # BS也給我們提供了跟CSS篩選器一模一樣的語法,用到的方法是 soup.select(),返回類型是 list 1.通過標簽名查找 print(soup.select("title")) print(soup.select("b")) 2.通過類名查找 print(soup.select(".sister")) 3.通過 id 名查找 print(soup.select("#myp")) 4.組合查找 print(soup.select("p #bbb")) # 在p的子孫里面找id為bbb的標簽 print(soup.select("p>#bbb")) # 只在p的兒子里面找id為bbb的標簽 print(soup.select("p.story")) # 找到class為story的p標簽 5.屬性查找 print(soup.select("a[href='http://example.com/tillie']")) # 找到href屬性值為http://example.com/tillie的a標簽