1. 程式人生 > >c#爬取Silverlight網頁

c#爬取Silverlight網頁

前言:

爬取普通的文字網頁非常容易,但爬取Silverlight的網頁程式碼時,有時候可能會加密。這樣就會很麻煩了。下面就爬取網站http://zx.bjmemc.com.cn/ (北京空氣質量網)進行說明。

任務:

網站http://zx.bjmemc.com.cn/顯示的內容如下圖所示。我們的任務就是將空氣質量資料抓取下來。


工具:

1、fiddler,http://www.telerik.com/fiddler,一款優秀的網頁請求分析工具

2、reflector,http://download.csdn.net/detail/qing_lgq/6764265,.net原始碼破解工具

步驟

1、安裝fiddler和reflector,並破解reflector。注意

,下載、安裝和破解reflector的時候請關閉防毒軟體,內有註冊機,可能會被防毒軟體直接刪除。

2、開啟fiddler,使之處於監聽狀態。

3、用瀏覽器開啟網頁:http://zx.bjmemc.com.cn/。(此網站做得非常好,為了防止爬取,一旦使用者開啟瀏覽器自帶的developer tool,就不會載入任何東西,為他們點個贊,這就是為什麼我們必須用fiddler等分析工具的原因)

4、待網頁載入完畢,fiddler便已經抓取到了所有的網頁請求,如下圖所示。在此簡要說明一下fiddler的頁面佈局。左側是所有的網頁請求,右上是傳送請求資訊,右下是對應的接收資訊。最下邊狀態列的左邊有兩個按鈕,左側的是監聽開關(下圖是關閉狀態,未顯示該按鈕),右側的ie圖示是監聽物件,可以選擇監聽網頁請求、非網頁請求還是所有程序的請求。注意請求資訊和接收資訊視窗都有很多tag,根據自己的需求切換。


5、在fiddler左側找到網頁的直接請求,即上圖的左側的第二項,返回資訊視窗的tag切換到textview。在視窗的搜尋框輸入xap,快速找到<param name="source" value="ClientBin/BEPB.xap"/>,其中的value屬性值就是後臺Silverlight的程式碼。

6、將ClientBin/BEPB.xap與當前網頁網址拼接為http://zx.bjmemc.com.cn/ClientBin/BEPB.xap,用瀏覽器開啟,瀏覽器自動下載BEPB.xap檔案。

7、修改BEPB.xap檔名字尾為BEPB.zip,用壓縮檔案解壓縮,得到的資料夾內容如下圖所示。這些就是Silverlight後臺檔案以及引用檔案。其中BEPB.dll是該Silverlight專案的核心檔案。


8、用reflector開啟BEPB.dll,如下圖所示。左側是方法名/名稱空間名,右側是對應的程式碼。接下來便是一個痛苦的過程了,得一個一個分析,找到金鑰。但是還是有章可循。


9、fiddler繼續上場。如下圖所示。經過分析,獲得空氣質量資料的是第7個請求,選中它,右下部分的返回資訊視窗的tag選擇HexView,表示以十六進位制顯示。右上部分發送資訊視窗選擇Raw,可是傳送時post的資料呼叫了DataService下的GetWebData方法,在reflector的搜尋框中輸入此兩個關鍵字,如此順藤摸瓜,便能很快找到加密的密碼。


10、上圖右下返回資訊視窗的hexview中,兩端有部分資訊並不是加密資訊,分析需要將返回資訊的兩端無用位元組刪除。

以下附上Silverlight加解密的程式碼。具體抓取程式碼便不公佈了,具體情況具體分析,這裡只是提供一個大體思路。

/// <summary>
/// hmacSha1演算法加密(生成長度40)
/// </summary>
/// <param name="signatureString">加密明文</param>
/// <param name="secretKey">加密金鑰</param>
/// <param name="raw_output">是否輸出原始編碼</param>
/// <returns></returns>
public static object hmacSha1(string signatureString, string secretKey, bool raw_output = false)
{
	var enc = Encoding.UTF8;
	HMACSHA1 hmac = new HMACSHA1(enc.GetBytes(secretKey));
	hmac.Initialize();

	byte[] buffer = enc.GetBytes(signatureString);
	if (raw_output)
	{
		return hmac.ComputeHash(buffer);
	}
	else
	{
		return BitConverter.ToString(hmac.ComputeHash(buffer)).Replace("-", "").ToLower();
	}
}

/// <summary>
/// 使用AES加密字串
/// </summary>
/// <param name="encryptString">待加密字串</param>
/// <param name="encryptKey">加密密匙</param>
/// <param name="salt">鹽</param>
/// <returns>加密結果,加密失敗則返回源串</returns>
public static string EncryptAES(string encryptString, string encryptKey, string salt)
{
	AesManaged aes = null;
	MemoryStream ms = null;
	CryptoStream cs = null;

	string str = null;

	try
	{
		Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(encryptKey, Encoding.UTF8.GetBytes(salt));

		aes = new AesManaged();
		aes.Key = rfc2898.GetBytes(aes.KeySize / 8);
		aes.IV = rfc2898.GetBytes(aes.BlockSize / 8);

		ms = new MemoryStream();
		cs = new CryptoStream(ms, aes.CreateEncryptor(), CryptoStreamMode.Write);

		byte[] data = Encoding.UTF8.GetBytes(encryptString);
		cs.Write(data, 0, data.Length);
		cs.FlushFinalBlock();

		str = Convert.ToBase64String(ms.ToArray());
	}
	catch
	{
		str = encryptString;
	}
	finally
	{
		if (cs != null)
			cs.Close();

		if (ms != null)
			ms.Close();

		if (aes != null)
			aes.Clear();
	}

	return str;
}

/// <summary>
/// 使用AES解密字串
/// </summary>
/// <param name="decryptString">待解密字串</param>
/// <param name="decryptKey">解密密匙</param>
/// <param name="salt">鹽</param>
/// <returns>解密結果,解謎失敗則返回源串</returns>
public static string DecryptAES(string decryptString, string decryptKey, string salt)
{
	AesManaged aes = null;
	MemoryStream ms = null;
	CryptoStream cs = null;

	string str = null;

	try
	{
		Rfc2898DeriveBytes rfc2898 = new Rfc2898DeriveBytes(decryptKey, Encoding.UTF8.GetBytes(salt));

		aes = new AesManaged();
		aes.Key = rfc2898.GetBytes(aes.KeySize / 8);
		aes.IV = rfc2898.GetBytes(aes.BlockSize / 8);

		ms = new MemoryStream();
		cs = new CryptoStream(ms, aes.CreateDecryptor(), CryptoStreamMode.Write);

		byte[] data = Convert.FromBase64String(decryptString);
		cs.Write(data, 0, data.Length);
		cs.FlushFinalBlock();

		str = Encoding.UTF8.GetString(ms.ToArray(), 0, ms.ToArray().Length);
	}
	catch
	{
		str = decryptString;
	}
	finally
	{
		if (cs != null)
			cs.Close();

		if (ms != null)
			ms.Close();

		if (aes != null)
			aes.Clear();
	}

	return str;
}