最近一直在關注Python寫爬蟲相關的知識,嘗試了採用requests + Beautiful Soup來爬取房天下(原搜房網)的推薦新樓盤。html
不用不知道,一用發現有驚喜也有驚嚇,本文就一同記錄下驚喜和踩的一些亂碼的坑。
python
首先,以爲Beautiful soup解析網頁更加符合人類的常規思惟,比使用正則表達式(python中的re庫)更容易理解。 同時關於requests遇到了中文字符和特殊字符解碼的問題。本文都將給於深刻的解說。正則表達式
軟件環境bash
Python : 3.6.0 app
PyCharm: Community 2017.2 ide
庫1 : requests測試
庫2 : beautifulsoupui
Requests知識概要this
Requests 是用Python語言編寫,基於 urllib,採用 Apache2 Licensed 開源協議的 HTTP 庫。它比 urllib 更加方便,能夠節約咱們大量的工做,徹底知足 HTTP 測試需求。編碼
徹底支持Python3哦
1. 安裝requests
能夠採用pip安裝requests,具體代碼是:
pip install requests
2. 簡單例子
下面例子中是幾個經常使用的屬性,response.text能夠獲得html的源代碼,response.encoding默認爲ISO-8859-1,此編碼針對英文沒有問題,若是咱們的網頁中帶有中文,將會引發亂碼的問題。後面有解決方案。
import requests response = requests.get("http://sh.fang.com/") #獲取一個Http請求 print(response.encoding) #當前編碼,默認爲 ISO-8859-1 print(response.apparent_encoding) #當前網頁的內容的實際編碼 print(response.content) #content返回的是bytes型的原始數據 print(response.text) #text返回的是處理過的Unicode型的數據
Beautiful Soup簡要知識
1. Beautiful Soup 是一個很是流行的 Python 模塊。 該模塊能夠解析網頁, 並
提供定位 內 容的便捷接 口 。 若是你尚未安裝該模塊, 可使用下面的命令
安裝其最新版本:
pip install beautifulsoup4
使用 Beautiful Soup 的第一步是將下載的 HTML 內容解析爲 soup 文檔 。
2. 如何引用Beautiful Soup
from bs4 import BeautifulSoup
3. 一個簡單例子
from bs4 import BeautifulSoup html = "<ul class='country'><li>中國</li><li>美國</li></ul>" # 解析html而後獲得一個soup文檔 soup = BeautifulSoup(html,'html.parser') html = soup.prettify() ul = soup.find('ul',attrs={'class':'country'}) #下面代碼只返回一個匹配的元素 print(ul.find('li')) # returns the first match element #下面代碼返回又有匹配的元素 for countryname in ul.find_all('li'): print(countryname)
實戰爬取房天下推薦新樓盤
在Chrome中打開sh.fang.com地址,按F12觀察新盤推薦tab的網頁源代碼結構
主要結構是: 頂層爲一個名爲ti011的div,下面有四個class爲tenrtd的div用於四個樓盤的現實。每一個樓盤下面有一個class = "text1"的div存儲了樓盤名稱,另外一個class = "text2"的存儲了樓盤的價格。
2. 初版代碼完成以下,可是發現有一箇中文亂碼的問題
from bs4 import BeautifulSoup import requests rep = requests.get("http://sh.fang.com/") html = rep.text soup = BeautifulSoup(html, 'html.parser') #獲取頂層 新盤推薦 的整個div div = soup.find('div',attrs={'id':'ti011'}) #獲取四個樓盤的div,更具他們的class = "tenrtd" for house in div.find_all('div', attrs={'class': 'tenrtd'}): # 根據class="text1"獲取存儲樓盤標題的div titleDiv = house.find('div', attrs={'class': 'text1'}) title = titleDiv.find('a').text # 根據class="text2"獲取存儲樓盤價格的div priceDiv = house.find('div', attrs={'class': 'text2'}) price = priceDiv.find('b').text print(title, " ", price)
輸出的結果盡然是這樣, Why?
Öнðº£ÌÄÍå 48000Ôª/©O »ªÒêÒÝÆ·À½Íå 43057Ôª/©O Öйú¹é¹È´´¿ÍSOHO ¼Û¸ñ´ý¶¨ ÐÂÎ÷ÌÁ¿×ȸ³Ç 14000Ôª/©O
3. 研究中文亂碼問題
中文亂碼也算是requests常見的一個問題,爲何會這樣的呢,看bs本身的文檔描述
Encodings When you receive a response, Requests makes a guess at the encoding to use for decoding the response when you access the Response.text attribute. Requests will first check for an encoding in the HTTP header, and if none is present, will use chardet to attempt to guess the encoding. The only time Requests will not do this is if no explicit charset is present in the HTTP headersand the Content-Type header contains text. In this situation, RFC 2616 specifies that the default charset must be ISO-8859-1. Requests follows the specification in this case. If you require a different encoding, you can manually set the Response.encoding property, or use the rawResponse.content.
其實重要的也就是若是沒有在requests的header部分裏面指定encoding的話,那麼默認採用ISO-8859-1這個編碼去解碼response.content裏面的字節數據。ISO-8859-1針對英文是沒有問題的,可是針對中文就不行了。
解決思路固然就是換encoding,可是咱們應該用什麼編碼呢?下面這個代碼能夠看到網頁的實際encoding,下面代碼將輸出:GB2312 ,因此咱們採用GB2312來解碼咱們的數據。
print(rep.apparent_encoding)
4.換編碼解決中文字符
通過上面的研究,咱們修訂代碼對response設定encoding = "GB2312"
rep.encoding = "GB2312"
運行後結果以下:
中金海棠灣 48000元/�O 華誼逸品瀾灣 43057元/�O 中國歸谷創客SOHO 價格待定 新西塘孔雀城 14000元/�O
發現代碼顯示的結果中㎡又亂碼了,不該該啊,這又是爲何呢? 下面接着研究。。。
5. 研究解決特殊字符亂碼問題
引發亂碼的緣由估計就是在字符集中找不到特定的字符,好比這個㎡。是否是GB2312這個字符集不夠全面呢?帶着這個疑問去查閱相關的資料關於中文的幾個編碼:
GB2312
GB 2312 或 GB 2312-80 是中國國家標準簡體中文字符集,全稱《信息交換用漢字編碼字符集·基本集》,又稱 GB 0,由中國國家標準總局發佈,1981 年 5 月 1 日實施。GB 2312 編碼通行於中國大陸;新加坡等地也採用此編碼。中國大陸幾乎全部的中文系統和國際化的軟件都支持 GB 2312。GB 2312 標準共收錄 6763 個漢字,其中一級漢字 3755 個,二級漢字 3008 個;同時收錄了包括拉丁字母、希臘字母、日文平假名及片假名字母、俄語西裏爾字母在內的 682 個字符。GB 2312 的出現,基本知足了漢字的計算機處理須要,它所收錄的漢字已經覆蓋中國大陸99.75% 的使用頻率。對於人名、古漢語等方面出現的罕用字,GB 2312 不能處理,這致使了後來 GBK 及 GB 18030 漢字字符集的出現。
GBK
GBK 即漢字內碼擴展規範,K 爲漢語拼音 Kuo Zhan(擴展)中「擴」字的聲母。英文全稱 Chinese Internal Code Specification。GBK 共收入 21886 個漢字和圖形符號,包括:GB 2312 中的所有漢字、非漢字符號。BIG5 中的所有漢字。與 ISO 10646 相應的國家標準 GB 13000 中的其它 CJK 漢字,以上合計 20902 個漢字。其它漢字、部首、符號,共計 984 個。GBK 向下與 GB 2312 徹底兼容,向上支持 ISO 10646 國際標準,在前者向後者過渡過程當中起到的承上啓下的做用。GBK 採用雙字節表示,整體編碼範圍爲 8140-FEFE 之間,首字節在 81-FE 之間,尾字節在 40-FE 之間,剔除 XX7F 一條線
GB18030
GB 18030,全稱:國家標準 GB 18030-2005《信息技術中文編碼字符集》,是×××現時最新的內碼字集,是 GB 18030-2000《信息技術信息交換用漢字編碼字符集基本集的擴充》的修訂版。GB 18030 與 GB 2312-1980 和 GBK 兼容,共收錄漢字70244個。與 UTF-8 相同,採用多字節編碼,每一個字能夠由 1 個、2 個或 4 個字節組成。編碼空間龐大,最多可定義 161 萬個字符。支持中國國內少數民族的文字,不須要動用造字區。漢字收錄範圍包含繁體漢字以及日韓漢字
從上文中能夠簡要的得出GB 2312 過期標準、GBK 微軟標準、GB 18030 國家標準,字符個數方面:GB 18030 > GBK > GB2312
因此咱們決定採用 GB18030來解碼咱們的數據,代碼改動以下:
rep.encoding = "gb18030"
附上完整的代碼
from bs4 import BeautifulSoup import requests rep = requests.get("http://sh.fang.com/") rep.encoding = "gb18030" html = rep.text soup = BeautifulSoup(html, 'html.parser') #獲取頂層 新盤推薦 的整個div div = soup.find('div',attrs={'id':'ti011'}) #獲取四個樓盤的div,更具他們的class = "tenrtd" for house in div.find_all('div', attrs={'class': 'tenrtd'}): # 根據class="text1"獲取存儲樓盤標題的div titleDiv = house.find('div', attrs={'class': 'text1'}) title = titleDiv.find('a').text # 根據class="text2"獲取存儲樓盤價格的div priceDiv = house.find('div', attrs={'class': 'text2'}) price = priceDiv.find('b').text print(title, " ", price)
輸出結果:
中金海棠灣 48000元/㎡ 華誼逸品瀾灣 43057元/㎡ 中國歸谷創客SOHO 價格待定 新西塘孔雀城 14000元/㎡
問題解決了,關鍵知識點總結:
能夠採用requests庫來獲取網頁html
採用Beautiful soup基於html構建一個soup文檔,而後用find 或者 find_all方法查詢本身須要的html節點
根據目標網頁的內容來更改response.encoding從而解決亂碼問題