BeautifulSoup是一個靈活有方便的網頁解系庫,處理搞笑,支持多種解析器,利用他可以不編寫正賊表達式即可方便實現網頁信息的提取。
解析庫:

我們主要用lxml解析器
標簽選擇器:
# coding=utf-8
from bs4 import BeautifulSoup as bs
html = """
<html><head><title>The Dormouse's story</title></head>
<body>
<p class="title" name="dromouse"><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 = bs(html, 'lxml')
print(soup.title)
print(type(soup.title))
print(soup.head)
print(type(soup.head))
print(soup.p)
print(type(soup.p))
這里我們print了soup.title、head、p三個標簽以及他們的類型,結果如下:

他們的類型都是bs.elment.tag,類型,類就是標簽類型,並且對於soup.p,是把第一個p標簽輸出,也就是說有多個相同的標簽,只輸出第一個
獲取名稱:
print(soup.title.name)

輸出結果就是title
獲取屬性:
print(soup.title.attrs['name'])

print(soup.p['name'])

可以看到這兩種方式都是相同的
獲取內容:
print(soup.p.string)

嵌套選擇:

也就是說從body到p,是一個嵌套的關系,p也是說,通過 .head得到的tag還可以進一步 向下索取,通過.body.p得到p標簽
子節點和子孫節點(children和contents):
contents:
# coding=utf-8 from bs4 import BeautifulSoup as bs html = """ <html> <head> <title> The Dormouse's story </title> </head> <body> <p class="story"> Once upon a time there were three little sisters; and their names were <a class="sister" href="http://example.com/elsie" id="link1"> Elsie </a> , <a class="sister" href="http://example.com/lacie" id="link2"> Lacie </a> and <a class="sister" href="http://example.com/tillie" id="link2"> Tillie </a> ; and they lived at the bottom of a well. </p> <p class="story"> ... </p> </body> </html> """ soup = bs(html, 'lxml') print(soup.body.contents)

可以看到contents屬性返回了一個列表,整個p中的內容。把所有的換行符 標簽放進了列表
children:
當我們把contents換成children:
print(soup.body.children)
contents:

它返回了一個迭代器,需要用for循環遍歷使用
后代descendants:
print(soup.body.descendants)

還是一個迭代器,並且descendants是獲得所有子孫節點,也就是兒子的兒子也會獲得
父節點parent:
返回父節點

父節點parents:

兄弟節點siblings:

以上是標簽選擇器,是通過名字進行選擇,但是在選擇時候,往往會有很多名字相同的標簽,所以我們不能完全用標簽選擇器進行選擇,故引入標准選擇器:
標准選擇器:
find_all(name, attrs, recursive, text, **kwargs)
可根據標簽名、屬性、內容查找文檔, 把所有符合條件的結果,並以列表的形式返回
name:

可以看到findall返回的列表中的每一個項哦都是tag類型
由此我們可以嵌套for循環:
for p in soup.find_all('p'): print(p.find_all('a'))
attrs:
print(soup.find_all(attrs={'id': 'list-1'})) print(soup.find_all(attrs={'name': 'elements'}))
attr需要傳入一個字典
並且對於某一些屬性,可以直接用參數傳入:
print(soup.find_all(id='list-1')) print(soup.find_all(class_='elements')) #class 是python的一個關鍵詞,所以我們用class_代替class
text:
根據文本的內容選擇,而它的返回值僅僅是文本的列表而不是tag


find(name, attrs, recursive, text, **kwargs)
與find_all不同是 find返回單個元素,fan_all返回所有元素。 find查找一個不存在的元素返回None
find_parent()和find_parents():
find_parent()返回所有祖先節點,find_parent()返回直接父節點。
find_next_siblings() 和 find_next_sibling()
find_next_siblings() 返回后面所有兄弟節點 find_next_sibling()返回前面一個兄弟節點
find_all_next() 和find_next()
find_all_next()返回節點后所有符合條件的節點,find_next()返回第一個符合條件的節點
find_all_previous()和find_previous()
find_all_previous()返回節點錢所有符合條件的節點,find_previous返回第一個符合條件的節點
css選擇器
通過css()直接傳入css選擇器即可完成選擇
標簽(什么都不用加).屬性(加點) #id(加井號)
- import requests
- from bs4 import BeautifulSoup as bs
- html = """
- <html>
- <head>
- <title>The Dormouse's story</title>
- </head>
- <body>
- <p class="title" name="dromouse">
- <b>The Dormouse's story</b>
- </p>
- <p class="story">
- Once upon a time there were three little sisters; and their names were
- <a class="mysis" href="http://example.com/elsie" id="link1">
- <b>the first b tag<b>
- Elsie
- </a>,
- <a class="mysis" href="http://example.com/lacie" id="link2" myname="kong">
- Lacie
- </a>and
- <a class="mysis" href="http://example.com/tillie" id="link3">
- Tillie
- </a>;and they lived at the bottom of a well.
- </p>
- <p class="story">
- myStory
- <a>the end a tag</a>
- </p>
- <a>the p tag sibling</a>
- </body>
- </html>
- """
- soup = bs(html, 'lxml')
- print(soup.select('p'))
- print(soup.select('p a'))
- print(type(soup.select('p')[0]))
輸出結果1是一個包含所有p標簽的列表 2是一個包含所有p標簽下的a標簽的列表,3是
,也就是說。css選擇器生成的結果就是一個tag類型的列表
同時對於soup.select('a.mysis‘表示class屬性為mysis的所有標簽。也即沒有空格的表示有某一個屬性的或者id的標簽。 有空格代表是同等的
又因為select返回的是tag類型的列表,所以我們可以繼續使用上面的方法獲得屬性即:、
- for a in soup.select('p a'):
- #方法一
- print(a['href'])
- #方法二
- print(a.attrs['href'])
以下羅列出一些css選擇器的方法:(以下內容轉自https://www.cnblogs.com/kongzhagen/p/6472746.html)
1、通過標簽選擇
- # 選擇所有title標簽
- soup.select("title")
- # 選擇所有p標簽中的第三個標簽
- soup.select("p:nth-of-type(3)") 相當於soup.select(p)[2]
- # 選擇body標簽下的所有a標簽
- soup.select("body a")
- # 選擇body標簽下的直接a子標簽
- soup.select("body > a")
- # 選擇id=link1后的所有兄弟節點標簽
- soup.select("#link1 ~ .mysis")
- # 選擇id=link1后的下一個兄弟節點標簽
- soup.select("#link1 + .mysis")
2、通過類名查找
- # 選擇a標簽,其類屬性為mysis的標簽
- soup.select("a.mysis")
3、通過id查找
- # 選擇a標簽,其id屬性為link1的標簽
- soup.select("a#link1")
4、通過【屬性】查找,當然也適用於class
- # 選擇a標簽,其屬性中存在myname的所有標簽
- soup.select("a[myname]")
- # 選擇a標簽,其屬性href=http://example.com/lacie的所有標簽
- soup.select("a[href='http://example.com/lacie']")
- # 選擇a標簽,其href屬性以http開頭
- soup.select('a[href^="http"]')
- # 選擇a標簽,其href屬性以lacie結尾
- soup.select('a[href$="lacie"]')
- # 選擇a標簽,其href屬性包含.com
- soup.select('a[href*=".com"]')
- # 從html中排除某標簽,此時soup中不再有script標簽
- [s.extract() for s in soup('script')]
- # 如果想排除多個呢
- [s.extract() for s in soup(['script','fram']
5、獲取文本及屬性
- 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>;
- </p>
- and they lived at the bottom of a well.
- <p class="story">...</p>
- </body>
- """
- from bs4 import BeautifulSoup
- '''''
- 以列表的形式返回
- '''
- soup = BeautifulSoup(html_doc, 'html.parser')
- s = soup.select('p.story')
- s[0].get_text() # p節點及子孫節點的文本內容
- s[0].get_text("|") # 指定文本內容的分隔符
- s[0].get_text("|", strip=True) # 去除文本內容前后的空白
- print(s[0].get("class")) # p節點的class屬性值列表(除class外都是返回字符串)
6、UnicodeDammit.detwingle() 方法只能解碼包含在UTF-8編碼中的Windows-1252編碼內容
- new_doc = UnicodeDammit.detwingle(doc)
- print(new_doc.decode("utf8"))
- # ☃☃☃“I like snowmen!”
在創建 BeautifulSoup 或 UnicodeDammit 對象前一定要先對文檔調用 UnicodeDammit.detwingle() 確保文檔的編碼方式正確.如果嘗試去解析一段包含Windows-1252編碼的UTF-8文檔,就會得到一堆亂碼,比如: ☃☃☃“I like snowmen!”.
7 、其他
- 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>;
- </p>
- and they lived at the bottom of a well.
- <p class="story">...</p>
- </body>
- """
- from bs4 import BeautifulSoup
- '''''
- 以列表的形式返回
- '''
- soup = BeautifulSoup(html_doc, 'html.parser')
- soup.select('title') # title標簽
- soup.select("p:nth-of-type(3)") # 第三個p節點
- soup.select('body a') # body下的所有子孫a節點
- soup.select('p > a') # 所有p節點下的所有a直接節點
- soup.select('p > #link1') # 所有p節點下的id=link1的直接子節點
- soup.select('#link1 ~ .sister') # id為link1的節點后面class=sister的所有兄弟節點
- soup.select('#link1 + .sister') # id為link1的節點后面class=sister的第一個兄弟節點
- soup.select('.sister') # class=sister的所有節點
- soup.select('[class="sister"]') # class=sister的所有節點
- soup.select("#link1") # id=link1的節點
- soup.select("a#link1") # a節點,且id=link1的節點
- soup.select('a[href]') # 所有的a節點,有href屬性
- soup.select('a[href="http://example.com/elsie"]') # 指定href屬性值的所有a節點
- soup.select('a[href^="http://example.com/"]') # href屬性以指定值開頭的所有a節點
- soup.select('a[href$="tillie"]') # href屬性以指定值結尾的所有a節點
- soup.select('a[href*=".com/el"]') # 支持正則匹配
