點選分類進入即可檢視到每個子分類都有很多圈子,圈子累死QQ的群組,是某一興趣團體的部落格,裡面收集很多相關的資料及資訊,如下所示:
這裡不關心圈子的有哪些寶貴學習資料,我更關心的是這些圈子的使用者如何采集出來,由于使用者都是網易的使用者,是以他們一個賬戶就會對應一個賬号,有163.com,163.net,yeah.net,126.com等等的,我們先看看圈子的使用者資訊是如何顯示的。
我們看到上圖裡面圈子的資訊是一個清單,有的圈子多,有的圈子少,不過他們的名稱中都會關聯一個部落格位址的,由于部落格位址和郵件位址有一一對應關系,是以可以擷取對應的郵件資訊,這就是我們所要的重要資訊。
下面用一個程式來介紹如何采集圈子的分類、圈子資料以及圈子使用者資料資訊,測試的程式如下所示:
下面我們來看看按鈕”重新整理分類資料“的實作代碼,主要是擷取圈子大類、圈子子類以及儲存資料操作,代碼如下所示:
private void btnRefreshCategory_Click(object sender, EventArgs e)
{
string url = "http://q.163.com/";
string mainTypeReg = "<div\\s*style=\"font-size:14px;\"><b><a\\s*?href=\"(?<value>.*?)\">(?<key>.*?)</a></b></div>";
string subTypeReg = "<div\\s*class=\"left\"><a\\s*href=\"(?<value>.*?)\">(?<key>.*?)</a></div> ";
#region 取得大類
httpHelper.Encoding = Encoding.Default;
string content = httpHelper.GetHtml(url);
Regex re = new Regex(mainTypeReg, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
Match mc = re.Match(content);
Dictionary<string, string> typeDict = new Dictionary<string, string>();
if (mc.Success)
{
MatchCollection mcs = re.Matches(content);
foreach (Match me in mcs)
{
string strKey = me.Groups["key"].Value;
string strValue = me.Groups["value"].Value;
//截取連接配接前面部分作為大類辨別
string newValue = strValue.TrimEnd('/');
int eIndex = newValue.LastIndexOf('/');
newValue = newValue.Substring(0, eIndex) + "/";
if (!typeDict.ContainsKey(strKey))
{
typeDict.Add(strKey, newValue);
}
}
}
#endregion
#region 取得子類
Dictionary<string, CircleSubTypeInfo> circleDict = new Dictionary<string, CircleSubTypeInfo>();
re = new Regex(subTypeReg, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
mc = re.Match(content);
string typeValue = strValue.TrimEnd('/');
int eIndex = typeValue.LastIndexOf('/');
typeValue = typeValue.Substring(0, eIndex) + "/";
if (!circleDict.ContainsKey(strKey))
CircleSubTypeInfo info = new CircleSubTypeInfo();
info.Name = strKey;
info.LinkUrl = strValue;
info.TypeUrlValue = typeValue;
circleDict.Add(strKey, info);
#region 儲存資料
Database db = DatabaseFactory.CreateDatabase();
DbCommand command = null;
string sql = "";
foreach (string key in typeDict.Keys)
sql = string.Format("Insert into CircleType(TypeName, TypeValue) values('{0}', '{1}') ", key, typeDict[key]);
command = db.GetSqlStringCommand(sql);
db.ExecuteNonQuery(command);
foreach (string key in circleDict.Keys)
CircleSubTypeInfo info = circleDict[key];
sql = string.Format("Insert into CircleSubType(SubTypeName, LinkUrl, TypeUrlValue) values('{0}', '{1}', '{2}') ", info.Name, info.LinkUrl, info.TypeUrlValue);
}
this.lblTips.Text = "擷取分類操作完成";
}
其中主要是采用了正規表達式來對擷取的内容進行處理,然後整理出來相關的分類資料放到資料庫中,以便擷取圈子使用者資訊作準備。
有了圈子分類資訊,我們第二步驟就是看如何擷取圈子資料,然後才能通過圈子的唯一ID擷取圈子的使用者資料,這步也是必須的,擷取圈子資料是比較複雜的,需要組裝較多的參數擷取資料,部分代碼如下所示。
foreach (string key in urlDict.Keys)
string keyNumberReg = "/mapCircleList/(?<d1>[1-9]\\d*)/(?<d2>[1-9]\\d*)*/(?<d3>[1-9]\\d*)/";
Regex re = new Regex(keyNumberReg, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
LogTextHelper.WriteLine(string.Format("正在處理類型:{0}", urlDict[key]));
cookie = new System.Net.CookieContainer();
string urlKey = key;
Match mc = re.Match(urlKey);
string d1 = mc.Groups["d1"].Value;
string d2 = mc.Groups["d2"].Value;
string d3 = mc.Groups["d3"].Value;
int pageSize = 30;
urlKey = urlKey.Trim('/');//清除前後的/字元
string url = "http://q.163.com/dwr/call/plaincall/CircleMainpageBean.getCircleByType2IdInMemberOrder.dwr";
//string refUrl = "http://q.163.com/mapCircleList/2/11/48/?fromCircleCircleMap";
string refUrl = string.Format("http://q.163.com/{0}/?fromCircleCircleMap", urlKey);
#region 内容正規表達式
StringBuilder circleReg = new StringBuilder();
circleReg.Append("s[0-9]\\d*.circleId=(?<circleId>[0-9]\\d*[^;])");
circleReg.Append(".*?s[0-9]\\d*.circleType1Str=\"(?<circleType1Str>.*?)\"");
circleReg.Append(".*?s[0-9]\\d*.circleType2Str=\"(?<circleType2Str>.*?)\"");
circleReg.Append(".*?s[0-9]\\d*.createDateStr=\"(?<createDateStr>.*?)\"");
circleReg.Append(".*?s[0-9]\\d*.creatorId=(?<creatorId>[0-9]\\d*[^;])");
circleReg.Append(".*?s[0-9]\\d*.creatorName=\"(?<creatorName>.*?)\"");
circleReg.Append(".*?s[0-9]\\d*.creatorSpaceUrl=\"(?<creatorSpaceUrl>.*?)\"");
circleReg.Append(".*?s[0-9]\\d*.description=\"(?<description>.*?)\"");
circleReg.Append(".*?s[0-9]\\d*.joinDeclaration=\"(?<joinDeclaration>.*?)\"");
circleReg.Append(".*?s[0-9]\\d*.linkImgUrl=\"(?<linkImgUrl>.*?)\"");
circleReg.Append(".*?s[0-9]\\d*.memberNum=(?<memberNum>[0-9]\\d*[^;])");
circleReg.Append(".*?s[0-9]\\d*.name=\"(?<name>.*?)\"");
circleReg.Append(".*?s[0-9]\\d*.urlName=\"(?<urlName>.*?)\"");
circleReg.Append( ".*?s[0-9]\\d*.visitNum=(?<visitNum>[0-9]\\d*[^;])")
通過組裝參數資料,然後擷取頁面資料,對頁面資料進行分析即可,主要代碼如下所示:
if (mc.Success)
string message = string.Format("正在處理類型{0}:{1}, 第{2}次資料, 共處理了{3}", urlDict[key], url, i + 1, j);
CallCtrlWithThreadSafety.SetText(this.lblTips, message, this);
Application.DoEvents();
Thread.Sleep(10);
MatchCollection mcs = re.Matches(content);
foreach (Match me in mcs)
{
#region MyRegion
j++;
int memberNum = 0;
try
{
memberNum = Convert.ToInt32(me.Groups["memberNum"].Value);
}
catch { }
if (memberNum < 50)
flag = false;
break;
sql = string.Format(@"insert into Circle(circleId,circleType1Str,circleType2Str,createDateStr,creatorId,
creatorName,creatorSpaceUrl,description,joinDeclaration,linkImgUrl,memberNum,name2,urlName,SubTypeName)
values('{0}','{1}','{2}','{3}','{4}','{5}','{6}','{7}','{8}','{9}','{10}','{11}','{12}','{13}') ", me.Groups["circleId"].Value,
UnicodeHelper.UnicodeToString(me.Groups["circleType1Str"].Value.Replace("'", "")), UnicodeHelper.UnicodeToString(me.Groups["circleType2Str"].Value.Replace("'", "")),
me.Groups["createDateStr"].Value, me.Groups["creatorId"].Value, UnicodeHelper.UnicodeToString(me.Groups["creatorName"].Value),
me.Groups["creatorSpaceUrl"].Value, UnicodeHelper.UnicodeToString(me.Groups["description"].Value.Replace("'", "")), UnicodeHelper.UnicodeToString(me.Groups["joinDeclaration"].Value.Replace("'", "")),
me.Groups["linkImgUrl"].Value, me.Groups["memberNum"].Value, UnicodeHelper.UnicodeToString(me.Groups["name"].Value.Replace("'", "")), me.Groups["urlName"].Value, urlDict[key]);
command = db.GetSqlStringCommand(sql);
db.ExecuteNonQuery(command);
catch (Exception ex)
LogTextHelper.WriteLine(sql);
LogTextHelper.WriteLine(ex.ToString());
message = string.Format("正在處理{0}:{1} 正在寫入資料{2}次", urlDict[key], url, j);
CallCtrlWithThreadSafety.SetText(this.lblTips, message, this);
Application.DoEvents();
Thread.Sleep(10);
#endregion
}
else
flag = false;//沒有比對就停止
break;
}
構造擷取圈子使用者資訊也是比較複雜的一個過程,需要組裝更多的參數來擷取相關的資料,部分主要實作代碼如下所示:
httpHelper = new HttpHelper();
cookie = new CookieContainer();
Regex re = null;
Match mc = null;
int pageSize = 30;
string url = "http://q.163.com/dwr/call/plaincall/CircleBean.getNewCircleUsers.dwr";
foreach (string key in circlelDict.Keys)
string circleId = key;
string urlName = circlelDict[key];
string refUrl = string.Format("http://q.163.com/{0}/members/", urlName);
circleReg.Append("s[0-9]\\d*.ageStr=\"(?<ageStr>.*?)\"");
circleReg.Append(".*?s[0-9]\\d*.city=\"(?<city>.*?[^;])\"");
circleReg.Append(".*?s[0-9]\\d*.hometownCity=(\"(?<hometownCity>.*?[^;])\"|(?<hometownCity>null))");
circleReg.Append(".*?s[0-9]\\d*.hometownProvince=(\"(?<hometownProvince>.*?)\"|(?<hometownProvince>null))");
circleReg.Append(".*?s[0-9]\\d*.name=(\"(?<name>.*?)\"|(?<name>null))");
circleReg.Append(".*?s[0-9]\\d*.nickname=(\"(?<nickname>.*?)\"|(?<nickname>null))");
circleReg.Append(".*?s[0-9]\\d*.profileImage140=\"(?<profileImage140>.*?)\"");
circleReg.Append(".*?s[0-9]\\d*.profileImage60=\"(?<profileImage60>.*?)\"");
circleReg.Append(".*?s[0-9]\\d*.province=\"(?<province>.*?)\"");
circleReg.Append(".*?s[0-9]\\d*.qq=(\"(?<qq>.*?)\"|(?<qq>null))");
circleReg.Append(".*?s[0-9]\\d*.realName=(\"(?<realName>.*?)\"|(?<realName>null))");
circleReg.Append(".*?s[0-9]\\d*.spaceName=(\"(?<spaceName>.*?)\"|(?<spaceName>null))");
circleReg.Append(".*?s[0-9]\\d*.userId=(?<userId>[0-9]\\d*[^;])");
circleReg.Append(".*?s[0-9]\\d*.userName=\"(?<userName>.*?)\"");
#endregion
bool flag = true;
int i = 0;
int j = 0;
List<CircleMemberInfo> entityList = new List<CircleMemberInfo>();
while (flag)
#region 構造送出參數
StringBuilder sb = new StringBuilder();
sb.AppendFormat("callCount=1");
sb.AppendFormat("&page=/{0}/members/", urlName);
sb.AppendFormat("&httpSessionId=");
sb.AppendFormat("&scriptSessionId=D4DAC4AD9C3BF9B71C82802BDDBA0C25369");
sb.AppendFormat("&c0-scriptName=CircleBean");
sb.AppendFormat("&c0-methodName=getNewCircleUsers");
sb.AppendFormat("&c0-id=0");//保留字元
sb.AppendFormat("&c0-param0=number:{0}", circleId);//11
sb.AppendFormat("&c0-param1=number:{0}", pageSize);//數量
sb.AppendFormat("&c0-param2=number:{0}", pageSize * i);//0,30,60
sb.AppendFormat("&c0-param3=boolean:true");
sb.AppendFormat("&batchId={0}", i);
i++;
#endregion
然後我們通過代碼來擷取頁面資料了,實作代碼如下:
string content = "";
try
httpHelper.ContentType = "text/plain";
content = httpHelper.GetHtml(url, cookie, sb.ToString(), true, refUrl);
re = new Regex(circleReg.ToString(), RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
mc = re.Match(content);
catch (Exception ex)
LogTextHelper.WriteLine(ex.ToString());
}
然後我們就開始用正規表達式來分析傳回的資料,以便顯示或者添加到資料庫中,以供他用,代碼實作如下所示:、
MatchCollection mcs = re.Matches(content);
j++;
sql = string.Format(@"insert into CircleMember(userId,userName,realName,nickname,circleId)
values('{0}','{1}','{2}','{3}','{4}') ", me.Groups["userId"].Value, httpHelper.RemoveHtml(UnicodeHelper.UnicodeToString(me.Groups["userName"].Value)),
httpHelper.RemoveHtml(UnicodeHelper.UnicodeToString(me.Groups["realName"].Value.Replace("'", ""))), httpHelper.RemoveHtml(UnicodeHelper.UnicodeToString(me.Groups["nickname"].Value.Replace("'", ""))), circleId);
message = string.Format("正在處理{0} 正在寫入資料{1}次", urlName, j);
Thread.Sleep(10);
}
該軟體除了可以采集網易部落格圈子使用者的郵件資訊,給營銷推廣人提供資料外,還可以利用網易部落格的找朋友子產品,擷取相關的使用者資料資訊,當然也是可以轉換為郵件位址資訊的了,如下所示。
以上對網易部落格的應用的代碼實作以及一個較為綜合的軟體産品介紹,希望能帶給大家更多的啟示和知識了解。