最近在自學用python進行網(wǎng)頁數(shù)據(jù)抓取坦辟,結果被中文亂碼的問題折騰了好久刊侯。網(wǎng)上google了各種解決方案都無法解決我遇到的問題,索性自己深入的研究了下锉走,終于把這難題給解決了滨彻。在此梳理下整個分析過程。
網(wǎng)站&開發(fā)工具
- 網(wǎng)站:http://www.jjwxc.net/fenzhan/noyq/
- python v2.7
- BeautifulSoup v4.0
遇到的問題
一開始我的代碼是這樣寫的:
<pre><code>response = requests.get('http://www.jjwxc.net/fenzhan/noyq/')
soup = bs4.BeautifulSoup(response.text, "html.parser")
print soup.title
</code></pre>
執(zhí)行后返回的結果中文部分都變成了亂碼挪蹭。很顯然亭饵,是中文編碼在轉換過程中出現(xiàn)了問題。
分析
查看jjwxc.net的源碼梁厉,可以得知該網(wǎng)站的中文編碼是gb18030
<pre><code><meta http-equiv="Content-Type" content="text/html; charset=gb18030" /></code></pre>
查閱requestes以及BeautifulSoup的相關文檔辜羊,可知requests會自動將從服務器端獲取到的內容自動轉換成unicode, 而beauifulsoup也會將獲取到內容自動解碼成unicode。既然response.text已經是unicode形式词顾,那么再傳遞給beautifulsoup八秃,是unicode->unicode之間的直接傳遞,應該不存在編碼轉換錯誤的情況肉盹,那么為什么最后print出來的會是亂碼呢昔驱?
于是進一步查閱requests和bs4的官方文檔,發(fā)現(xiàn)了這樣兩段描述:
When you make a request, Requests makes educated guesses about the encoding of the response based on the HTTP headers. The text encoding guessed by Requests is used when you access r.text. You can find out what encoding Requests is using, and change it, using the r.encoding property.
Beautiful Soup uses a sub-library called Unicode, Dammit to detect a document’s encoding and convert it to Unicode. The autodetected encoding is available as the .original_encoding attribute of the BeautifulSoup object.Unicode, Dammit guesses correctly most of the time, but sometimes it makes mistakes. Sometimes it guesses correctly, but only after a byte-by-byte search of the document that takes a very long time. If you happen to know a document’s encoding ahead of time, you can avoid mistakes and delays by passing it to the BeautifulSoup constructor as from_encoding.
大意是requests和beautifulsoup都會自行猜測原文的編碼方式上忍,然后用猜測出來的編碼方式進行解碼轉換成unicode骤肛。大多數(shù)時候猜測出來的編碼都是正確的,但也有猜錯的情況窍蓝,如果猜錯了可以指定原文的編碼腋颠。
OK,那讓我們看一下requests和beautifulsoup是否猜對了原文編碼吓笙。
<pre><code>response = requests.get('http://www.jjwxc.net/fenzhan/noyq/')
print response.encoding
soup = bs4.BeautifulSoup(response.text, "html.parser")
print soup.original_encoding
<br />運行結果:
ISO-8859-1
None</code></pre>
可以發(fā)現(xiàn)是由于requests這里對原文的編碼猜錯了導致亂碼的出現(xiàn)淑玫,所以我們需要在response.text傳給beautifulsoup之前指定編碼:
<pre><code>response = requests.get('http://www.jjwxc.net/fenzhan/noyq/')
response.encoding = 'gb18030'
soup = bs4.BeautifulSoup(response.text, "html.parser")
print soup.title</code></pre>
但是運行后發(fā)現(xiàn)輸出的結果還是亂碼。
繼續(xù)查閱上述語句的相關官方文檔观蓄,發(fā)現(xiàn)beautifulsoup對于輸出內容的編碼方式有這樣一段介紹:
意即beautifulsoup在輸出文本時默認以UTF-8的方式編碼混移,無論原文是否以它進行編碼的。如果你不希望以UTF-8的方式編碼侮穿,可以用prettify()或則encode()方式來指定編碼歌径。
所以我們將代碼更改下:
<pre><code>response = requests.get('http://www.jjwxc.net/fenzhan/noyq/')
response.encoding = 'gb18030'
soup = bs4.BeautifulSoup(response.text, "html.parser")
print soup.title.prettify('gb18030')
print soup.title.encode('gb18030')
<br />運行結果:
<title>
非言情小說網(wǎng)|同人小說|古代純愛小說|現(xiàn)代純愛小說|同人純愛小說|動漫同人小說【晉江文學城】bl小說站
</title>
<title>非言情小說網(wǎng)|同人小說|古代純愛小說|現(xiàn)代純愛小說|同人純愛小說|動漫同人小說【晉江文學城】bl小說站</title></code></pre>
可以看到兩種指定方式都可以獲得無亂碼的中文內容。
這里再介紹下prettify()的功能亲茅,prettify()除了可以制定輸出的編碼方式回铛,它的最主要功能是對beautifulsoup的k語法分析樹重新排版狗准,使輸出的內容整潔易讀。這里用一段代碼說明:
至此茵肃,中文亂碼的問題解決了腔长。在查找解決方案的過程中,我另外查了下unicode验残、byte捞附、以及編碼和解碼方面的知識點。這一塊的東西解釋起來一時半會兒說不完您没,而且有些細節(jié)的地方我也沒完全搞懂鸟召,暫時就不賣弄了。對這塊內容感興趣的可以先看下下面這兩位大牛的文章氨鹏,寫得非常通俗易懂: