lxml解析網頁


1. 什么是lxml

lxml是干什么的?簡單的說來,lxml是幫助我們解析HTML、XML文件,快速定位,搜索、獲取特定內容的Python庫。我們知道,對於純文本的HTML文件的查找可以使用正則表達式BeautifulSoup等完成。lxml也是對網頁內容解析的一個庫。

那么為什么要用lxml呢?據聽說他比較快。我沒有用來做過大項目,對解析速度理解不是很深刻。我用lxml只是因為它似乎比BeautifulSoup好用。

2. 初次使用

  1. 安裝
sudo pip3 install lxml  
  1. 初次使用
# 導入lxml
from lxml import etree

# html字符串
html_str = """
<html>
<head>
<title>demo</title>
</head>
<body>
<p>1111111</p>
</body>
</html>
"""

# 利用html_str創建一個節點樹對象
html = etree.HTML(html_str)
type(html) # 輸出結果為:lxml.etree._Element
  1. 首次解析HTML

不用理會下面代碼中出現的新的方法和各種解析的技巧。先看一下lxml如何快速方便的解析html.

# 我們現在要獲得上面的html文件中的p標簽的內容
p_str = html.xpath('//body/p/text()')  # 返回結果為一個列表:['1111111']

上面的例子,給出一個lxml如何解析HTML文件的實例。后文中眾多的知識點,只不過是講解更多的xpath解析方法技巧。

3. xpath

我們一直再講lxml,這里突然出現xpath是干什么的?lxml的主要功能是解析HTML,他是利用什么語法來解析HTML的呢?就是利用xpath,因此,我們需要了解如何使用xpath。

xpath將html文檔看做一個有眾多的節點按照特定級別組織的節點樹,對於其中內容的解析,又三種主要的措施:

  1. 標簽定位
  2. 序列定位
  3. 軸定位

很抱歉,我們又引入了新的概念。但現在我們解釋這些概念是不明智的,還是先看一下如何使用。

3.2 標簽定位

為了說明xpath各種定位語法,我們下面利用如下的HTML來完成講解。

from lxml import etree

html_str = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>The Document's story</title>
</head>
<body>
    <div class="table1">
        <ul class="one" id="id1">
            <tr>tr1</tr>
            <tr>tr2</tr>
            <tr>tr3</tr>
            <tr>tr4</tr>
        </ul>
        <ol class="two" id="id2">
            <td>td1</td>
            <td>td2</td>
            <td>td3</td>
            <td>td4</td>
        </ol>
    </div>
    <div class="table2">
        <a href="www.table2_1.com"><span>table2_span</span></a>
        <a href="www.table2_2.com">
            <p><h2>TABLE2</h2></p>
        </a>
        <a href="www.table2_3.com" id="id3">
                <ul class="table_one" id="id4">
                    <tr>tr1_a</tr>
                    <tr>tr2_a</tr>
                    <tr>tr3_a</tr>
                    <tr>tr4_a</tr>
                </ul>
        </a>
    </div>
</body>
</html>
"""

html = etree.HTML(html_str)
html.xpath('//*') # 請將'//*'替換成下面表中實例列的表達式以觀察各表達式的含義和作用。

先給出一張表。下表中給出了標簽定位的表達式和對其作用的描述。下表中,實例列中的表達式的全部表達如下:如第一行中的命令為'//div',則全部的表達式為html.xpath('//div') 。注意,這里的html是一個lxml.etree._Element對象。(我們用HTML表示HTML文件或者其對應的字符串。)

表達式 描述 實例 解釋
nodename 選取此節點的所有子節點 '//div' 找到html樹中的所有div標簽
/ 從根節點選取 '/head/title' 從根節點找到head->title
// 選取任意位置的某個節點 '//' html中所有p標簽
. 選取當前節點 '.' 返回當前節點對象
.. 選取當前節點的父節點 '/html/head/..' 返回head的父節點html
@ 選取屬性 '//div[@class="one"]' 返回具有屬性class,並且class的值為"one"的節點
* 通配符 '//div/*' 返回所有滿足條件的節點
| 一次選擇多個路徑 '/html/head | //div' 返回head節點或者div節點
@* 選取所有屬性 '//div[@*]' 返回所有具有屬性的div對象

3.3 序列定位

通過上面的學習,我們知道html.xpath()返回的是一個包含節點樹對象的列表,對於列表中的元素,我們可以按照列表的索引進行查找,但是,如果想在xpath里面進行選擇,就需要使用序列定位。

下面的代碼承接上文。在給出一張表。

謂語 描述 實例 解釋
[1] 第一個元素 '//div[1]' 返回第一個div對象
[last()] 最后一個元素 '//div[last()]' 返回最后一個div對象
[last()-1] 倒數第二個元素 '//div/ul[1]/tr[last()-1]' 返回所有div對象中第一個ul對象下面的倒數第二個tr對象
[position()❤️] 最前面的兩個元素 '//tr[position()❤️]' 返回前兩個tr對象
[@lang] 所有擁有屬性lang的元素 '//div[@class]' 返回具有calss屬性的div
[@lang='en'] 所有lang='en'的元素 '//div[@class="en"]' 返回class屬性值為en的div對象

3.4 軸定位

同上。

軸名稱 描述 實例 解釋
child 當前節點的所有子元素 '//div[1]/child:😗' 第一個div的所有子節點
parent 當前節點的父節點 '//div[1]/parent:😗' 第一個div的父節點
ancestor 當前節點的所有先輩 '//div[1]/ancestor:😗' 第一個div的所有祖先節點
ancestor-or-self 當前節點及其所有先輩 '//div[1]/ancestor-or-self:😗' ..
descendant 當前節點的所有后代 '//div[1]/descendant::ul' 第一個div的子孫節點中的ul對象
descendant-or-self 當前節點及其所有后代 '//body/descendant-or-self' ..
preceding 文檔中當前節點開始標記之前的所有節點 '//body/preceding:😗' ..
following 文檔中當前節點結束標記之后的所有節點 '//body/following:😗' ..
preceding-sibling 當前節點之前的所有同級節點 '//div/preceding-sibling:😗' ..
following-sibling 當前節點之后的所有同級節點 '//div/following-sibling:😗' ..
self 當前節點 '//div[0]/self:😗' 返回當前節點
attribute 當前節點的所有屬性 '//div/attribute:😗' 返回所有屬性值
namespace 當前節點的所有命名空間 '//div/namespace:😗' 返回命名空間

4. 實例

我們已經學了很多的xpath語法。現在可以完成一個小小的綜合練習,用以鞏固我們的學習。

我們的需求:到tiobe上面抓取最后歡迎的語言top20,並對這些語言的使用情況最簡單的可視化。

上代碼,如有需要請看代碼中的注釋。

網頁內容定位如下:

# 導入所需要的庫
import urllib.request as urlrequest
from lxml import etree

# 獲取html
url = r'https://www.tiobe.com/tiobe-index/'
page = urlrequest.urlopen(url).read()
# 創建lxml對象
html = etree.HTML(page)
# 解析HTML,篩選數據
df = html.xpath('//table[contains(@class, "table-top20")]/tbody/tr//text()')
# 數據寫入數據庫
import pandas as pd
tmp = []
for i in range(0, len(df), 5):
    tmp.append(df[i: i+5])
df = pd.DataFrame(tmp)
# 查看數據
df.head(2)
0 1 2 3 4
0 1 1 Java 16.028% -0.85%
1 2 2 C 15.154% +0.19%

原始數據存在一些問題:

  1. 數據列名稱含義不明,要給列添加有意義的名稱['Aug 2019', 'Aug 2018', 'Name', 'Rating', 'Change']
  2. 最后兩列為字符串,需要轉換成float格式
# 數據處理
df.columns = ['Aug 2019', 'Aug 2018', 'Name', 'Rating', 'Change']
# 處理最后兩列數據
df['Rating'] = [float(istr.replace('%', '')) for istr in df['Rating']]
df['Change'] = [float(istr.replace('%', '')) for istr in df['Change']]
# 再次查看數據
df.head()
Aug 2019 Aug 2018 Name Rating Change
0 1 1 Java 16.028 -0.85
1 2 2 C 15.154 0.19
2 3 4 Python 10.020 3.03
3 4 3 C++ 6.057 -1.41
4 5 6 C# 3.842 0.30
# 繪制圖像
import matplotlib.pyplot as plt
plt.pie(df['Rating'], labels=df['Name'])
plt.show()


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM