למרות שהמאמר הזה כבר פורסם באדיבות בבלוג של ג'סטין אנג'ל, אביא אותו אצלי בשנית, בעיקר בגלל שראיתי את השאלה הזאת עולה שוב ושוב בפורומים השונים.
הפתרון הבא עוסק בזיהוי נכון של קידוד דף הWeb בשימוש במחלקות כמו WebRequest. הבעיה במקרים אלו היא שלא תמיד בוני האתרים טורחים לשלוח עם הדפים שהם כותבים את הHeader (כותרת http) שמזהה את קידוד הדף, ולכן במקרים אלו צריך קודם לקרוא את הדף עד הסוף, ורק אז לשלוף את כותרת הmeta המתאימה מהדף עצמו ולקודד איתה את הדף.
זהו תסריט השימוש הרצוי:
Sub Test()
Dim connection As New CWebConnection(New Uri("http://www.tapuz.co.il/"))
connection.SendRequest()
Dim distortedContent As String = connection.GetContent
Dim readableContent As String = connection.GetDecodedContent
End Sub
וזוהי מחלקה שממשת את הדרישות והתהליך שתיארנו:
Imports System.Text
Imports System.Text.RegularExpressions
Imports System.Net
Imports System.IO
Public Class CWebConnection
Private m_request As HttpWebRequest
Private m_response As HttpWebResponse
Private m_content As String
Public Sub New(ByVal url As Uri)
m_content = Nothing
m_request = WebRequest.Create(url)
m_request.Credentials = CredentialCache.DefaultCredentials
m_request.Timeout = 30000
End Sub
Public Sub SendRequest()
m_response = m_request.GetResponse()
End Sub
Public Function GetContent() As String
If m_content = Nothing Then
Dim dataStream As Stream = m_response.GetResponseStream()
Using reader As New StreamReader(dataStream, Encoding.Default)
m_content = reader.ReadToEnd() + vbCrLf
End Using
End If
Return m_content
End Function
Public Function GetDecodedContent() As String
Dim raw As String = GetContent()
Dim dest_enc As Encoding = GetEncoding()
Return ConvertEncoding(raw, Encoding.Default, dest_enc)
End Function
Public Function GetEncoding() As Encoding
Dim pattern As String = "<\s*meta\s*http-equiv=\s*[""']content-type\s*[""']\s*content\s*=\s*[""']\s*[\w/\\]*\s*;\s*charset\s*=\s*(?<encoding>[^""';>]*)\s*[""']\s*/?\s*>"
Dim m_RegEx As Regex
Dim encMatch As Match
Dim enc As Encoding = Encoding.GetEncoding("windows-1255")
If m_content = Nothing Then
Throw New Exception("First call GetContent()")
End If
If GetEncodingHeader() IsNot Nothing Then
Return GetEncodingHeader()
Else
m_RegEx = New Regex(pattern, RegexOptions.IgnoreCase Or RegexOptions.Compiled)
encMatch = m_RegEx.Match(m_content)
If encMatch.Success Then
Try
enc = Encoding.GetEncoding(encMatch.Groups("encoding").ToString.ToLower)
Catch
End Try
End If
End If
Return enc
End Function
Public Function GetEncodingHeader() As Encoding
Dim output As Encoding = Nothing
If m_response.ContentEncoding <> "" Then
Try
output = Encoding.GetEncoding(m_response.ContentEncoding)
Catch
output = Nothing
End Try
End If
Return output
End Function
Public Shared Function ConvertEncoding(ByVal raw As String, ByVal src_enc As Encoding, ByVal dest_enc As Encoding) As String
Return src_enc.GetChars(Encoding.Convert(dest_enc, src_enc, src_enc.GetBytes(raw)))
End Function
End Class
במחלקה הזאת אנו שולחים בקשה לדף ע"י המתודה SendRequest, אח"כ יכולים לקבל את הדף כמו שהוא (קידוד ברירת מחדל) ע"י הפונקציה GetContent, או לקבל את הדף בקידוד הנכון ע"י GetDecodedContent.
GetDecodedContent עובד בשני שלבים:
- בודק אם נשלחה כותרת הhttp, אם כן אז משתמשים בה, אם לא אז ממשיכים לשלב 2.
- מחפש ע"י ביטוי רגולרי את כותרת הmeta של הדף עצמו ושולפים משם את הקידוד (אם יש).