网络步入了营销的时代,营销则进入精准、细化的操作阶段,QQ用户群的分类则是很多进行精准营销的企业和个人紧盯着的一块肥肉。与普通进行大面积邮件发送营销的不同,QQ群邮件的发送成本很低,一次发送,就可以使得群里面成千上百的人都收到邮件,比普通邮件发送的模式和准确率都好很多。用手工登录QQ邮箱发送邮件是一种无奈的选择,效率并不高,如果利用软件自动识别发送群,并能批量发送群邮件,那么效率将会提高很多,不过利用QQ进行群邮件的发送,也并非一件容易的事情,本文就是探讨这块的实现,抛砖引玉,希望大家一起进行学习探讨。
由于邮箱是需要登录的,我们首先需要模拟邮箱的登录,如下图所示
QQ的群,不是你创建的群,就能发送,类似爱情买卖那首歌:“爱情不是你想卖 想买就能卖“,腾讯做法是,”群邮不是你想发,想发就能发的“,呵呵。
呵呵,言归正传,我们登录邮箱,发送群邮件的时候,就知道这样的情况了,群只有对方运行发送,并且你加入到对方的群,这样才可以发送邮件的,我们看看网页界面:
打开QQ群列表,我们看到他会列出一些可以发送邮件的群列表,如下所示:
这样,我们模拟这个操作,先把账号可以发送的群列表及相关信息获取出来,如下图所示:
获取这些信息,我们就完成了一个较为重要的步骤,剩下的就是发送群邮件的操作了。
看了上面的分析,我们来具体探讨下C#源码的实现,首先需要获取到能发送群邮件的群相关信息,具体的操作如下函数所示:
string id32Value = "" ;
private void GetCanSendMailQun()
{
// 引用地址
string refUrl = string .Format( " http://m411.mail.qq.com/cgi-bin/frame_html?sid={0}&r={1} " , Portal.gc.LoginInfo.SID, Portal.gc.LoginInfo.R);
// 可发送邮件的群列表信息
string url = string .Format( " http://m411.mail.qq.com/cgi-bin/grouplist?sid={0}&t=compose_group&gid=&s=from_mail_list " , Portal.gc.LoginInfo.SID);
httpHelper.Encoding = Encoding.GetEncoding( " gb18030 " );
string content = httpHelper.GetHtml(url, Portal.gc.EmailCookie, refUrl);
// 获取群列表并解析
string itemRegex = " " ;
Regex re = new Regex(itemRegex, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
Match mc = re.Match(content);
if (mc.Success)
{
MatchCollection mcs = re.Matches(content);
this .txtQunList.Items.Clear();
foreach (Match me in mcs)
{
string strName = me.Groups[ " name " ].Value;
string strEmail = me.Groups[ " gid " ].Value;
CListItem item = new CListItem(strName, strEmail);
this .txtQunList.Items.Add(item);
}
}
// 取得一个动态构造的Hidden参数的名称和值,该参数名和值都是采用32位MD5编码,所以比较容易拿到
itemRegex = " " ( ?< id > .{ 32 })\ " \\stype=\ " hidden\ " \\s*name=\ " ( ?< name > .{ 32 })\ " \\s*value=\ " ( ?< value > .{ 32 })\ " /> " ;
re = new Regex(itemRegex, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.IgnorePatternWhitespace);
mc = re.Match(content);
if (mc.Success)
{
id32 = mc.Groups[ " id " ].Value;
id32Value = mc.Groups[ " value " ].Value;
}
}
以上操作就是获取群列表的功能代码,剩下的我们继续跟踪如何发送群邮箱的,发送群邮件的操作和一般的POST方式不同,他是采用了一种特别的方式进行处理,也就是发送MultiPart内容方式。下面我发送了一份宣传的邮件,监测到提交的字符列表如下所示:
Content-Disposition: form-data; name="sid"
ZwcM6rV_qNv5eiRp
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="actiontype"
send
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="bigattachcnt"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="priority"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="contenttype"
html
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="savesendbox"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="sendname"
alias
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="qqshow"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="fmailid"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="cattachelist"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="fattachlist"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="rsturl"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="fileidlist"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="verifykey"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="verifycode"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="verifycode_cn"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="t"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="s"
group
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="contenttext"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="hitaddrbook"
0
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="backurl"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="newwin"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="mailtype"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="sid"
ZwcM6rV_qNv5eiRp
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="21a3f85bcf259a6e1f6f4b31e469b701"
dae08a1893ae318e4e65fb9784596ee1
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="groupname"
武汉人在北京创业群
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="qqgroupid"
[email protected]
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="subject"
给大家介绍一个营销好帮手,QQ搜通天企业版
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="votesubject"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="option"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="option"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="option"
-----------------------------7dbda2110d091a
Content-Disposition: form-data; name="content__html"
< DIV >< SPAN id =picture >< A href ="http://www.iqidi.com/Download/QQEnterprise1.png" >< IMG height =756 alt =QQ搜通天企业版 src ="http://www.iqidi.com/Download/QQEnterprise1.png" width =925 border =0 > A > SPAN >< BR > DIV >
< DIV > ★软件功能 < BR > < SPAN style ="MARGIN-LEFT: 16px; LINE-HEIGHT: 150%; MARGIN-RIGHT: 16px" > QQ搜通天企业版2010,是一款可以批量申请QQ号码、搜索采集QQ空间QQ号码信息、按地区批量采集QQ号码、QQ群号码采集、批量添加QQ群、个人QQ群成员号码、QQ校友查询、空间日志查看发表、QQ批量留言等,以及邮件群发的软件。该软件通过选择筛选条件,可以在腾讯QQ空间查询来自何地、年龄、性别、状态、职业 等条件的QQ用户号码及相关信息,并支持按地区自动批量导出QQ数据;可以根据关键字及搜索类型采集查询相关QQ群信息;批量添加群;导出群成员等功能,方便寻找共同爱好的组织和进行业务群锁定推广工作。 SPAN >< BR >< SPAN style ="MARGIN-LEFT: 16px; LINE-HEIGHT: 150%; MARGIN-RIGHT: 16px" > 该软件集成邮件群发功能,采用不用发件人信息可发送邮件的 < SPAN class =style17 >< STRONG >< FONT color =#aa0000 > 邮件直投技术 FONT > STRONG > SPAN > 和 < SPAN class =style17 >< STRONG >< FONT color =#aa0000 > 普通发送方式结合 FONT > STRONG > SPAN > ,非常方便向QQ用户发送邮件,也可以发送其他邮件地址的邮件,软件采用多线程发送邮件,有效利用您的机器资源,发送过程不影响您的其他软件操作。 SPAN >< BR >< BR > QQ搜通天企业版2010,支持查找QQ空间的QQ用户数据和日志空间信息,方便您寻找某些方面的QQ用户日志,娱乐与学习共存,休闲和交友并进。 < BR > QQ搜通天企业版2010,独有的QQ按地区自动化批量导出QQ数据,非常方便您大量获取相关条件下的QQ号码等信息,根据方便您寻找商业目标客户,迅速推广您的产品。 < BR > QQ搜通天企业版2010,支持QQ群资料搜索采集、QQ群批量添加和群成员号码采集,能够快速查找相关的QQ号码和QQ群资料,为您的业务推广提供准确的群体,也为您的兴趣找到合适的组织。 < BR > QQ搜通天企业版2010,可以快速发表、查看您自己及其他用户的日志,可以批量给QQ用户快速留言,方便您了解学习其他人的文章并作出相应的留言回复,也可以做宣传使用。 < BR > QQ搜通天企业版2010,支持查询QQ校友录相关的用户数据,非常方便您寻找您黄金岁月的校友及同桌伙伴。 < BR > QQ搜通天企业版2010,采用多线程发送邮件,有效利用您的机器资源,发送过程不影响您的其他软件操作;发送的邮件可以保存起来,可以方便的时候调出来进行发送,邮件发送过程会记录发送历史,可以设置跳过重复发送的邮件,对发送未成功的邮件,可以在再次发送。软件界面美观大方,方便使用。 < BR >
< P > 1.本软件QQ数据全部来自于“QQ空间-找朋友”、“QQ城市达人”、“QQ群”、“QQ校友录”等腾讯官方频道。 < BR > 2.本软件重要功能之一是搜索QQ用户和QQ群,不过他不同于QQ本身自带的搜索功能,这款软件可以使你获得更多信息,可以很方便的使你交到理想的朋友。 < BR > 3.本软件另外的重要功能就是可以群发邮件,可以给搜索到的QQ用户发送邮件,也可以发送给其他Email邮箱群发邮件,非常实用方便。 < BR > 4.本软件还适用于一些网络推广人群,可以直接做推广使用,通过提供采集号码服务和邮件群发功能,您可以快速方便进行网络信息的推广。 < BR >< BR > P >
< P > ★系统需求 P >
< P > QQ搜通天企业版2010 使用C# 2.0开发,是一款CS架构的软件, 适运行在 Microsoft WindowsNT/2000/XP/2003 等平台,但必须安装有.Net 2.0平台. P >
< P > 该软件利用了微软.NET Framework2.0优秀的框架,因此在安装软件前,您需要花费一点时间来安装下面的组件(请您按照顺序安装即可): P >
< P > (1)MicroSoft .NET Framework 2.0 官方下载地址: < BR >< A href ="http://www.microsoft.com/downloads/info.aspx?na=90&p=&SrcDisplayLang=zh-cn&SrcCategoryId=&SrcFamilyId=0856eacb-4362-4b0d-8edd-aab15c5e04f5&u=http://download.microsoft.com/download/5/6/7/567758a3-759e-473e-bf8f-52154438565a/dotnetfx.exe" > http://www.microsoft.com/downloads/info.aspx?na=90 & p= & SrcDisplayLang=zh-cn & SrcCategoryId= & SrcFamilyId=0856eacb-4362-4b0d-8edd-aab15c5e04f5 & u=http%3a%2f%2fdownload.microsoft.com%2fdownload%2f5%2f6%2f7%2f567758a3-759e-473e-bf8f-52154438565a%2fdotnetfx.exe A > P >
< P > (2)最后下载 QQ搜通天企业版2010,进行安装即完成整个软件的安装。安装地址为: < FONT face =宋体 > < SPAN id =lblDownUrl >< A href ="http://www.iqidi.com/Download/qqenterprise.rar" > http://www.iqidi.com/Download/qqenterprise.rar A > SPAN > FONT >< BR > P > DIV >
-----------------------------7dbda2110d091a--
发送邮件的代码如下所示:
private void btnSendQunMail_Click(object sender, EventArgs e)
CListItem item = this .txtQunList.SelectedItem as CListItem;
if (item != null )
{
string refurl = string .Format( " http://m411.mail.qq.com/cgi-bin/grouplist?sid={0}&t=compose_group&gid=&s=from_mail_list " , Portal.gc.LoginInfo.SID);
string sendUrl = string .Format( " http://m411.mail.qq.com/cgi-bin/groupmail_send?sid={0} " , Portal.gc.LoginInfo.SID);
#region 构造邮件内容
NameValueCollection col = new NameValueCollection();
col.Add( " sid " , Portal.gc.LoginInfo.SID);
col.Add( " actiontype " , " send " );
col.Add( " bigattachcnt " , "" );
col.Add( " priority " , "" );
col.Add( " contenttype " , " html " );
col.Add( " savesendbox " , "" );
col.Add( " sendname " , " alias " );
col.Add( " qqshow " , "" );
col.Add( " fmailid " , "" );
col.Add( " cattachelist " , "" );
col.Add( " fattachlist " , "" );
col.Add( " rsturl " , "" );
col.Add( " fileidlist " , "" );
col.Add( " verifykey " , "" );
col.Add( " verifycode " , "" );
col.Add( " verifycode_cn " , "" );
col.Add( " t " , "" );
col.Add( " s " , " group " );
col.Add( " contenttext " , "" );
col.Add( " hitaddrbook " , " 0 " );
col.Add( " backurl " , "" );
col.Add( " newwin " , "" );
col.Add( " mailtype " , "" );
// col.Add("sid", Portal.gc.LoginInfo.SID);
col.Add(id32, id32Value);
col.Add( " groupname " , item.Text);
col.Add( " qqgroupid " , item.Value);
col.Add( " subject " , " 给大家介绍一个营销好帮手,QQ搜通天企业版 " );
col.Add( " votesubject " , "" );
// col.Add("option", "");
// col.Add("option", "");
col.Add( " option " , "" );
col.Add( " content__html " , " ");
#endregion
string content = HttpPostData(sendUrl, col, refurl);
if (content.Contains( " 验证码错误 " ))
{
MessageUtil.ShowWarning( " 验证码错误 " );
}
}
}
其中HttpPostData关键函数就是封装发送MultiPart内容的代码,主要是组装各种键值的数据。
{
string responseContent = "" ;
var memStream = new MemoryStream();
var webRequest = (HttpWebRequest)WebRequest.Create(url);
// 边界符
var boundary = " --------------------------- " + DateTime.Now.Ticks.ToString( " x " );
// 边界符
var beginBoundary = Encoding.ASCII.GetBytes( " -- " + boundary + " \r\n " );
// 最后的结束符
var endBoundary = Encoding.ASCII.GetBytes( " -- " + boundary + " --\r\n " );
// 设置属性
webRequest.Method = " POST " ;
webRequest.Accept = " image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */* " ;
webRequest.Referer = referer;
webRequest.ContentType = " multipart/form-data; boundary= " + boundary;
webRequest.UserAgent = " Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.2; SV1; Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) ; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; .NET4.0C; .NET4.0E; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729; InfoPath.2) " ;
// webRequest.Timeout = timeOut;
// 写入字符串的Key
var stringKeyHeader = " \r\n-- " + boundary +
" \r\nContent-Disposition: form-data; name=\ " { 0 }\ "" +
" \r\n\r\n{1}\r\n " ;
try
{
foreach ( string key in stringDict.Keys)
{
string formitem = string .Format(stringKeyHeader, key, stringDict[key]);
byte [] formitembytes = Encoding.GetEncoding( " gb18030 " ).GetBytes(formitem);
memStream.Write(formitembytes, 0 , formitembytes.Length);
}
// 写入最后的结束边界符
memStream.Write(endBoundary, 0 , endBoundary.Length);
webRequest.ContentLength = memStream.Length;
webRequest.CookieContainer = Portal.gc.EmailCookie;
var requestStream = webRequest.GetRequestStream();
memStream.Position = 0 ;
var tempBuffer = new byte [memStream.Length];
memStream.Read(tempBuffer, 0 , tempBuffer.Length);
memStream.Close();
requestStream.Write(tempBuffer, 0 , tempBuffer.Length);
requestStream.Close();
var httpWebResponse = (HttpWebResponse)webRequest.GetResponse();
using (var httpStreamReader = new StreamReader(httpWebResponse.GetResponseStream(),
Encoding.GetEncoding( " gb18030 " )))
{
responseContent = httpStreamReader.ReadToEnd();
}
// fileStream.Close();
httpWebResponse.Close();
webRequest.Abort();
}
catch (Exception ex)
{
LogHelper.Error(ex);
}
return responseContent;
}
完成上面的步骤,发送邮件就差不多了,检测到的内容和在QQ邮件网站发送的内容差不多,就是Cookie的内容有一些差异,不过不知道是什么原因,一直发送不成功,返回的HTML内容提示CGIExeption,可能是哪里出了问题吧,需要继续探讨。
希望各位提供思路,进一步解决这个问题,下面是具体的说明信息:
document.domain = " qq.com " ;
function getTop()
{
var _oSelfFunc = arguments.callee;
if ( ! _oSelfFunc._moTop)
{ try {_oSelfFunc._moTop = window != parent ? (parent.getTop ? parent.getTop():parent.parent.getTop()):window;} catch (_oError){_oSelfFunc._moTop = window;}}
return _oSelfFunc._moTop;
}
try {window.top = getTop();} catch (e){eval( " var top=getTop(); " );}
var gsTest = "
,-1 " ;
var g_uin = " 1584619360 " ;
window == getTop() && document.write( ' ' + ' script> ' );
(getTop().initPageEvent || function (){})(window);
script >< script src ="http://rescdn.qqmail.com/zh_CN/htmledition/js/all02cc76.js" type ="text/javascript" > script >< title > QQ邮箱 title >< style > style >< script >
var bIsDiskPanelFrame = "" == " pannel " ;
( function () {
window.fsuccesss = "" ;
if (getTop().initPageEvent)
{
getTop().initPageEvent(window);
}
window.isMainFrameError = ! getTop().getMainWin ||
getTop().getMainWin() == window || ( getTop() == window && ! getTop().getMainWin );
if ( ! isMainFrameError )
{
return ;
}
document.write( [
' ' ,
' '
].join( "" ) );
window.onerror = function (msg, url, line) { return true ;};
window.onload = function ()
{
setTimeout( function () {
document.body.backgroundColor = " #fff " ;
var oMsgText = getTop().S( " msg_txt " ,window);
var _sMsg = getTop().trim(oMsgText.innerHTML);
var bHasHistory = history.length > 0 && getTop() != window;
var sCode = "" ;
if (bIsDiskPanelFrame) // QQ diskpanel
{
sCode = [ '
}
else if (_sMsg)
{
sCode = [
'
' ' ,
'
bHasHistory ? '
'
'
'
];
}
oMsgText.innerHTML = sCode.join( "" );
oMsgText.style.display = " block " ;
}, 20 );
};
} )();
function SafeModeSwitchConfirm()
{
if (getTop().S( " divSafeModeOn " ))
{
return ;
}
if (confirm( " QQ邮箱正在进行系统维护,您必须进入只读模式才能继续使用邮箱。\n请注意:如果您目前正在写邮件或写记事,直接进入只读模式将可能导致数据丢失,请[取消]并手工保存您的数据 " ))
{
getTop().goUrlTopWin(getTop().location.href, true );
}
}
script > head >< body class ="tipbg" style ="text-align:center;background:white;" >< div id ="msg_txt" style ="display:none;" code ="-141" > 验证码错误 < script > var bVerifycode = true ;getTop().verifyCode( " cncodewrong " ); script > div > body >< script language ="Javascript" >
( function () {
var _oMainWin = getTop().getMainWin(),
_sMainWinLoc = _oMainWin.location.href,
_sActionWinLoc = getTop().getActionWin().location.href,
_msgTxtObj = getTop().S( " msg_txt " , window),
_bIsShowVerifyFrame = getTop().QMDialog( " QMVerify " ) || typeof (bVerifycode) != " undefined " ;
if ( ! _msgTxtObj || getTop().trim && getTop().trim(_msgTxtObj.innerText || _msgTxtObj.textContent) == " [发送错误报告] " )
{
return ;
}
_msgTxtObj.innerHTML = getTop().filteScript( _msgTxtObj.innerHTML );
if (window[ " misslist " ])
{
var infos = _oMainWin.QMAttach.getInfoUid(misslist);
getTop().confirmBox( {
msg : getTop().TE([ ' 以下%len%个附件绑定失败,请重试:
' ,
' %@%for(%list%)%@% ' ,
' ' ,
' %@%eval subAsiiStr(%name%,%_root_.width%,"...",1)%@% ' ,
' ; ' ,
' %@%if(%_root_.len%<5)%@%
%@%endif%@% ' ,
' %@%endfor%@% ' ], ' % ' ).replace( {
len : infos.length,
width : infos.length < 5 ? 40 : 15 ,
list : infos
} ),
title : " 失败信息 " ,
confirmBtnTxt : " 重试 " ,
cancelBtnTxt : " 取消 " ,
onreturn : function (_abIsOk) {
if (_abIsOk)
{
_oMainWin.fireMouseEvent(_oMainWin.SN( " sendbtn " )[ 0 ], " click " );
}
else
{
E(infos, function (_aoInfo) {
_oMainWin.delAttach(_aoInfo.id);
});
}
}
} );
}
else if ( ! _bIsShowVerifyFrame
&& ! isMainFrameError
&& (_sMainWinLoc.indexOf( " t=compose " ) != - 1 ) // || _sMainWinLoc.indexOf("t=mail_list_groupsms")>-1)
&& _sActionWinLoc.indexOf( " customstationery " ) == - 1
&& "
" != " autosave " && "
" != " sms_notify " )
{
getTop().msgBox((_msgTxtObj && _msgTxtObj.innerHTML) || " 系统错误 " , " dialog " , true , 0 , " 失败信息 " , window );
}
else if ( ! isMainFrameError ) {
if ( ! bIsDiskPanelFrame || window.name != " keepSession " )
{
getTop().msgBox( null , fsuccesss, true , 5000 , null , window);
}
}
try {
getTop().errorProcess();
}
catch ( e ){}
} )();
script > html >