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

爬蟲之數據解析的三種方式

att 很慢 下標 清空 應用程序 文檔轉換 sce spl 返回結果

一,正則表達式解析

re正則就不寫了,前面已經寫入一篇很詳細的正則表達式模塊了~

而且,在爬蟲中,下面兩種方式用的多一些~

正則表達式:https://www.cnblogs.com/peng104/p/9619801.html

大致用法:

pattern = re.compile(<dd>.*?board-index.*?>(\d+)</i>.*?src="(.*?)".*?name"><a
                     +.*?>(.*?)</a>.*?star">(.*?)</p>.*?releasetime">(.*?)</p>
+.*?integer">(.*?)</i>.*?fraction">(.*?)</i>.*?</dd>, re.S) items = re.findall(pattern, html)

二,Xpath解析

簡介及安裝

簡介:XPath 是一門在 XML 文檔中查找信息的語言。XPath 可用來在 XML 文檔中對元素和屬性進行遍歷。XPath 是 W3C XSLT 標準的主要元素,並且 XQuery 和 XPointer 都構建於 XPath 表達之上。

安裝:pip install lxml

調用方法:

# 先導包
from lxml import etree

# 將html文檔或者xml文檔轉換成一個etree對象,然後調用對象中的方法查找指定的節點
# 1. 本地文件
tree = etree.parse(文件名)
tree.xpath("xpath表達式")

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

語法簡介

先準備一個HTML格式的字符串

技術分享圖片
html_doc = """
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<div class="d1">
    <div class="d2">
            <p class="story">
                <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" id="link3">Tillie</a>
            </p>
    </div>
    <div>
        <p id="p1">人生苦短</p>
        <p id="p2">我用python</p>
    </div>
</div>

<div class="d3">
    <a href="http://www.baidu.com">baidu</a>
    <p>百度</p>
</div>

</body>
</html>
"""
變量準備

1,選取節點

nodename     # 選取nodename節點的所有子節點         xpath(‘//div’)         # 選取了所有div節點
/            # 從根節點選取                        xpath(‘/div’)          # 從根節點上選取div節點
//           # 選取所有的當前節點,不考慮他們的位置    xpath(‘//div’)         # 選取所有的div節點
.            # 選取當前節點                        xpath(‘./div’)         # 選取當前節點下的div節點
..           # 選取當前節點的父節點                 xpath(‘..’)            # 回到上一個節點
@            # 選取屬性                           xpath(’//@calss’)     # 選取所有的class屬性

#######################?例子?######################

ret=selector.xpath("//div")
ret=selector.xpath("/div")
ret=selector.xpath("./div")
ret=selector.xpath("//p[@id=‘p1‘]")
ret=selector.xpath("//div[@class=‘d1‘]/div/p[@class=‘story‘]")

2,謂語

表達式                                         結果
xpath(‘/body/div[1]’)                     # 選取body下的第一個div節點
xpath(‘/body/div[last()]’)                # 選取body下最後一個div節點
xpath(‘/body/div[last()-1]’)              # 選取body下倒數第二個div節點
xpath(‘/body/div[positon()<3]’)           # 選取body下前兩個div節點
xpath(‘/body/div[@class]’)                # 選取body下帶有class屬性的div節點
xpath(‘/body/div[@class=”main”]’)         # 選取body下class屬性為main的div節點
xpath(‘/body/div[@price>35.00]’)          # 選取body下price元素值大於35的div節點

#######################?例子?######################

ret=selector.xpath("//p[@class=‘story‘]//a[2]")
ret=selector.xpath("//p[@class=‘story‘]//a[last()]")

3,通配符

Xpath通過通配符來選取未知的XML元素

表達式                 結果
xpath(’/div/*’)     # 選取div下的所有子節點
xpath(‘/div[@*]’)    # 選取所有帶屬性的div節點

#######################?例子?######################

ret=selector.xpath("//p[@class=‘story‘]/*")
ret=selector.xpath("//p[@class=‘story‘]/a[@class]")

4,取多個路徑

使用 “|” 運算符可以選取多個路徑

表達式                         結果
xpath(‘//div|//table’)    # 選取所有的div和table節點

#######################?例子?######################

ret=selector.xpath("//p[@class=‘story‘]/a[@class]|//div[@class=‘d3‘]")
print(ret)

5,Xpath軸

軸可以定義相對於當前節點的節點集

軸名稱                      表達式                                  描述
ancestor                xpath(‘./ancestor::*’)              # 選取當前節點的所有先輩節點(父、祖父)
ancestor-or-self        xpath(‘./ancestor-or-self::*’)      # 選取當前節點的所有先輩節點以及節點本身
attribute               xpath(‘./attribute::*’)             # 選取當前節點的所有屬性
child                   xpath(‘./child::*’)                 # 返回當前節點的所有子節點
descendant              xpath(‘./descendant::*’)            # 返回當前節點的所有後代節點(子節點、孫節點)
following               xpath(‘./following::*’)             # 選取文檔中當前節點結束標簽後的所有節點
following-sibing        xpath(‘./following-sibing::*’)      # 選取當前節點之後的兄弟節點
parent                  xpath(‘./parent::*’)                # 選取當前節點的父節點
preceding               xpath(‘./preceding::*’)             # 選取文檔中當前節點開始標簽前的所有節點
preceding-sibling       xpath(‘./preceding-sibling::*’)     # 選取當前節點之前的兄弟節點
self                    xpath(‘./self::*’)                  # 選取當前節點

6,功能函數

使用功能函數能夠更好的進行模糊搜索

函數                  用法                                                               解釋
starts-with         xpath(‘//div[starts-with(@id,”ma”)]‘)                        # 選取id值以ma開頭的div節點
contains            xpath(‘//div[contains(@id,”ma”)]‘)                           # 選取id值包含ma的div節點
and                 xpath(‘//div[contains(@id,”ma”) and contains(@id,”in”)]‘)    # 選取id值包含ma和in的div節點
text()              xpath(‘//div[contains(text(),”ma”)]‘)                        # 選取節點文本包含ma的div節點

Element對象

from lxml.etree import _Element
for obj in ret:
    print(obj)
    print(type(obj))  # from lxml.etree import _Element

‘‘‘
Element對象

class xml.etree.ElementTree.Element(tag, attrib={}, **extra)

  tag:string,元素代表的數據種類。
  text:string,元素的內容。
  tail:string,元素的尾形。
  attrib:dictionary,元素的屬性字典。
  
  #針對屬性的操作
  clear():清空元素的後代、屬性、text和tail也設置為None。
  get(key, default=None):獲取key對應的屬性值,如該屬性不存在則返回default值。
  items():根據屬性字典返回一個列表,列表元素為(key, value)。
  keys():返回包含所有元素屬性鍵的列表。
  set(key, value):設置新的屬性鍵與值。

  #針對後代的操作
  append(subelement):添加直系子元素。
  extend(subelements):增加一串元素對象作為子元素。#python2.7新特性
  find(match):尋找第一個匹配子元素,匹配對象可以為tag或path。
  findall(match):尋找所有匹配子元素,匹配對象可以為tag或path。
  findtext(match):尋找第一個匹配子元素,返回其text值。匹配對象可以為tag或path。
  insert(index, element):在指定位置插入子元素。
  iter(tag=None):生成遍歷當前元素所有後代或者給定tag的後代的叠代器。#python2.7新特性
  iterfind(match):根據tag或path查找所有的後代。
  itertext():遍歷所有後代並返回text值。
  remove(subelement):刪除子元素。
‘‘‘

三,BeautifulSoup

簡介及安裝

簡介:

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

安裝:pip3 install beautifulsoup4

解析器:

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

官方文檔

簡單使用

使用方式:可以將一個html文檔,轉化為BeautifulSoup對象,然後通過對象的方法或者屬性去查找指定的節點內容

from bs4 import BeautifulSoup

# 轉化本地文件
soup = BeautifulSoup(open(本地文件), lxml)

# 轉化網絡文件
soup = BeautifulSoup(字符串類型或者字節類型, lxml)

# 從文檔中獲取所有文字內容:
print(soup.get_text())

語法簡介

# 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選擇器返回永遠是列表,需要通過下標提取指定的對象

find_all()

先準備一個HTML格式的字符串

技術分享圖片
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>
"""
變量準備

1,五種過濾器

字符串、正則表達式、列表、True、方法

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


#1、字符串:即標簽名
print(soup.find_all(b))

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

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

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

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

tag

像調用 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)

find

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 Dormouse‘s story</title>]
soup.find(title)
# <title>The Dormouse‘s 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 Dormouse‘s story</title>
soup.find("head").find("title")
# <title>The Dormouse‘s story</title>

爬蟲之數據解析的三種方式