1. 程式人生 > >爬蟲:python之BeautifulSoup(lxml)

爬蟲:python之BeautifulSoup(lxml)

一、簡介

一個靈活又方便的HTML解析庫,處理高效,支援多種解析器,利用它不使用正則表示式也能抓取網頁內容。

解析器使用方法優勢劣勢
python標準庫BeautifulSoup(markup,"html.parser")

python內建標準庫

執行速度適中

文件糾錯能力強

python2.7.3以前的版本容錯能力差
lxml HTML解析器BeautifulSoup(markup,"lxml")

速度快

文件糾錯能力強

需要安裝C語言庫
lxml xml解析器BeautifulSoup(markup,["lxml","xml"])

速度快

唯一的支援解析的xml的解析器

需要安裝C語言庫
html5libBeautifulSoup(markup,"html5lib")

最好的容錯性

以瀏覽器的方式解析文件

生成html5格式的文件

速度慢

不宜懶外部庫

二、lxml解析器的基本使用

#獲取直接子節點:contents、children
#獲取父節點:parent
#獲取兄弟節點:next_siblings、next_sibling、previous_siblings、previous_sibling
html = '''
<html>
    <head>
        <title>The Dormouse's story</title>
    </head>
    <body>
        <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">
                <span>Elsie</span>
            </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,'html.parser')
# contents:獲取直接子節點,返回list型別
print(soup.p.contents)
# children,返回的是可以迭代的,直接列印輸出None
for i in soup.p.children:
    print(i)
print(soup.p.childrensoup)
#獲取 父節點
print(soup.a.parent)
# 獲取兄弟節點
for i in soup.a.next_siblings:#獲取a標籤後面的所有兄弟節點
    print(i)
html = '''
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"><b>The Dormouse's 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>
'''
import lxml
from bs4 import BeautifulSoup
#建立bs物件 bs是使用的python預設的解析器,lxml也是解析器
soup = BeautifulSoup(html,'lxml')
#prettify實現格式化的輸出
print(soup.prettify())
#通過soup標籤名,獲取這個標籤的內容。注意:通過這種方式獲取標籤,如果文件中有多個這樣的標籤,返回的結果是第一個標籤內容
print(soup.a)
print(soup.p)
#獲取名稱name
print(soup.title.name)
print(soup.p.name)
#獲取屬性
print(soup.a['href'])
#獲取文字內容-string、text
print(soup.a.string)
print(soup.a.text)
print(soup.title.string)
print(soup.title.text)
#巢狀選擇,直接通過巢狀的方式獲取
print(soup.p.b.string)
print(soup.head.title.text)

三、lxml解析器標準選擇器、find_all的使用

搜尋文件樹:

(1)find_all():可以根據標籤名、屬性、內容查詢文件

(2)find():返回匹配結果的第一個元素

(3)find_parents() find_parent()

(4)find_next_siblings() find_next_sibling()

(5)find_previous_siblings() find_previous_sibling()

(6)find_all_next() find_next()

(7)find_all_previous() 和 find_previous()

html='''
<div class="panel">
    <div class="panel-heading">
        <h4>Hello</h4>
    </div>
    <div class="panel-body">
        <ul class="list" id="list-1">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
            <li class="element">Jay</li>
        </ul>
        <ul class="list list-small" id="list-2">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
        </ul>
    </div>
</div>
'''
from bs4 import BeautifulSoup
soup = BeautifulSoup(html, 'lxml')
print(soup.find_all('ul'))
print(type(soup.find_all('ul')[0]))
html='''
<div class="panel">
    <div class="panel-heading">
        <h4>Hello</h4>
    </div>
    <div class="panel-body">
        <ul class="list" id="list-1">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
            <li class="element">Jay</li>
        </ul>
        <ul class="panel-body" id="list-2">
            <li class="element">年後中好說歹說開發,什麼才能傷風膠囊</li>
            <li class="element">Bar</li>
        </ul>
         <a href="link1.html">first item</a>
         <a href="link2.html">second item</a>
         <li class="item-inactive"><a href="link3.html"><span class="bold">third item</span></a></li>
     </ul>
    </div>
</div>
'''
from bs4 import BeautifulSoup
soup=BeautifulSoup(html,"lxml")
import re 
# 1、name引數
# 查詢所有名字為name的tag,搜尋 name 引數的值可以使任一型別的 過濾器 ,字元竄,正則表示式,列表,方法或是 True 
print(soup.find_all('li'))
# 使用列表
print(soup.find_all(['li','a']))
print(soup.find_all(True))
print(soup.find_all(re.compile('h4')))
# 2、keyword關鍵字引數
# 關鍵字是指tag的屬性:id、title、href等,注意:使用class時要加上'_'
print(soup.find_all('a',href="link1.html"))
print(soup.find_all(id="list-1"))
print(soup.find_all('ul',class_="list"))
# 使用正則
print(soup.find_all(href=re.compile('3.html')))
# 3、text
# 一般與name一起使用,通過 text 引數可以搜搜文件中的字串內容.與 name 引數的可選值一樣, text 引數接受 字串 , 正則表示式 , 列表, True 
print(soup.find_all(text=re.compile('好')))
print(soup.find_all('li',text=re.compile('好')))
# 使用多個屬性
print(soup.find_all(class_="panel-body",id="list-2"))
# 3、string
# 一般與name一起使用,通過 string 引數可以搜搜文件中的字串內容.與 name 引數的可選值一樣, string 引數接受 字串 , 正則表示式 , 列表, True;
print(soup.find_all("a", string="first item"))
print(soup.find_all(string="first item"))
# 4、limit引數
# 這個引數其實就是控制我們獲取資料的數量,效果和SQL語句中的limit一樣;
print(soup.find_all("a",limit=2))
# 5、recursive引數
# 呼叫tag的 find_all() 方法時,Beautiful Soup會檢索當前tag的所有子孫節點,如果只想搜尋tag的直接子節點,可以使用引數 recursive=False; 

四、CSS選擇器

# select()直接傳入CSS選擇器完成選擇
# .表示class ,#表示id
# 標籤1,標籤2
# 標籤1 標籤2
# [attr]可以通過這種方式找到具有某個屬性值的所有標籤
# [attr=value]例子:[target=blank]
html='''
<div class="panel">
    <div class="panel-heading">
        <h4>Hello</h4>
    </div>
    <div class="panel-body">
        <ul class="list" id="list-1">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
            <li class="element">Jay</li>
        </ul>
        <ul class="list list-small" id="list-2">
            <li class="element">Foo</li>
            <li class="element">Bar</li>
        </ul>
    </div>
</div>
'''
from bs4 import BeautifulSoup
soup=BeautifulSoup(html,"lxml")
# 找到class屬性是panel的標籤內的class屬性是panel-heading的標籤內容
print(soup.select(".panel .panel-heading"))
# 找id屬性為list-1和id屬性為list-2的所有標籤
print(soup.select('#list-1,#list-2'))
# 找到ul標籤下的li標籤
print(soup.select('ul li'))
# 找到id屬性值為list-2內部class屬性是element的所有標籤
print(soup.select('#list-2 .element'))
# get_text():拿到標籤文字值
# 所有li標籤下的文字值
for i in soup.select('li'):
    print(i.get_text())
# 獲取屬性值
for i in soup.select('ul'):
    print(i["id"])

總結:推薦使用lxml解析庫,必要時使用html.parser,標籤選擇篩選功能弱但是速度快,
          建議使用find()、find_all() 查詢匹配單個結果或者多個結果
          如果對CSS選擇器熟悉建議使用select()
          記住常用的獲取屬性和文字值的方法