Python BeautifulSoup的使用

1.BeautifulSoup的能夠幹什麼?

  • BeautifulSoup 是一個能夠從HTML或XML文件中提取數據的Python庫.它可以經過你喜歡的轉換器實現慣用的文檔導航,查找,修改文檔的方式;
  • BeautifulSoup提供一些簡單的、python式的函數用來處理導航、搜索、修改分析樹等功能。它是一個工具箱,經過解析文檔爲用戶提供須要抓取的數據,由於簡單,因此不須要多少代碼就能夠寫出一個完整的應用程序;
  • BeautifulSoup自動將輸入文檔轉換爲Unicode編碼,輸出文檔轉換爲utf-8編碼。你不須要考慮編碼方式,除非文檔沒有指定一個編碼方式,這時,BeautifulSoup就不能自動識別編碼方式了。而後,你僅僅須要說明一下原始編碼方式就能夠了;
  • BeautifulSoup已成爲和lxml、html6lib同樣出色的python解釋器,爲用戶靈活地提供不一樣的解析策略或強勁的速度。

2.bs4在windows環境下的安裝

  1. 官網上下載,我下的版本是4.5.1
  2. 下載完成後解壓縮到,python的安裝目錄;
  3. 運行cmd,進入bs4文件夾;
  4. 執行 setup.py build;
  5. 執行 setup.py install 便可以完成安裝;
  6. 安裝完成後要檢查是否安轉成功。其實不必這麼複雜,若是有pip 咱們直接pip安裝便可。

3.四大對象種類

Beautiful Soup將複雜HTML文檔轉換成一個複雜的樹形結構,每一個節點都是Python對象,全部對象能夠概括爲4種:css

  1. Tag:Tag通俗點講就是HTML中的一個個標籤。Beautiful Soup是如何方便的獲取Tags?首先咱們須要建立一個suop對象如:soup = BeautifulSoup(html文件,"html.parser")
soup.title
# <title>The Dormouse's story</title>
soup.title.name
# u'title'
soup.title.string
# u'The Dormouse's story'
soup.title.parent.name
# u'head'
soup.p
# <p class="title"><b>The Dormouse's story</b></p>
soup.p['class']
# u'title'
soup.a
# <a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>
soup.find_all('a')
# [<a class="sister" href="http://example.com/elsie" id="link1">Elsie</a>,
#  <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>,
#  <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]
soup.find(id="link3")
# <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>
  1. BeautifulSoup:BeautifulSoup 對象表示的是一個文檔的所有內容.大部分時候,能夠把它看成 Tag 對象,是一個特殊的 Tag,咱們能夠分別獲取它的類型,名稱。
  2. NavigableString:咱們已經得到標籤的內容,怎麼樣獲取標籤內部的文字呢?用.string。它的類型是一個 NavigableString,翻譯過來叫能夠遍歷的字符串。
print(soup.p.string)
print(type(soup.p.string))//結果會發現,打印出來的結果其實就是一個NavigableString類型
  1. Comment:Comment 對象是一個特殊類型的 NavigableString 對象,其實輸出的內容仍然不包括註釋符號,可是若是很差好處理它,可能會對咱們的文本處理形成意想不到的麻煩.
//咱們找一個帶有註釋的標籤:
print(soup.a)
//運行結果(一個帶有註釋的a標籤)
<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>
print(soup.a.string)
//注:咱們在打印.string的時候,最好先判斷(根據類型),
//避免運行結果(輸出了註釋,可是沒有註釋符號)
//Elsie註釋與非註釋混淆打印出來了
print(type(soup.a.string))
//運行結果(類型是Comment)
<class 'bs4.element.Comment'>

4.遍歷文檔樹

  1. 遍歷直接子節點。這裏包含兩個屬性:.contents.children 屬性,tag 的 .content 屬性能夠將tag的子節點以列表的方式輸出,.children它返回的不是一個 list,不過咱們能夠經過遍歷獲取全部子節點。咱們打印輸出 .children 看一下,能夠發現它是一個 list 生成器對象。
查看其類型:print(soup.children)
運行結果:<list_iterator object at 0x0000000000E24438>
  1. ** 遍歷全部子孫節點**。要點:.descendants屬性。.contents 和 .children 屬性僅包含tag的直接子點,.descendants 屬性能夠對全部tag的子孫節點進行遞歸循環,和 children相似,咱們也須要遍歷獲取其中的內容。
  2. 遍歷節點內容。若是tag只有一個 NavigableString 類型子節點,那麼這個tag能夠 使用 .string 獲得子節點。若是一個tag僅有一個子節點,那麼這個tag也可使用 .string 方法,輸出結果與當前惟一子節點的 .string 結果相同。通俗點說就是:若是一個標籤裏面沒有標籤了,那麼 .string 就會返回標籤裏面的內容。若是標籤裏面只有惟一的一個標籤了,那麼 .string 也會返回最裏面的內容。若是tag包含了多個子節點,tag就沒法肯定,string 方法應該調用哪一個子節點的內容, .string 的輸出結果是 None。
print(soup.head.string)
//輸出: The Dormouse's story
print(soup.title.string)
//輸出:The Dormouse's story
//若是tag包含太多子節點,例如:
print(soup.html.string)
//輸出:None

那麼咱們就須要利用.strings.stripped_strings 屬性遍歷多個內容。.strings獲取多個內容,不過須要遍歷獲取;.strings 輸出的字符串中可能包含了不少空格或空行,用 .stripped_strings 能夠去除多餘空白內容。html

  1. 遍歷文檔樹父節點。使用.parent 屬性能夠得到節點的父節點;經過元素的 .parents屬性能夠遞歸獲得元素的全部父輩節點。
content = soup.head.title.string
for parent in content.parents:
    print(parent.name)
輸出:
title
head
html
[document]
  1. 遍歷兄弟節點。兄弟節點能夠理解爲和本節點處在統一級的節點,.next_sibling 屬性獲取了該節點的下一個兄弟節點,.previous_sibling則與之相反,若是節點不存在,則返回 None。**注意:**實際文檔中的tag的.next_sibling.previous_sibling 屬性一般是字符串或空白,由於空白或者換行也能夠被視做一個節點,因此獲得的結果多是空白或者換行。經過 .next_siblings 和``` .previous_siblings
1. **遍歷文檔樹的先後節點**。```.next_element ```,```.previous_element ```屬性與``` .next_sibling```,```.previous_sibling``` 不一樣,它並非針對於兄弟節點,而是在全部節點,不分層次。

好比 head 節點爲:<head><title>The Dormouse's story</title></head> 那麼它的下一個節點爲:print(soup.head.next_element) 輸出:<title>The Dormouse‘s story</title> 並無層次關係python

### 5.搜索文檔樹。
主要是```find_all()```方法搜索當前tag的全部tag子節點,並判斷是否符合過濾器的條件。```find_all( name , attrs , recursive , text , **kwargs )```
1. name參數:name 參數能夠查找全部名字爲 name 的tag,字符串對象會被自動忽略掉。
    - A.傳字符串:最簡單的過濾器是字符串。在搜索方法中傳入一個字符串參數,Beautiful Soup會查找與字符串完整匹配的內容

print(soup.find_all("b")) 輸出:[<b>The Dormouse's story</b>]mysql

- B.傳正則表達式:若是傳入正則表達式做爲參數,BeautifulSoup會經過正則表達式的 match() 來匹配內容.下面例子中找出全部以b開頭的標籤,這表示<body>和<b>標籤都應該被找到。

import re for tag in soup.find_all(re.compile("^a")): print(tag.name) 輸出: a a ajquery

- C.傳列表:若是傳入列表參數,Beautiful Soup會將與列表中任一元素匹配的內容返回.下面代碼找到文檔中全部<a>標籤和<b>標籤。

for tag in (soup.find_all(["p","b"])): print(tag.name) 輸出: p b p p正則表達式

- 傳 True:True 能夠匹配任何值,下面代碼查找到全部的tag,可是不會返回字符串節點。

for tag in (soup.find_all(True)): print(tag.name) 輸出: html head title body p b psql

- 傳方法:若是沒有合適過濾器,那麼還能夠定義一個方法,方法只接受一個元素參數,若是這個方法返回 True 表示當前元素匹配而且被找到,若是不是則反回 False。下面方法校驗了當前元素,若是包含 class 屬性卻不包含 id 屬性,那麼將返回 True。

def has_tag_but_no_id(tag): return tag.has_attr("class") and not tag.has_attr("id") for tagName in (soup.find_all(has_tag_but_no_id)): print(tagName.name)數據庫

1. keyword參數
    - 有些tag屬性在搜索不能使用,好比HTML5中的 data-* 屬性

data_soup = BeautifulSoup("<div data-foo = 'value'>foo!</div>","html.parser") print(data_soup.find_all("data-foo")) 輸出:[]windows

- 可是能夠經過 find_all() 方法的 attrs 參數定義一個字典參數來搜索包含特殊屬性的tag。

print(data_soup.find_all(attrs = {"data-foo":"value"})) 輸出: [<div data-foo="value">foo!</div>]app

1. text參數:經過 text 參數能夠搜索文檔中的字符串內容。與 name 參數的可選值同樣, text 參數接受 字符串 , 正則表達式 , 列表, True。

print(soup.find_all(text="Lacie")) 輸出:['Lacie'] print(soup.find_all(text=["Lacie","Tillie"])) 輸出:['Lacie', 'Tillie']

1. recursive 參數:調用tag的find_all() 方法時,BeautifulSoup會檢索當前tag的全部子孫節點,若是隻想搜索tag的直接子節點,可使用參數```recursive=False```。
1. **相似於find_all()的一些其餘方法**
    - find_previous_siblings(),find_previous_sibling():這2個方法經過 .previous_siblings 屬性對當前 tag 的前面解析的兄弟 tag 節點進行迭代, find_previous_siblings() 方法返回全部符合條件的前面的兄弟節點,find_previous_sibling() 方法返回第一個符合條件的前面的兄弟節點;
    - find_all_next()  find_next():這2個方法經過 .next_elements 屬性對當前 tag 的以後的 tag 和字符串進行迭代, find_all_next() 方法返回全部符合條件的節點, find_next() 方法返回第一個符合條件的節點;
    - find_all_previous() 和 find_previous():這2個方法經過 .previous_elements 屬性對當前節點前面的 tag 和字符串進行迭代, find_all_previous() 方法返回全部符合條件的節點, find_previous()方法返回第一個符合條件的節點。

### 6.css選擇器
1. 咱們在寫 CSS 時,標籤名不加任何修飾,類名前加點,id名前加 #,在這裏咱們也能夠利用相似的方法來篩選元素,用到的方法是 soup.select(),返回類型是 list。

print(soup.select("title")) 輸出:[<title>The Dormouse‘s story</title>](返回列表形式)

1. 經過類名查找

print(soup.select(".sister")) 輸出:[<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

1. id和組合查找
    - 經過id查找

print(soup.select("#link2")) 輸出:[<a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>]

- 組合查找(組合查找即和寫 class 文件時,標籤名與類名、id名進行的組合原理是同樣的,例如查找 p 標籤中,id 等於 link1的內容,兩者須要用空格分開)。

print(soup.select("p #link1")) 輸出:[<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>]

1. 屬性查找:查找時還能夠加入屬性元素,屬性須要用中括號括起來,注意屬性和標籤屬於同一節點,因此中間不能加空格,不然會沒法匹配到。

print(soup.select("a[class='sister']")) //是否是很相似於jquery中的篩選 輸出: [<a class="sister" href="http://example.com/elsie" id="link1"><!--Elsie--></a>, <a class="sister" href="http://example.com/lacie" id="link2">Lacie</a>, <a class="sister" href="http://example.com/tillie" id="link3">Tillie</a>]

### 7.分享一下用BeautifulSoup爬蟲的小例子
- 部分源碼以下:

#url = http://wuhan.anjuke.com/sale/ from bs4 import BeautifulSoup import requests import time import pymysql import re url_QX = [] def url_qx(url): wb_data = requests.get(url).text soup = BeautifulSoup(wb_data,"html.parser") information = soup.select("div.div-border.items-list > div.items > span.elems-l > a")

# 咱們要找的就是下面的這些url,實際上語句執行的 查找出來的結果有多的,不要緊,咱們只取咱們要的
#<div class="items"><span class="item-title">區域:</span><span class="elems-l">
# <span class="selected-item">所有</span>
# <a href='http://wuhan.anjuke.com/sale/wuchanga/' class='' >武昌</a>
# <a href='http://wuhan.anjuke.com/sale/hongshana/' class='' >洪山</a><
# a href='http://wuhan.anjuke.com/sale/jiangan/' class='' >江岸</a>
# <a href='http://wuhan.anjuke.com/sale/jianghana/' class='' >江漢</a>
# <a href='http://wuhan.anjuke.com/sale/qiaokou/' class='' >礄口</a>
# <a href='http://wuhan.anjuke.com/sale/hanyang/' class='' >漢陽</a>
# <a href='http://wuhan.anjuke.com/sale/dongxihu/' class='' >東西湖</a>
# <a href='http://wuhan.anjuke.com/sale/qingshan/' class='' >青山</a>
# <a href='http://wuhan.anjuke.com/sale/jiangxiat/' class='' >江夏</a>
# <a href='http://wuhan.anjuke.com/sale/zhuankoukaifaqu/' class='' >沌口開發區</a>
# <a href='http://wuhan.anjuke.com/sale/huangpiz/' class='' >黃陂</a>
# <a href='http://wuhan.anjuke.com/sale/caidianz/' class='' >蔡甸</a>
# <a href='http://wuhan.anjuke.com/sale/hannanz/' class='' >漢南</a>
# <a href='http://wuhan.anjuke.com/sale/xinzhouz/' class='' >新洲</a>
# <a href='http://wuhan.anjuke.com/sale/qitao/' class='' >其餘</a></span></div><!-- 區域 end-->
for url in information:
    data =  {
        "url":url.get("href"),
        "address:":url.get_text()
    }
    #print(data)
    url_QX.append(data)

url_qx("http://wuhan.anjuke.com/sale/") url_qxTop = url_QX[0:15] #這是武昌,漢口等區,咱們最後但願獲得的是 武昌全部位置(好比說沙湖等位置) #print(url_qxTop) url_finally = [] #創建一個臨時列表,用來進行地址的截取 def url_qx_finally(urlDict): for i in range(0,15): eachUrl = urlDict[i]["url"] wb_data = requests.get(eachUrl).text wb_soup = BeautifulSoup(wb_data,"html.parser") wb_information = wb_soup.select("div.div-border.items-list > div.items > span.elems-l > div.sub-items > a") #以武昌區爲例,咱們找到了全部武昌區下面更詳細的區 #<div class="items"><span class="item-title">區域:</span><span class="elems-l"> # <a href='http://wuhan.anjuke.com/sale/' class='' >所有</a><span class="selected-item">武昌</span><a href='http://wuhan.anjuke.com/sale/hongshana/' class='' >洪山</a><a href='http://wuhan.anjuke.com/sale/jiangan/' class='' >江岸</a><a href='http://wuhan.anjuke.com/sale/jianghana/' class='' >江漢</a><a href='http://wuhan.anjuke.com/sale/qiaokou/' class='' >礄口</a><a href='http://wuhan.anjuke.com/sale/hanyang/' class='' >漢陽</a><a href='http://wuhan.anjuke.com/sale/dongxihu/' class='' >東西湖</a><a href='http://wuhan.anjuke.com/sale/qingshan/' class='' >青山</a><a href='http://wuhan.anjuke.com/sale/jiangxiat/' class='' >江夏</a><a href='http://wuhan.anjuke.com/sale/zhuankoukaifaqu/' class='' >沌口開發區</a><a href='http://wuhan.anjuke.com/sale/huangpiz/' class='' >黃陂</a><a href='http://wuhan.anjuke.com/sale/caidianz/' class='' >蔡甸</a><a href='http://wuhan.anjuke.com/sale/hannanz/' class='' >漢南</a><a href='http://wuhan.anjuke.com/sale/xinzhouz/' class='' >新洲</a><a href='http://wuhan.anjuke.com/sale/qitao/' class='' >其餘</a> # <div class="sub-items"><span class="selected-item">所有</span> # <span class="sub-letter-item" style="color: #f60;margin-right: 3px;">D</span> # <a href='http://wuhan.anjuke.com/sale/donghudongting/' class=''>東湖東亭</a> # <a href='http://wuhan.anjuke.com/sale/dongtingwuchanga/?from=shangquan' class=''>東亭</a>< # a href='http://wuhan.anjuke.com/sale/dingziqiao/?from=shangquan' class=''>丁字橋</a> # <span class="sub-letter-item" style="color: #f60;margin-right: 3px;">F</span> # <a href='http://wuhan.anjuke.com/sale/fujiapo/' class=''>傅家坡</a> # <span class="sub-letter-item" style="color: #f60;margin-right: 3px;">J</span> # <a href='http://wuhan.anjuke.com/sale/jiyuqiao/' class=''>積玉橋</a> # <span class="sub-letter-item" style="color: #f60;margin-right: 3px;">L</span><a href='http://wuhan.anjuke.com/sale/liangdaojie/' class=''>糧道街</a><span class="sub-letter-item" style="color: #f60;margin-right: 3px;">N</span><a href='http://wuhan.anjuke.com/sale/nanhuhuayuan/' class=''>南湖花園</a><span class="sub-letter-item" style="color: #f60;margin-right: 3px;">S</span><a href='http://wuhan.anjuke.com/sale/shuiguohu/' class=''>水果湖</a><a href='http://wuhan.anjuke.com/sale/simenkou/' class=''>司門口</a><a href='http://wuhan.anjuke.com/sale/shouyilu/?from=shangquan' class=''>首義路</a><span class="sub-letter-item" style="color: #f60;margin-right: 3px;">T</span><a href='http://wuhan.anjuke.com/sale/tuanjiedadao/' class=''>團結大道</a><span class="sub-letter-item" style="color: #f60;margin-right: 3px;">W</span><a href='http://wuhan.anjuke.com/sale/wuchanghuochezhan/' class=''>武昌火車站</a><a href='http://wuhan.anjuke.com/sale/wuchangzhoubian/' class=''>武昌周邊</a><a href='http://wuhan.anjuke.com/sale/wutaizhafenghuo/' class=''>武泰閘烽火</a><a href='http://wuhan.anjuke.com/sale/wutaizha/?from=shangquan' class=''>武泰閘</a><span class="sub-letter-item" style="color: #f60;margin-right: 3px;">X</span><a href='http://wuhan.anjuke.com/sale/xiaodongmen/' class=''>小東門</a><a href='http://wuhan.anjuke.com/sale/xudong/' class=''>徐東</a><a href='http://wuhan.anjuke.com/sale/xujiapeng/' class=''>徐家棚</a><span class="sub-letter-item" style="color: #f60;margin-right: 3px;">Y</span><a href='http://wuhan.anjuke.com/sale/yangyuand/' class=''>楊園</a><a href='http://wuhan.anjuke.com/sale/yuemachangshouyi/' class=''>閱馬場首義</a><span class="sub-letter-item" style="color: #f60;margin-right: 3px;">Z</span><a href='http://wuhan.anjuke.com/sale/zhonghualud/' class=''>中華路</a><a href='http://wuhan.anjuke.com/sale/ziyanglu/' class=''>紫陽路</a><a href='http://wuhan.anjuke.com/sale/zhongbeilu/' class=''>中北路</a><a href='http://wuhan.anjuke.com/sale/zhongnandingziqiao/' class=''>中南丁字橋</a></div></span></div><!-- 區域 end--> for wb_url in wb_information: finallyData = { "url":wb_url.get("href"), "address":wb_url.get_text() } url_finally.append(finallyData) url_qx_finally(url_qxTop) #這裏咱們就獲得了全部的url #print(url_finally) #接下來咱們鏈接mysql #打開數據庫鏈接 conn = pymysql.connect( host = "localhost", user = "用戶名", password = "密碼", port = 3306, db = "mysql", charset = "UTF8" ) #使用cursor()方法獲取操做遊標 cursor = conn.cursor() #cursor.execute("DROP TABLE if 安居客20160917 exs") sql= """ create table 安居客20160917( 房價 CHAR (30), 地址 CHAR (200)) """ cursor.execute(sql) #cursor.close() #cursor.execute("select * from 安居客20160917") #results = cursor.fetchall() #print(results) for urlOrignal in url_finally: for i in range(1): url = (urlOrignal["url"] + "p{}/").format(i) data = requests.get(url).text soup = BeautifulSoup(data, "html.parser") address = soup.select("div.house-details > div > span.comm-address") #print(len(address)) prices = soup.select(("div.house-details > div > span:nth-of-type(3)")) #print(len(prices)) #break # print(price) #if prices == None: #continue #else: for price, address in zip(prices, address): price = price.get_text() #print(price) address = address.get("title") #print(address) try: cursor.execute("insert into 安居客20160917 (房價,地址) values (%s,%s)",(price,address)) except: conn.rollback() conn.commit() #print(url) url = urlOrignal cursor.close() conn.close()

相關文章
相關標籤/搜索