[原創]手把手教你寫網路爬蟲(8):亂碼

来源:https://www.cnblogs.com/tuohai666/archive/2018/04/27/8960736.html
-Advertisement-
Play Games

手把手教你寫網路爬蟲(8) 作者:拓海 摘要:從零開始寫爬蟲,初學者的速成指南! 封面: 字元編解碼是爬蟲里必學的一項知識,在我們的爬蟲生涯中早晚會爬到亂碼的網頁,與其遇到時驚慌失措,不如早學早好,徹底避免亂碼問題。 字元編碼簡介 什麼是字元集 在介紹字元編碼之前,我們先瞭解下什麼是字元集。 字元( ...


 

手把手教你寫網路爬蟲(8)

作者:拓海

摘要:從零開始寫爬蟲,初學者的速成指南!

封面:

 

字元編解碼是爬蟲里必學的一項知識,在我們的爬蟲生涯中早晚會爬到亂碼的網頁,與其遇到時驚慌失措,不如早學早好,徹底避免亂碼問題。

 

字元編碼簡介

什麼是字元集

在介紹字元編碼之前,我們先瞭解下什麼是字元集。

字元(Character)是各種文字和符號的總稱,包括各國家文字、標點符號、圖形符號、數字等。字元集(Character set)是多個字元的集合,字元集種類較多,每個字元集包含的字元個數不同,常見字元集:ASCII字元集、GBK字元集、Unicode字元集等。

什麼是字元編碼

字元編碼和字元集不同。字元集只是字元的集合,無法進行網路傳送、處理,必須經編碼後才能使用。如Unicode字元集可依不同需求以UTF-8、UTF-16、UTF-32等方式編碼。

字元編碼就是以二進位的數字來對應字元集的字元。各個國家和地區在制定編碼標準的時候,“字元的集合”和“編碼”一般都是同時制定的。因此,平常我們所說的“字元集”,除了有“字元的集合”這層含義外,同時也包含了“編碼”的含義。

常用字元集

簡單介紹幾個常見的。

ASCII:

ASCII是學電腦同學的啟蒙字元集,一般是從這本書里學到的:

 

請允許我懷舊一下,以下引用譚浩強老師的講解:

 

 

中文字元集:

GB2312:包含6763個漢字。

GBK:包含21003個漢字。GBK相容GB2312,也就是說用GB2312編碼的漢字可以用GBK來解碼。

GB18030:收錄了70000個漢字,這麼多是因為包含了少數民族文字。同樣相容GBK和GB2312。

Unicode:Unicode 是為瞭解決傳統的字元編碼方案的局限而產生的,它為每種語言中的每個字元設定了統一併且唯一的二進位編碼,以滿足跨語言、跨平臺進行文本轉換、處理的要求。具有多種編碼方式,如UTF-7、 UTF-8、UTF-16、UTF-32等。

 

為什麼會產生亂碼

簡單的說亂碼的出現是因為:編碼和解碼時用了不同的字元集。對應到真實生活中,就好比是一個英國人為了表示祝福在紙上寫了bless(編碼)。而一個法國人拿到了這張紙,由於在法語中bless表示受傷的意思,所以認為他想表達的是受傷(解碼)。同理,在電腦中,一個用UTF-8編碼後的字元,用GBK去解碼。由於兩個字元集的字型檔表不一樣,同一個漢字在兩個字元表的位置也不同,最終就會出現亂碼。

那麼,爬蟲中的亂碼是怎麼產生的,又該如何解決呢?

 

爬蟲中的亂碼

假設我們的爬蟲是java開發的,網路請求庫使用OkHttp,網頁存儲到MongoDB中。亂碼產生的過程如下:

  1. OkHttp請求指定url,返回了一個GBK編碼的網頁位元組流;
  2. OkHttp以預設UTF-8進行解碼(此時已亂),並以UTF-16方式編碼為Java的String類型,返回給處理程式。(為什麼以UTF-16方式編碼?因為Java的數據在記憶體中的編碼是UTF-16);
  3. 爬蟲拿到這個編碼錯誤的String類型的網頁,調用MongoDB的API,將數據編碼為UTF-8存儲到資料庫中。所以最後在資料庫看到的數據是亂的。

 

 

顯然,導致亂碼的根本原因就是OkHttp在最初使用了錯誤的解碼方式進行解碼。所以要解決這個問題,就要讓OkHttp知道網頁的編碼類型,進行正確的解碼。

 

 

網頁有兩種約定的方式告訴爬蟲自己使用的是什麼編碼方式:

1. Http協議的響應頭中的約定:

  Content-Type: text/html;charset=utf-8

2. Html中meta標簽中的約定:

  <meta http-equiv=”Content-Type” content=”text/html; charset=UTF-8“/>

從約定中獲取網頁的編碼後,Okhttp就可以正確的解碼了。然而實際情況卻並不樂觀,很多網頁並不遵守約定,缺少這兩個信息。有人通過Alexa統計各國遵守這個約定的網頁數:

語言

URL尾碼

URL

HTTP頭中包含

charset的URL數

Chinese

.cn

10086

3776

English

.us/.uk

21565

13223

Russian

.ru

39453

28257

Japanese

.jp

20339

6833

Arabic

.iq

1904

1093

German

.de

35318

23225

Persian

.ir

7396

4018

Indian

.in

12236

4867

Total

all

148297

85292

結果表明我們不能被動的依賴網頁告訴我們,而要根據網頁內容來主動探測其編碼類型。

 

探測字元編碼

什麼是字元編碼自動檢測?

它是指當面對一串不知道編碼信息的位元組流的時候,嘗試著確定一種編碼方式以使我們能夠讀懂其中的文本內容。它就像我們沒有解密鑰匙的時候,嘗試破解出編碼。

那不是不可能的嗎?

通常來說,是的,不可能。但是,有一些編碼方式為特定的語言做了優化,而語言並非隨機存在的。有一些字元序列在某種語言中總是會出現,而其他一些序列對該語言來說則毫無意義。一個熟練掌握英語的人翻開報紙,然後發現“txzqJv 2!dasd0a QqdKjvz”這樣一些序列,他會馬上意識到這不是英語(即使它完全由英語中的字母組成)。通過研究許多具有“代表性(typical)”的文本,電腦演算法可以模擬人的這種對語言的感知,並且對一段文本的語言做出啟發性的猜測。換句話說就是,檢測編碼信息就是檢測語言的類型,並輔之一些額外信息,比如每種語言通常會使用哪些編碼方式。

這樣的演算法存在嗎?

結果證明,是的,它存在。所有主流的瀏覽器都有字元編碼自動檢測的功能,因為互聯網上總是充斥著大量缺乏編碼信息的頁面。Mozilla Firefox包含有一個自動檢測字元編碼的庫,已經移植到Python中,叫做chardet。

chardet使用

安裝:

pip install chardet

使用:

>>> import urllib

>>> rawdata = urllib.urlopen('http://www.jd.com/').read()

>>> import chardet

>>> chardet.detect(rawdata)

{'confidence': 0.98999999999999999, 'language': '', 'encoding': 'utf-8'}

註意:返回結果中有confidence,即置信度,這說明探測結果不是100%準確的。

使用其他語言的小伙伴不用擔心,chardet在很多語言中都有移植版。不過C++好像沒有太好的選擇,可以考慮使用IBM的ICU(http://site.icu-project.org/)。

 

擴展閱讀

《A composite approach to language/encoding detection》

(https://www-archive.mozilla.org/projects/intl/UniversalCharsetDetection.html)

這篇論文解釋了Chardet背後使用的探測演算法,分別是“編碼模式方法”、“字元分佈方法”和“雙字元序列分佈方法”。最後說明瞭三種方法組合使用的必要性,並舉例說明如何組合使用。

《Charset Encoding Detection of HTML Documents A Practical Experience》

(https://github.com/shabanali-faghani/IUST-HTMLCharDet/blob/master/wiki/Charset-Encoding-Detection-of-HTML-Documents.pdf)

利用現有的探測技術,通過一些技巧來提高探測的準確性。主要原理是組合使用Mozilla CharDet和IBM ICU,併在探測前巧妙的去掉了HTML標簽。雖然這是伊朗大學發的Paper,但據說這種方法已經在生產環境取得了很好的效果,目前正應用在一個10億級別數據量的大型爬蟲上。

 

下一步

最近聊的話題越來越沉重,想必大家也累了。下期打算帶大家一起放鬆一下,聊點輕鬆的話題。從系列的開篇到現在也有半年了,技術領域有了不小的更新,出現了一些好用的工具,我們需要替換哪些工具呢?請聽下回分解!

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 以前排版排的比較少,沒有設計圖的那種,我們後端一般都是用框架或者仿照樣式,最近公司需要定製一個企業站,要還原設計稿。我在排版中大量用到了垂直居中與水平居中。 1,傳統的水平居中(固定寬度居中),如一個div,寬1200 2,水平與垂直居中,網上有很多種方式,我現在用的是這種方式 這個方法使用絕對定位 ...
  • 組件: ng g component component/demo 服務: ng g service service/news 然後在app.module.ts里引入 ng g service service/news --module=app 在創建服務時候在後面加上 --module=app 就 ...
  • 1.首先,下載xampp,安裝按預設勾選即可。 2.安裝完成後,啟動Apache和MySQL這兩個服務。 啟動後變成綠色,表示啟動成功。 3.點擊MySQL項的Admin進入資料庫後臺。 4.點擊用戶賬戶新建用戶。 5.填寫用戶名,host name選本地,生成密碼,用戶名密碼用文本保留好。 5.選 ...
  • 在anular4更新到angular5後,有些模塊也發生了有些變化,例如http模塊。 首先在app.module.ts裡面引入HttpClientModule import { HttpClientModule } from '@angular/common/http'; 在組件使用的時候引入: ...
  • jQuery length和size()區別總結如下: 1.length是屬性,size()是方法。 2.如果你只是想獲取元素的個數,兩者效果一樣既 $("img").length 和 $("img").size() 獲取的值是一樣的。 3.如果計算一個字元串的長度或者計算一個數組元素的個數就只得用 ...
  • 首先請原諒本文標題取的有點大,但並非為了嘩眾取寵。本文取這個標題主要有3個原因,這也是寫作本文的初衷: (1)目前國內幾乎搜索不到全面講解如何搭建前後端分離框架的文章,講前後端分離框架思想的就更少了,而筆者希望在本文中能夠全面、詳細地闡述我們團隊在前後端分離的摸索中所得到的搭建思路、最佳實踐以及架構 ...
  • [Java] Design Pattern:Code Shape manage your code shape Code Shape Design Pattern Here I will introduce a design pattern: Code Shape。 It's fine that y ...
  • 前幾天寫了一篇 Java 8 即將在 2019 年停止免費向企業提供更新的文章,企圖迫使用戶向更新一代的 Java 版本升級,但讓人遺憾的是,小編今天收到了 Oracle Java 版本的升級推送,裝完居然是 Java 10 !!!! 很意外,為什麼直接從 Java 8 升級到了 Java 10,去 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...