Python爬蟲基礎(chǔ)教程——lxml爬取入門

大家好,上次介紹了BeautifulSoup爬蟲入門,本篇內(nèi)容是介紹lxml模塊相關(guān)教程,主要為Xpath與lxml.cssselect 的基本使用。

一、lxml介紹

引用官方的解釋:

lxml XML工具箱是C庫libxml2和libxslt的Python綁定 。它的獨特之處在于它將這些庫的速度和XML功能的完整性與本機Python API的簡單性結(jié)合在一起,該Python API大多數(shù)都兼容,但優(yōu)于著名的 ElementTree API。
lxml.etree是一個非??焖俚腦ML庫。這主要是由于libxml2的速度,例如解析器和序列化器,或XPath引擎。lxml的其他區(qū)域?qū)iT為在高層操作(例如樹迭代器)中的高性能而編寫。

簡單的來說,lxml 是一種使用 Python 編寫的庫,可以迅速、靈活地處理 XML 和 HTML。

二、學(xué)習(xí)lxml庫的目的

利用所學(xué)的XPath語法與lxml.cssselect模塊,來快速定位特定元素以及節(jié)點信息,目的是提取HTML、XML目標數(shù)據(jù)

三、lxml安裝

pip install lxml

pip install lxml

-i http://pypi.douban.com/simple/

--trusted-host http://pypi.douban.com

順便說一句:我使用的開發(fā)工具還是vscode,不清楚的看一下之前的推文。

四、etree模塊

使用etree模塊,我們可以創(chuàng)建XML/Html元素及其子元素,我們用于操作Html或XML文件時非常有用。

4.1 Element類

用于ElementTree的API主容器對象。大多數(shù)XML樹功能都是通過此類訪問的。

下面嘗試一下:

from lxml import etree

root=etree.Element('root')
print(root.tag)
child=etree.SubElement(root,'child') # 添加一個子節(jié)點
child.set('id','test_Id')
print(etree.tostring(root))          # tostring 為序列化

結(jié)果:

可以看出來我們可以使用Element類來創(chuàng)建xml內(nèi)容。

4.2 tostring()

tostring()主要是對對象進行序列化,不能對集合進行序列化。

from lxml import etree

root = etree.XML('<root><a><b/></a></root>') 

print(etree.tostring(root))                        #default: method = 'xml'

print(etree.tostring(root, encoding='iso-8859-1')) #設(shè)置編碼方式為 iso-8859-1

print(etree.tostring(root, pretty_print=True))     # 格式化

root = etree.XML('<html><head/><body><p>Hello<br/>Python知識學(xué)堂</p></body></html>')
etree.tostring(root, method='xml')                 # 默認值為:xml

etree.tostring(root, method='html')                #轉(zhuǎn)成html

etree.tostring(root, method='text')                #轉(zhuǎn)成文本即獲取標簽內(nèi)的文本

不難理解,tostring()是序列化,那么fromstring()就是反序列化。

4.3 XML()/HTML()

類似于formstring()將字符串轉(zhuǎn)成xmL/HTML對象

root = etree.XML("<root>data</root>")
etree.tostring(root)
root = etree.HTML("<p>data</p>")
etree.tostring(root)

關(guān)于etree模塊就簡單的介紹一些,有想深入了解的可以看一下官方文檔。

五、XPath

lxml 支持XPath語法,XPath的語法還是比較簡單的,我們嘗試一下:

from lxml import etree

text="<div><h1>Python知識學(xué)堂</h1></div>"
html=etree.HTML(text)     #初始化生成一個XPath解析對象
result=html.xpath('//*')  #//代表獲取子孫節(jié)點,*代表獲取所有
for it in result:
    print(etree.tostring(it,encoding='utf-8').decode('utf-8'))

結(jié)果:

image
image.gif

下面介紹一下基本的語法以及舉一些實列。

5.1 XPath常見語法

實例

from lxml import etree

def to_string(html):
    return etree.tostring(html,encoding='utf-8').decode('utf-8') #只能解析對象不能解析list

text='''
     <div class="item-1" style="color:red;"><a href="link1.html">Python知識學(xué)堂</a></div>
     <div class="item-2"><a href="link2.html"><div>學(xué)習(xí)Python</div></a></div>
     <div class="item-3 item-4"><a href="link5.html">很好玩</div>
'''
html=etree.HTML(text)              #初始化生成一個XPath解析對象  并且會補全html 比如:body標簽
print(to_string(html))
# /表示從根節(jié)點開始選擇
print(html.xpath('/div'))          #此時找不到div標簽的 因為text 轉(zhuǎn)成html 代碼補全 導(dǎo)致初始的標簽為html 
result=html.xpath('/html/body/div')#可以找到所有html下的body標簽 再找到所有body標簽下的div標簽
for item in result:
    print(to_string(item))
print('------------------分隔符---------------')
# // 從匹配選擇的當前節(jié)點選擇文檔中的節(jié)點,而不考慮它們的位置
result=html.xpath('/html//div')    #獲取html 標簽下的所有div 標簽
for item in result:
    print(to_string(item))
print('------------------分隔符---------------')
# . 選取當前節(jié)點
result=html.xpath('./body')        #獲取根節(jié)點下的body 標簽
for item in result:
    print(to_string(item))
print('------------------分隔符---------------')
#..  選取當前節(jié)點的父節(jié)點  @選取屬性
result=html.xpath('//a/../@class') #獲取a標簽的 父節(jié)點標簽的 class的屬性值
print(result) 
print('------------------分隔符---------------')
# * 匹配任何元素節(jié)點
result=html.xpath('//*')           #選取文檔中的所有元素
for item in result:
    print(to_string(item))
print('------------------分隔符---------------')
# [@attribute='value'] 匹配屬性值
result=html.xpath('//div[@class="item-2"]') #選取文檔div標簽 并且 含有class 屬性且值為‘item-2’的所有元素
for item in result:
    print(to_string(item))

大家需要注意:

  1. xpath的返回結(jié)果都是集合(list)
  2. xpath只能作用與對象(object)上

六、lxml.cssselect

lxml.cssselect模塊中最重要的是CSSSelector 即CSS選擇器。了解前端知識的可能會容易一點。

6.1 安裝

這個模塊需要安裝

pip install cssselect

6.2 CSS選擇器

上面是一些常用的用法,可以參考一下。

我在網(wǎng)上看到關(guān)于使用cssselect的問題

<div class="aa bb"></div>
使用cssselect('.aa bb')是取不出來的,解決方法是將空格轉(zhuǎn)換為 . 點。

可能不是很了解CSS選擇器的規(guī)則,不知道class屬性有多屬性值時如何選擇。

6.3 實例

from lxml.cssselect import CSSSelector
from lxml import etree

text='''
     <div class="item-1 item-2" style="color:red;"><a href="link1.html">Python知識學(xué)堂</a></div>
     <div class="item-2"><a href="link2.html"><div>學(xué)習(xí)Python</div></a></div>
     <div class="item-3 item-4"><a href="link5.html">很好玩</div>
'''
html=etree.HTML(text)      #初始化生成一個XPath解析對象  并且會補全html
result=html.cssselect('div.item-1.item-2 a') #選擇div標簽并且含有class屬性且值含有"item-1"與"item-2" 下的a 標簽子節(jié)點
for item in result:
    print(item.text)       # 獲取標簽的內(nèi)容
    print(item.get('href'))#獲取a 標簽的屬性值

從上面代碼可以看出,如果需要通過多個class屬性值來獲取指定的標簽,直接加.屬性值。注意空格問題,.class1.class2與.class1 .class2的結(jié)果可能是不一樣的,大家可以嘗試一下。

與xpath一樣,大家需要注意:

  1. cssselect 返回的結(jié)果都是集合(list)
  2. cssselect 只能作用與對象(object)上

七、案例

我們還是以之前的案例《獲取省市區(qū)數(shù)據(jù)》,現(xiàn)在我們用lxml的方式重新實現(xiàn)一下

7.1 XPath方式

import requests
from lxml import etree
import time

class Demo():
    def __init__(self):
        base_url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/'
        trlist = self.get_data(base_url, "provincetable",'provincetr')#查看頁面,就知道所有的省所在的tr上都有唯一的class='provincetr'
        for tr in trlist:
            td=tr.xpath('.//td')[0]
            a=td.find('a')
            if a is None:
                continue
            print("省:" + a.text)                    #獲取每個省
            time.sleep(0.5)
            c_url=base_url+a.xpath('./@href')[0]     #獲取下級城市地址
            c_trlist= self.get_data(c_url, "citytable","citytr")
            for c_tr in c_trlist:
                c_tds=c_tr.xpath('.//td')
                a=c_tds[0].find('a')
                t_url=base_url+a.xpath('./@href')[0] #獲取下級區(qū)縣地址
                print('市:('+a.text +')'+c_tds[1].find('a').text)
                time.sleep(0.5)

                t_list=self.get_data(t_url,"countytable","countytr")
                if len(t_list)==0:
                    t_list=self.get_data(t_url,"towntable","towntr")
                for t_tr in t_list:
                    t_tds=t_tr.xpath('.//td')
                    a=t_tds[0].find('a')
                    if a is None:
                        continue
                    print('區(qū)/縣:('+a.text +')'+t_tds[1].find('a').text)

    def get_data(self, url, table_attr,attr):
        response = requests.get(url)
        response.encoding = 'gb2312'                 #編碼轉(zhuǎn)換
        tree=etree.HTML(response.text)               #初始化生成一個XPath解析對象
        trlist = tree.xpath('//table[@class="'+table_attr+'"]//tr[@class="'+attr+'"]')
        return trlist

if __name__ == '__main__':
    Demo()

7.2 lxml.cssselect方式

import requests
from lxml import etree
from lxml.cssselect import CSSSelector
import time

class Demo():

    def __init__(self):
        base_url = 'http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2019/'
        trlist = self.get_data(base_url, "provincetable",'provincetr')#查看頁面,就知道所有的省所在的tr上都有唯一的class='provincetr'
        for tr in trlist:
            tds=tr.cssselect('td')
            for td in tds:
                a=td.cssselect('a')
                if len(a) ==0:
                    continue
                print("省:" + a[0].text)           #獲取每個省
                time.sleep(0.5)
                c_url=base_url+a[0].attrib['href'] #獲取下級城市地址
                c_trlist= self.get_data(c_url, "citytable","citytr")
                for c_tr in c_trlist:
                    c_tds=c_tr.cssselect('td')
                    c_a=c_tds[0].cssselect('a')
                    if len(c_a)==0:
                        continue
                    t_url=base_url+c_a[0].attrib['href'] #獲取下級區(qū)縣地址
                    print('市:('+c_a[0].text +')'+c_tds[1].cssselect('a')[0].text)
                    time.sleep(0.5)

                    t_list=self.get_data(t_url,"countytable","countytr")
                    if len(t_list)==0:
                        t_list=self.get_data(t_url,"towntable","towntr")
                    for t_tr in t_list:
                        t_tds=t_tr.cssselect('td')
                        t_a=t_tds[0].cssselect('a')
                        if len(t_a)==0:
                            continue
                        print('區(qū)/縣:('+t_a[0].text +')'+t_tds[1].cssselect('a')[0].text)

    def get_data(self, url, table_attr,attr):
        response = requests.get(url)
        response.encoding = 'gb2312'              #編碼轉(zhuǎn)換
        tree=etree.HTML(response.text)            #初始化生成一個XPath解析對象
        trlist = tree.cssselect('table.'+table_attr+' tr.'+attr+'')
        return trlist

if __name__ == '__main__':
    Demo()

可以研究一下代碼,可以用自己的方式實現(xiàn)出來。

八、總結(jié)

本篇文章主要講述了關(guān)于lxml模塊的一些內(nèi)容,主要為XPath與lxml.cssselect的基本使用。lxml還有很多其他的功能,大家可以去官網(wǎng)上自行學(xué)習(xí)。

貼一下 官網(wǎng)地址:https://lxml.de/

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容