解析庫之xpath,beautifulsoup,pyquery

xpath

1、經常使用xpath表達式

  • 屬性定位:
    1. #找到class屬性值爲song的div標籤
    2. //div[@class="song"]
  • 層級&索引定位:
    1. #找到class屬性值爲tang的div的直系子標籤ul下的第二個子標籤li下的直系子標籤a
    2. //div[@class="tang"]/ul/li[2]/a
  • 邏輯運算:
    1. #找到href屬性值爲空且class屬性值爲du的a標籤
    2. //a[@href="" and @class="du"]
  • 模糊匹配:
    1. //div[contains(@class, "ng")]
    2. //div[starts-with(@class, "ta")]
  • 取文本:
    1. # /表示獲取某個標籤下的文本內容
    2. # //表示獲取某個標籤下的文本內容和全部子標籤下的文本內容
    3. //div[@class="song"]/p[1]/text()
    4. //div[@class="tang"]//text()
  • 取屬性:
    1. //div[@class="tang"]//li[2]/a/@href
  • xpath函數返回的老是一個列表

2、基礎使用

  • 下載:pip install lxml
  • 導包:from lxml import etree
  • 官網推薦在如今的項目中使用Beautiful Soup 4, 移植到BS4
  • 將html文檔或者xml文檔轉換成一個etree對象,而後調用對象中的方法查找指定的節點
  • 本地文件:
    1. tree = etree.parse('本地文件路徑')
    2. tree.xpath("xpath表達式")
  • 網絡數據:
    1. tree = etree.HTML("網絡請求到的頁面數據")
    2. tree.xpath("xpath表達式")
  • xpath插件:就能夠直接將xpath表達式做用於瀏覽器的網頁當中
  • 安裝:更多工具-》擴展程序-》開啓右上角的開發者模式-》xpath插件拖動到頁面便可
  • 快捷鍵:啓動和關閉插件 ctrl + shift + x

Beautifulsoup模塊

  • Beautiful Soup 是一個能夠從HTML或XML文件中提取數據的Python庫.它可以經過你喜歡的轉換器實現慣用的文檔導航,查找,修改文檔的方式.
  • Beautiful Soup會幫你節省數小時甚至數天的工做時間.你可能在尋找 Beautiful Soup3 的文檔,Beautiful Soup 3 目前已經中止開發.
  • 官網推薦在如今的項目中使用Beautiful Soup 4, 移植到BS4
  • 官網推薦使用lxml做爲解析器,由於效率更高.
  • 在Python2.7.3以前的版本和Python3中3.2.2以前的版本,必須安裝lxml或html5lib, 由於那些Python版本的標準庫中內置的HTML解析方法不夠穩定.
  • 中文文檔點擊

1、環境安裝

  • 須要將pip源設置爲國內源,阿里源、豆瓣源、網易源等
  • windows:
    1. 打開文件資源管理器(文件夾地址欄中)
    2. 地址欄上面輸入 %appdata%
    3. 在這裏面新建一個文件夾 pip
    4. 在pip文件夾裏面新建一個文件叫作 pip.ini ,內容寫以下便可
    5. [global]
    6. timeout = 6000
    7. index-url = https://mirrors.aliyun.com/pypi/simple/
    8. trusted-host = mirrors.aliyun.com
  • linux:
    1. cd ~
    2. mkdir ~/.pip
    3. vi ~/.pip/pip.conf
    4. 編輯內容,和windows如出一轍
  • 須要安裝:pip install bs4
    1. bs4在使用時候須要一個第三方庫,把這個庫也安裝一下
    2. pip install lxml

2、基礎使用

核心思想:將html文檔轉換成Beautiful對象,而後調用該對象中的屬性和方法進行html文檔指定內容的定位查找。javascript

一、使用流程

  • 導包:from bs4 import BeautifulSoup
  • 使用方式:能夠將一個html文檔,轉化爲BeautifulSoup對象,而後經過對象的方法或者屬性去查找指定的節點內容
  • 轉化本地文件:soup = BeautifulSoup(open('本地文件'), 'lxml')
  • 轉化網絡文件:soup = BeautifulSoup('字符串類型或者字節類型', 'lxml')
  • 打印soup對象顯示內容爲html文件中的內容

二、基礎鞏固

  • 根據標籤名查找:
    1. soup.a 只能找到第一個符合要求的標籤
  • 獲取屬性:
    1. soup.a.attrs 獲取a全部的屬性和屬性值,返回一個字典
    2. soup.a.attrs['href'] 獲取href屬性
    3. soup.a['href'] 也可簡寫爲這種形式
  • 獲取內容:
    1. soup.a.string
    2. soup.a.text
    3. soup.a.get_text()
    4. 【注意】若是標籤還有標籤,那麼string獲取到的結果爲None,而其它兩個,能夠獲取文本內容
  • find:找到第一個符合要求的標籤:
    1. soup.find('a') 找到第一個符合要求的
    2. soup.find('a', title="xxx")
    3. soup.find('a', alt="xxx")
    4. soup.find('a', class_="xxx")
    5. soup.find('a', id="xxx")
  • find_All:找到全部符合要求的標籤:
    1. soup.find_All('a')
    2. soup.find_All(['a','b']) 找到全部的a和b標籤
    3. soup.find_All('a', limit=2) 限制前兩個
  • 根據選擇器選擇指定的內容:
    1. select:soup.select('#feng')
    2. 常見的選擇器:標籤選擇器(a)、類選擇器(.)、id選擇器(#)、層級選擇器
    3. 層級選擇器:div .dudu #lala .meme .xixi 下面好多級
    4. 層級選擇器:div > p > a > .lala 只能是下面一級
    5. 【注意】select選擇器返回永遠是列表,須要經過下標提取指定的對象
解析器 使用方法 優點 劣勢
Python標準庫 BeautifulSoup(markup, "html.parser")
  • Python的內置標準庫
  • 執行速度適中
  • 文檔容錯能力強
  • Python 2.7.3 or 3.2.2)前 的版本中文檔容錯能力差
lxml HTML 解析器 BeautifulSoup(markup, "lxml")
  • 速度快
  • 文檔容錯能力強
  • 須要安裝C語言庫
lxml XML 解析器

BeautifulSoup(markup, ["lxml", "xml"])css

BeautifulSoup(markup, "xml")html

  • 速度快
  • 惟一支持XML的解析器
  • 須要安裝C語言庫
html5lib BeautifulSoup(markup, "html5lib")
  • 最好的容錯性
  • 以瀏覽器的方式解析文檔
  • 生成HTML5格式的文檔
  • 速度慢
  • 不依賴外部擴展
#安裝 Beautiful Soup
pip install beautifulsoup4

#安裝解析器
Beautiful Soup支持Python標準庫中的HTML解析器,還支持一些第三方的解析器,其中一個是 lxml .根據操做系統不一樣,能夠選擇下列方法來安裝lxml:

$ apt-get install Python-lxml

$ easy_install lxml

$ pip install lxml

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

$ apt-get install Python-html5lib

$ easy_install html5lib

$ pip install html5lib
安裝lxml
html_doc = """
<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>
"""

#基本使用:容錯處理,文檔的容錯能力指的是在html代碼不完整的狀況下,使用該模塊能夠識別該錯誤。使用BeautifulSoup解析上述代碼,可以獲得一個 BeautifulSoup 的對象,並能按照標準的縮進格式的結構輸出
from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'lxml') #具備容錯功能
res=soup.prettify() #處理好縮進,結構化顯示
print(res)
基本使用
"""
#遍歷文檔樹:即直接經過標籤名字選擇,特色是選擇速度快,但若是存在多個相同的標籤則只返回第一個
#一、用法
#二、獲取標籤的名稱
#三、獲取標籤的屬性
#四、獲取標籤的內容
#五、嵌套選擇
#六、子節點、子孫節點
#七、父節點、祖先節點
#八、兄弟節點
"""
#遍歷文檔樹:即直接經過標籤名字選擇,特色是選擇速度快,但若是存在多個相同的標籤則只返回第一個
html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p id="my p" class="title"><b id="bbb" class="boldest">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>
"""

#一、用法
from bs4 import BeautifulSoup
soup=BeautifulSoup(html_doc,'lxml')
# soup=BeautifulSoup(open('a.html'),'lxml')

print(soup.p) #存在多個相同的標籤則只返回第一個
print(soup.a) #存在多個相同的標籤則只返回第一個

#二、獲取標籤的名稱
print(soup.p.name)

#三、獲取標籤的屬性
print(soup.p.attrs)

#四、獲取標籤的內容
print(soup.p.string) # p下的文本只有一個時,取到,不然爲None
print(soup.p.strings) #拿到一個生成器對象, 取到p下全部的文本內容
print(soup.p.text) #取到p下全部的文本內容
for line in soup.stripped_strings: #去掉空白
    print(line)


'''
若是tag包含了多個子節點,tag就沒法肯定 .string 方法應該調用哪一個子節點的內容, .string 的輸出結果是 None,若是隻有一個子節點那麼就輸出該子節點的文本,好比下面的這種結構,soup.p.string 返回爲None,但soup.p.strings就能夠找到全部文本
<p id='list-1'>
    哈哈哈哈
    <a class='sss'>
        <span>
            <h1>aaaa</h1>
        </span>
    </a>
    <b>bbbbb</b>
</p>
'''

#五、嵌套選擇
print(soup.head.title.string)
print(soup.body.a.string)


#六、子節點、子孫節點
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)

#七、父節點、祖先節點
print(soup.a.parent) #獲取a標籤的父節點
print(soup.a.parents) #找到a標籤全部的祖先節點,父親的父親,父親的父親的父親...


#八、兄弟節點
print('=====>')
print(soup.a.next_sibling) #下一個兄弟
print(soup.a.previous_sibling) #上一個兄弟

print(list(soup.a.next_siblings)) #下面的兄弟們=>生成器對象
print(soup.a.previous_siblings) #上面的兄弟們=>生成器對象
遍歷文檔樹

3、搜索文檔樹

一、五種過濾器

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


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

#一、五種過濾器: 字符串、正則表達式、列表、True、方法
#1.一、字符串:即標籤名
print(soup.find_All('b'))

#1.二、正則表達式
import re
print(soup.find_All(re.compile('^b'))) #找出b開頭的標籤,結果有body和b標籤

#1.三、列表:若是傳入列表參數,Beautiful Soup會將與列表中任一元素匹配的內容返回.下面代碼找到文檔中全部<a>標籤和<b>標籤:
print(soup.find_All(['a','b']))

#1.四、True:能夠匹配任何值,下面代碼查找到全部的tag,可是不會返回字符串節點
print(soup.find_All(True))
for tag in soup.find_All(True):
    print(tag.name)

#1.五、方法:若是沒有合適過濾器,那麼還能夠定義一個方法,方法只接受一個元素參數 ,若是這個方法返回 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))
View Code

二、find_All( name , attrs , recursive , text , **kwargs )

#二、find_All( name , attrs , recursive , text , **kwargs )
#2.一、name: 搜索name參數的值可使任一類型的 過濾器 ,字符竄,正則表達式,列表,方法或是 True .
print(soup.find_All(name=re.compile('^t')))

#2.二、keyword: key=value的形式,value能夠是過濾器:字符串 , 正則表達式 , 列表, True .
print(soup.find_All(id=re.compile('my')))
print(soup.find_All(href=re.compile('lacie'),id=re.compile('\d'))) #注意類要用class_
print(soup.find_All(id=True)) #查找有id屬性的標籤

# 有些tag屬性在搜索不能使用,好比HTML5中的 data-* 屬性:
data_soup = BeautifulSoup('<div data-foo="value">foo!</div>','lxml')
# data_soup.find_All(data-foo="value") #報錯:SyntaxError: keyword can't be an expression
# 可是能夠經過 find_All() 方法的 attrs 參數定義一個字典參數來搜索包含特殊屬性的tag:
print(data_soup.find_All(attrs={"data-foo": "value"}))
# [<div data-foo="value">foo!</div>]

#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的全部標籤

#2.四、attrs
print(soup.find_All('p',attrs={'class':'story'}))

#2.五、text: 值能夠是:字符,列表,True,正則
print(soup.find_All(text='Elsie'))
print(soup.find_All('a',text='Elsie'))

#2.六、limit參數:若是文檔樹很大那麼搜索會很慢.若是咱們不須要所有結果,可使用 limit 參數限制返回結果的數量.效果與SQL中的limit關鍵字相似,當搜索到的結果數量達到 limit 的限制時,就中止搜索返回結果
print(soup.find_All('a',limit=2))

#2.七、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)
'''
View Code

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

View Code

四、其餘方法

#見官網:https://www.crummy.com/software/BeautifulSoup/bs4/doc/index.zh.html#find-parents-find-parent
View Code

五、CSS選擇器

View Code

4、修改文檔樹

5、總結

  • 推薦使用lxml解析庫
  • 講了三種選擇器:標籤選擇器,find與find_All,css選擇器
    1. 標籤選擇器篩選功能弱,可是速度快
    2. 建議使用find,find_All查詢匹配單個結果或者多個結果
    3. 若是對css選擇器很是熟悉建議使用select
  • 記住經常使用的獲取屬性attrs和文本值get_text()的方法
"""
數據解析:
- 1.指定url
- 2.發起請求
- 3.獲取頁面數據
- 4.數據解析
- 5.進行持久化存儲
三種數據解析方式:
- 正則
- bs4
- xpath

使用正則對糗事百科中的圖片數據進行解析和下載
<div class="thumb">

<a href="/article/121159481" target="_blank">
<img src="//pic.qiushibaike.com/system/pictures/12115/121159481/medium/CIYK4P1D4DKSBY4L.jpg" alt="不是臭鹹魚嗎">
</a>

</div>
"""

import requests
import re
import os
#指定url
url = 'https://www.qiushibaike.com/pic/'
headers={
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
    }
#發起請求
response = requests.get(url=url,headers=headers)

#獲取頁面數據
page_text = response.text

#數據解析(該列表中存儲的就是當前頁面源碼中全部的圖片連接)
img_list = re.findall('<div class="thumb">.*?<img src="(.*?)".*?>.*?</div>',page_text,re.S)

#建立一個存儲圖片數據的文件夾
if not os.path.exists('./imgs'):
    os.mkdir('imgs')
for url in img_list:
    #將圖片的url進行拼接,拼接成一個完成的url
    img_url = 'https:' + url
    #持久化存儲:存儲的是圖片的數據,並非url。
    #獲取圖片二進制的數據值
    img_data = requests.get(url=img_url,headers=headers).content
    imgName = url.split('/')[-1]
    imgPath = 'imgs/'+imgName
    with open(imgPath,'wb') as fp:
        fp.write(img_data)
        print(imgName+'寫入成功')


"""
使用xpath對段子網中的段子內容和標題進行解析,持久化存儲
"""
import requests
from lxml import etree

#1.指定url
url = 'https://ishuo.cn/joke'
#2.發起請求
headers={
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
    }
response = requests.get(url=url,headers=headers)
#3.獲取頁面內容
page_text = response.text
#4.數據解析
tree = etree.HTML(page_text)
#獲取全部的li標籤(段子內容和標題都被包含在li標籤中)
li_list = tree.xpath('//div[@id="list"]/ul/li')
#注意:Element類型的對象能夠繼續調用xpath函數,對該對象表示的局部內容進行指定內容的解析
fp = open('./duanzi.txt','w',encoding='utf-8')
for li in li_list:
    content = li.xpath('./div[@class="content"]/text()')[0]
    title = li.xpath('./div[@class="info"]/a/text()')[0]
    #5.持久化
    fp.write(title+":"+content+"\n\n")
    print('一條數據寫入成功')


"""
需求:爬取古詩文網中三國小說裏的標題和內容
"""
import requests
from bs4 import BeautifulSoup

url = 'http://www.shicimingju.com/book/sanguoyanyi.html'
headers={
    'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36',
    }


#根據url獲取頁面內容中指定的標題所對應的文章內容
def get_content(url):
    content_page = requests.get(url=url,headers=headers).text
    soup = BeautifulSoup(content_page,'lxml')
    div = soup.find('div',class_='chapter_content')
    return div.text

page_text = requests.get(url=url,headers=headers).text

#數據解析
soup = BeautifulSoup(page_text,'lxml')
#a_list列表中存儲的是一系列的a標籤對象
a_list = soup.select('.book-mulu > ul > li > a')
#type(a_list[0])
#注意:Tag類型的對象能夠繼續調用響應的解析屬性和方法進行局部數據的解析

fp = open('./sanguo.txt','w',encoding='utf-8')
for a in a_list:
    #獲取了章節的標題
    title = a.string
    content_url = 'http://www.shicimingju.com'+a['href']
    print(content_url)
    #獲取章節的內容
    content = get_content(content_url)
    fp.write(title+':'+content+"\n\n\n")
    print('寫入一個章節內容')
三種數據解析方式

pyquery模塊

'''
強大而又靈活的網頁解析庫,若是你以爲正則寫起來太麻煩,若是你以爲beutifulsoup
語法太難記,若是你熟悉jquery的語法,那麼pyquery是最佳選擇


安裝pyquery
pip3 install pyquery
'''

html='''
</div><div class="account-signin">
    <ul class="navigation menu" aria-label="Social Media Navigation">
        哈哈哈
        <li class="tier-1 last" aria-haspopup="true">

            <a href="/accounts/login/" title="Sign Up or Sign In to Python.org">Sign In</a>
            <ul class="subnav menu">
                <li class="tier-2 element-1" role="treeitem"><a href="/accounts/signup/">Sign Up / Register</a></li>
                <li class="tier-2 element-2" role="treeitem"><a href="/accounts/login/">Sign In</a></li>
            </ul>

        </li>
    </ul>
</div>
'''


#用法:

#1===========>初始化
#===>字符串初始化
# from pyquery import PyQuery as pq
# doc=pq(html)
# print(doc('.tier-2')) #默認就是css選擇器

#===>url初始化
# from pyquery import PyQuery as pq
# doc=pq(url='http://www.baidu.com')
# print(doc('head'))

#===>文件初始化
# from pyquery import PyQuery as pq
# doc=pq(filename='demo.html')
# print(doc('li'))


#2===========>基本css選擇器
from pyquery import PyQuery as pq
doc=pq(html)
# print(doc('.tier-2')) #默認就是css選擇器

#查找元素

#子元素
# print(doc('li').find('li')) #這裏的find是查找全部,可是不必定是直接子元素
# print('==>',doc('li').children('li')) #查找直接子元素


#父元素
# print(doc('.tier-2').parent())

#祖先元素:爹,爹的爹
# print(doc('.tier-2').parents())
# print(doc('.tier-2').parents('.account-signin')) #從祖先裏篩選

#先補充:並列選擇
# print(doc('.tier-1 .tier-2'))
# print(doc('.tier-1 .tier-2.element-1'))

#兄弟元素
# print(doc('.tier-2.element-1').siblings())
# print(doc('.tier-2.element-1').siblings('li a'))







#3===========>遍歷

# lis=doc('li').items()
# print(lis)
#
# for i,j in enumerate(lis):
#     print(i,j)

#4===========>獲取屬性
# print(doc('li').attr('class'))
# print(doc('a').attr.href)


# 5===========>獲取文本
# print(doc('a').text())

#6===========>獲取html
# print(doc('.subnav.menu'))
# print(doc('.subnav.menu').html())


#7===========>DOM
#addclass,removeclass
# tag=doc('.subnav.menu')
# print(tag)
#
# tag.addClass('active')
# print(tag)
#
# tag.removeClass('active')
# print(tag)


# tag=doc('.tier-2.element-1 a')
# tag.attr('name','link')
# tag.css('font-size','14px')
# print(tag)


tag=doc('.navigation.menu')
# print(tag.text()) #獲取的是tag下全部的文本,

tag.find('li').remove()
print(tag.text()) #若是指向獲取url下的那個"哈哈哈",則須要先刪除li

#8===========>pyquery官網


# http://pyquery.readthedocs.io/en.latest/api.html


#9===========>僞類選擇器

print(doc('li:first-child')) #選擇li標籤的第一個
print(doc('li:last-child')) #選擇li標籤的最後一個
print(doc('li:nth-child(2)')) #選擇li標籤的第2個
print(doc('li:gt(2)')) #選擇li標籤第2個之後的
print(doc('li:nth-child(2n)')) #選擇li標籤的偶數標籤
print(doc('li:nth-child(2n+1)')) #選擇li標籤的奇數標籤
print(doc('li:contains(second)')) #選擇li標籤中包含second文本的標籤

#更多css選擇器能夠查看
# http://www.w3school.com.cn/css/index.asp

#官網:http://pyquery.readthedocs.io/
View Code
相關文章
相關標籤/搜索