1. 程式人生 > 其它 >章神的私房菜之資料預處理

章神的私房菜之資料預處理

安裝使用

# 安裝 pip3 install beautifulsoup4
from bs4 import BeautifulSoup

soup=BeautifulSoup(ret.text,'html.parser')  # 傳資料
soup=BeautifulSaoup(open('a.html','r'))		# 傳檔案

# html.parser內建解析器,速度稍微慢一些,但是不需要裝第三方模組
# lxml:速度快一些,但是需要安裝 pip3 install lxml

soup=BeautifulSoup(ret.text,'html.parser')
soup=BeautifulSoup(ret.text,'lxml')

# find(找到的第一個)
# find_all(找到的所有)
# 找頁面所有的li標籤
li_list=soup.find_all(name='li')


.text     # 全部拿出來,拼在一起
.string    # 只拿第一個
.strings   # 全部拿出來生成一個迭代器

遍歷文件樹

html_doc = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title"id="id_p"><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>
"""

soup=BeautifulSoup(html_doc,'lxml')


# 1.美化+自動補全閉合標籤
print(soup.prettify())

# 2.(通過.來查詢,只能找到第一個)
head=soup.head  # Tag物件
title=head.title  # Tag物件

# 3.獲取標籤的名稱
p=soup.body
print(p.name)  # body    #沒什麼卵用

# 4.獲取標籤的屬性
p=soup.p
# 獲取class屬性,可以有多個,所以拿到是列表
# 方式一
print(p['class'])  # ['title']
print(p['id'])     # id_p
# 方式二
print(p.get('class'))   # ['title']
print(p.attrs['class'])  # ['title']

# 5.獲取標籤內容
p=soup.p
print(p)  # <p class="title" id="id_p"><b>The Dormouse's story</b></p>
print(p.text)  # The Dormouse's story
print(p.string) # The Dormouse's story

# 6.巢狀選擇
title = soup.head.title.text  # 獲取head下第一個title的內容
print(title)  # The Dormouse's story

# 7.子節點、子孫節點
p1=soup.p.children   # 迭代器
p2=soup.p.contents  # 列表
print(p1)  # <list_iterator object at 0x000001FA66E0C4A8>
print(list(p1))  # [<b>The Dormouse's story</b>]
print(p2)  # [<b>The Dormouse's story</b>]

# 8.父節點、祖先節點
p1=soup.p.parent  # 直接父節點
p2=soup.p.parents
print(p1)
# # print(len(list(p2)))
print(list(p2))

# 9.兄弟節點
print(soup.a.next_sibling) #下一個兄弟
print(soup.a.previous_sibling) #上一個兄弟
print(list(soup.a.next_siblings)) #下面的兄弟們=>生成器物件
print(soup.a.previous_siblings) #上面的兄弟們=>生成器物件

查詢文件樹

# 查詢文件樹(find,find_all),速度比遍歷文件樹慢
# 兩個配合著使用(soup.p.find())
# 五種過濾器: 字串、正則表示式、列表、True、方法


# 1.字串查詢 引號內是字串
p=soup.find(name='p')  # <p class="title" id="id_p"><b>The Dormouse's story</b></p>
p=soup.find(name='body')  # body標籤所以內容
print(p)

# 查詢類名是title的所有標籤,class是關鍵字,class_
ret=soup.find_all(class_='title')  # [<p class="title" id="id_p"><b>The Dormouse's story</b></p>]
# 找id為xx的標籤
ret=soup.find_all(id='id_p')  # [<p class="title" id="id_p"><b>The Dormouse's story</b></p>]
# href屬性為http://example.com/elsie的標籤
ret=soup.find_all(href='http://example.com/elsie') # [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>]

# 2.正則表示式
import re
reg=re.compile('^id')  # ^id開頭
ret=soup.find_all(id=reg)  # 查詢以id開頭的id標籤
print(ret)  # [<p class="title" id="id_p"><b>The Dormouse's story</b></p>]

# 3.列表
# or關係
ret=soup.find_all(id=['id_psafdsaf','link1'])  # 查詢id有id_psafdsaf或者link1的標籤
ret=soup.find_all(class_=['title1','story'])  # 查詢類名有title1或者story的標籤
# and關係
ret=soup.find_all(class_='title',name='p') # 查詢類名有title1並且name=p的標籤

# 4.true
# 所有有名字的標籤
ret=soup.find_all(name=True)
#所有有id的標籤
ret=soup.find_all(id=True)
# 所有有herf屬性的
ret=soup.find_all(href=True)
print(ret)

# 5.方法
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))

# 6.其他使用
ret=soup.find_all(attrs={'class':"title"})
ret=soup.find_all(attrs={'id':"id_p1",'class':'title'})
print(ret)

# 7.limit(限制條數)
# soup.find()  就是find_all limit=1
ret=soup.find_all(name=True,limit=2)
print(len(ret)) # 2

# 8.recursive
# recursive=False (只找兒子)不遞迴查詢,只找第一層
ret=soup.body.find_all(name='p',recursive=False)
print(ret)

選擇器介紹

# bs4:自己的選擇器,css選擇器
# lxml:css選擇器,xpath選擇器
# selenium:自己的選擇器,css選擇器,xpath選擇器
# scrapy框架:自己的選擇器,css選擇器,xpath選擇器


# css選擇器,xpath選擇器會用了,它就是個通行證

CSS選擇器

# Tag物件.select("css選擇器")
from bs4 import BeautifulSoup
import requests

for i in range(1, 5):
    i1 = str(i - 1)
    i = str(i)
    url = 'https://www.mzitu.com/202340/' + i
    ret = requests.get(url,headers={'User-Agent': 'request', 'Referer': 'https://www.mzitu.com/206122/' + i1},proxies={'http': '47.115.54.89'}, )


soup = BeautifulSoup(ret.text, 'lxml')

案例:
#   div>p:兒子      div p:子子孫孫
#   找div下最後一個a標籤 div a:last-child

print(soup.select('#list-1 li:nth-child(1)')[0].text)    # 取第一個li標籤
print(soup.select('#list-1 li:nth-child(2)')[0].text)    # 取第二個li標籤
print(soup.select('#list-1 li:nth-last-child(1)')[0].text)    # 取倒數第一個li標籤
print(soup.select('#list-1 li:nth-last-child(2)')[0].text)    # 取倒數第二個li標籤


print(soup.p.select('.sister'))  # 可以組合使用。
print(soup.select('.sister span'))

print(soup.select('#link1'))
print(soup.select('#link1 span'))

print(soup.select('#list-2 .element.xxx'))

print(soup.select('#list-2')[0].select('.element')) #可以一直select,但其實沒必要,一條select就可以了

# 2、獲取屬性
print(soup.select('#list-2 h1')[0].attrs)
href = soup.select('body > div.main > div.content > div.main-image > p > a > img')
print(href[0].attrs['src'])  # 圖片連結


# 3、獲取內容
# .get_text()
# .text
# .string
# .strings 變成迭代器
print(soup.select('#list-2 h1')[0].get_text())

xpath選擇器

# xpath選擇
# / 從根節點選取  /a   從根節點開始,往下找a標籤(子)
# //從匹配選擇的當前節點選擇文件中的節點,而不考慮它們的位置  //a 從根節點開始找a標籤(子子孫孫中所有a)
# . 	選取當前節點。
# .. 	選取當前節點的父節點。
# @ 	選取屬性。
# 取值 /text()
# 取屬性 /@屬性名

使用:
from lxml import etree
html=etree.HTML(doc) # 傳字串
html=etree.parse('search.html',etree.HTMLParser())  # 傳檔案


# 1 文字獲取   標籤後加:/text() ********重點
a=html.xpath('//body//a[@href="image1.html"]/text()')
a=html.xpath('//body//a/text()')

# 2 屬性獲取  標籤後:/@href   ********重點
a=html.xpath('//body//a/@href')
# 注意從1 開始取(不是從0)
a=html.xpath('//body//a[3]/@href')

# 3 所有節點
a=html.xpath('//*')

# 4 指定節點(結果為列表)
a=html.xpath('//head')

# 5 子節點,子孫節點
a=html.xpath('//div/a')
a=html.xpath('//body/a') #無資料
a=html.xpath('//body//a')

# 6 父節點
a=html.xpath('//body//a[@href="image1.html"]/..')
a=html.xpath('//body//a[@href="image1.html"]')
a=html.xpath('//body//a[1]/..')
也可以這樣
a=html.xpath('//body//a[1]/parent::*')

# 7 屬性匹配
a=html.xpath('//body//a[@href="image1.html"]')

# 8 屬性多值匹配
a 標籤有多個class類,直接匹配就不可以了,需要用contains
a=html.xpath('//body//a[@class="li"]')
a=html.xpath('//body//a[@href="image1.html"]')
a=html.xpath('//body//a[contains(@class,"li")]')
a=html.xpath('//body//a[contains(@class,"li")]/text()')
a=html.xpath('//body//a[contains(@class,"li")]/@name')

# 9 多屬性匹配 or 和 and (瞭解)
a=html.xpath('//body//a[contains(@class,"li") or @name="items"]')
a=html.xpath('//body//a[contains(@class,"li") and @name="items"]/text()')
a=html.xpath('//body//a[contains(@class,"li")]/text()')

# 10 按序選擇
a=html.xpath('//a[2]/text()')
a=html.xpath('//a[2]/@href')
# 取最後一個(瞭解)
a=html.xpath('//a[last()]/@href')
a=html.xpath('//a[last()]/text()')
# 位置小於3的
a=html.xpath('//a[position()<3]/@href')
a=html.xpath('//a[position()<3]/text()')
# 倒數第二個
a=html.xpath('//a[last()-2]/@href')


# 11 節點軸選擇
ancestor:祖先節點
使用了* 獲取所有祖先節點
a=html.xpath('//a/ancestor::*')


# # 獲取祖先節點中的div
a=html.xpath('//a/ancestor::div')
a=html.xpath('//a/ancestor::div/a[2]/text()')
# attribute:屬性值
a=html.xpath('//a[1]/attribute::*')
a=html.xpath('//a[1]/@href')
# child:直接子節點
a=html.xpath('//a[1]/child::*')
a=html.xpath('//a[1]/img/@src')
descendant:所有子孫節點
a=html.xpath('//a[6]/descendant::*')

following:當前節點之後所有節點(遞迴)
a=html.xpath('//a[1]/following::*')
a=html.xpath('//a[1]/following::*[1]/@href')
# following-sibling:當前節點之後同級節點(同級)
a=html.xpath('//a[1]/following-sibling::*')
a=html.xpath('//a[1]/following-sibling::a')
a=html.xpath('//a[1]/following-sibling::*[2]')
a=html.xpath('//a[1]/following-sibling::*[2]/@href')