1. 程式人生 > >爬蟲數據解析的三種方式

爬蟲數據解析的三種方式

.text java pan value 轉換成 元素 三種 utf-8 hello

一.正則表達式解析

常用正則表達式回顧:

   單字符:
        . : 除換行以外所有字符
        [] :[aoe] [a-w] 匹配集合中任意一個字符
        \d :數字  [0-9]
        \D : 非數字
        \w :數字、字母、下劃線、中文
        \W : 非\w
        \s :所有的空白字符包,括空格、制表符、換頁符等等。等價於 [ \f\n\r\t\v]。
        \S : 非空白
    數量修飾:
        * : 任意多次  >=0
        + : 至少1次   >=1
? : 可有可無 0次或者1次 {m} :固定m次 hello{3,} {m,} :至少m次 {m,n} :m-n次 邊界: $ : 以某某結尾 ^ : 以某某開頭 分組: (ab) 貪婪模式: .* 非貪婪(惰性)模式: .*? re.I : 忽略大小寫 re.M :多行匹配 re.S :單行匹配 re.sub(正則表達式, 替換內容, 字符串)

 

二 Xpath解析

XPath在Python的爬蟲學習中,起著舉足輕重的地位,對比正則表達式 re兩者可以完成同樣的工作,實現的功能也差不多,但XPath明顯比re具有優勢,在網頁分析上使re退居二線。

測試頁面數據

<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>
 

常用xpath表達式

‘‘‘
屬性定位:
    #找到class屬性值為song的div標簽
    //div[@class="song"] 
層級&索引定位:
    #找到class屬性值為tang的div的直系子標簽ul下的第二個子標簽li下的直系子標簽a
    //div[@class="tang"]/ul/li[2]/a
邏輯運算:
    #找到href屬性值為空且class屬性值為du的a標簽
    //a[@href="" and @class="du"]
模糊匹配:
    //div[contains(@class, "ng")]
    //div[starts-with(@class, "ta")]
取文本:
    # /表示獲取某個標簽下的文本內容
    # //表示獲取某個標簽下的文本內容和所有子標簽下的文本內容
    //div[@class="song"]/p[1]/text()
    //div[@class="tang"]//text()
取屬性:
    //div[@class="tang"]//li[2]/a/@href
‘‘‘

代碼中使用xpath表達式進行數據解析

1.下載:pip install lxml
2.導包:from lxml import etree

3.將html文檔或者xml文檔轉換成一個etree對象,然後調用對象中的方法查找指定的節點

  2.1 本地文件:tree = etree.parse(文件名)
                tree.xpath("xpath表達式")

  2.2 網絡數據:tree = etree.HTML(網頁內容字符串)
                tree.xpath("xpath表達式")

三 Beautiful Soup

3.1 簡介

簡單來說,Beautiful Soup是python的一個庫,最主要的功能是從網頁抓取數據。官方解釋如下:

‘‘‘
Beautiful Soup提供一些簡單的、python式的函數用來處理導航、搜索、修改分析樹等功能。
它是一個工具箱,通過解析文檔為用戶提供需要抓取的數據,因為簡單,所以不需要多少代碼就可以寫出一個完整的應用程序。
‘‘‘

Beautiful Soup 是一個可以從HTML或XML文件中提取數據的Python庫.它能夠通過你喜歡的轉換器實現慣用的文檔導航,查找,修改文檔的方式.Beautiful Soup會幫你節省數小時甚至數天的工作時間.你可能在尋找 Beautiful Soup3 的文檔,Beautiful Soup 3 目前已經停止開發,官網推薦在現在的項目中使用Beautiful Soup 4。

安裝

pip3 install beautifulsoup4

解析器

Beautiful Soup支持Python標準庫中的HTML解析器,還支持一些第三方的解析器,如果我們不安裝它,則 Python 會使用 Python默認的解析器,lxml 解析器更加強大,速度更快,推薦安裝。

pip3 install lxml

另一個可供選擇的解析器是純Python實現的 html5lib , html5lib的解析方式與瀏覽器相同,可以選擇下列方法來安裝html5lib:

pip install html5lib

解析器對比:
技術分享圖片

3.2 BS語法

使用流程:

- 導包:from bs4 import BeautifulSoup
- 使用方式:可以將一個html文檔,轉化為BeautifulSoup對象,然後通過對象的方法或者屬性去查找指定的節點內容
    (1)轉化本地文件:
         - soup = BeautifulSoup(open(本地文件), lxml)
    (2)轉化網絡文件:
         - soup = BeautifulSoup(字符串類型或者字節類型, lxml)
    (3)打印soup對象顯示內容為html文件中的內容

基礎鞏固:

1)根據標簽名查找
        - soup.a   只能找到第一個符合要求的標簽
    (2)獲取屬性
        - soup.a.attrs  獲取a所有的屬性和屬性值,返回一個字典
        - soup.a.attrs[href]   獲取href屬性
        - soup.a[href]   也可簡寫為這種形式
    (3)獲取內容
        - soup.a.string
        - soup.a.text
        - soup.a.get_text()
       【註意】如果標簽還有標簽,那麽string獲取到的結果為None,而其它兩個,可以獲取文本內容
    (4)find:找到第一個符合要求的標簽
        - soup.find(a)  找到第一個符合要求的
        - soup.find(a, title="xxx")
        - soup.find(a, alt="xxx")
        - soup.find(a, class_="xxx")
        - soup.find(a, id="xxx")
    (5)find_all:找到所有符合要求的標簽
        - soup.find_all(a)
        - soup.find_all([a,b]) 找到所有的a和b標簽
        - soup.find_all(a, limit=2)  限制前兩個
    (6)根據選擇器選擇指定的內容
               select:soup.select(#feng)
        - 常見的選擇器:標簽選擇器(a)、類選擇器(.)、id選擇器(#)、層級選擇器
            - 層級選擇器:
                div .dudu #lala .meme .xixi  下面好多級
                div > p > a > .lala          只能是下面一級
        【註意】select選擇器返回永遠是列表,需要通過下標提取指定的對象

重點方法

BeautifulSoup定義了很多搜索方法,這裏著重介紹2個: find() 和 find_all() .其它方法的參數和用法類似

1、find_all()方法

#搜索文檔樹:BeautifulSoup定義了很多搜索方法,這裏著重介紹2個: find() 和 find_all() .其它方法的參數和用法類似
html_doc = """
<html><head><title>The Dormouses story</title></head>
<body>
<p id="my p" class="title"><b id="bbb" class="boldest">The Dormouses 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>
"""

from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,lxml)


#1、name的五種過濾器: 字符串、正則表達式、列表、True、方法
#1.1、字符串:即標簽名
print(soup.find_all(b))

#1.2、正則表達式
import re
print(soup.find_all(re.compile(^b))) #找出b開頭的標簽,結果有body和b標簽

#1.3、列表:如果傳入列表參數,Beautiful Soup會將與列表中任一元素匹配的內容返回.下面代碼找到文檔中所有<a>標簽和<b>標簽:
print(soup.find_all([a,b]))

#1.4、True:可以匹配任何值,下面代碼查找到所有的tag,但是不會返回字符串節點
print(soup.find_all(True))
for tag in soup.find_all(True):
    print(tag.name)

#1.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))



#2、按照類名查找,註意關鍵字是class_,class_=value,value可以是五種選擇器之一
print(soup.find_all(a,class_=sister)) #查找類為sister的a標簽
print(soup.find_all(a,class_=sister ssss)) #查找類為sister和sss的a標簽,順序錯誤也匹配不成功
print(soup.find_all(class_=re.compile(^sis))) #查找類為sister的所有標簽

#3、attrs
print(soup.find_all(p,attrs={class:story}))

#4、text: 值可以是:字符,列表,True,正則
print(soup.find_all(text=Elsie))
print(soup.find_all(a,text=Elsie))

#5、limit參數:如果文檔樹很大那麽搜索會很慢.如果我們不需要全部結果,可以使用 limit 參數限制返回結果的數量.效果與SQL中的limit關鍵字類似,當搜索到的結果數量達到 limit 的限制時,就停止搜索返回結果
print(soup.find_all(a,limit=2))

#6、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)

‘‘‘

2、find( name , attrs , recursive , text , **kwargs )

find_all() 方法將返回文檔中符合條件的所有tag,盡管有時候我們只想得到一個結果.比如文檔中只有一個<body>標簽,那麽使用 find_all() 方法來查找<body>標簽就不太合適, 使用 find_all 方法並設置 limit=1 參數不如直接使用 find() 方法.下面兩行代碼是等價的:

soup.find_all(title, limit=1)
# [<title>The Dormouses story</title>]
soup.find(title)
# <title>The Dormouses story</title>

唯一的區別是 find_all() 方法的返回結果是值包含一個元素的列表,而 find() 方法直接返回結果.
find_all() 方法沒有找到目標是返回空列表, find() 方法找不到目標時,返回 None .
print(soup.find("nosuchtag"))
# None

soup.head.title 是 tag的名字方法的簡寫.這個簡寫的原理就是多次調用當前tag的 find() 方法:

soup.head.title
# <title>The Dormouses story</title>
soup.find("head").find("title")
# <title>The Dormouses story</title>

爬蟲數據解析的三種方式