--------------------------------------------------------------------------------
第一步:邮箱批量采集器的制作。
邮箱批量采集,要选择好采集的页面,我在这里就选择163邮箱吧。因为这种页面采集比较中规中矩。
看出什么来了吗?对,这个页面全部是163会员的信息。其中这个页面的html文本里面还有我们要找的邮箱资料。
红框里面的就是我们要找的邮箱地址的“base64字符串格式”,只要提取出来后,将其变化为普通的文本就可以了,这在.net里很好解决。
下面的要做事就是用http嗅探器抓取参数资料了。这一步就直接省略...
接下来就直接贴上代码了。
View Code
5 //
6 using System;
7 using System.Collections.Generic;
8 using System.Linq;
9 using System.Text;
10 using System.Web;
11 using System.Text.RegularExpressions;
12 using System.Net.Mail;
13 using System.Net;
14 namespace System
15 { //邮箱提取控制类
16 public class MailUtil
17 {
18 public int Index { get; set; }
19 public int Type { get; set; }
20 public string Gender { get; set; }
21 public string Online { get; set; }
22 public string Province { get; set; }
23 public string City { get; set; }
24 public bool HasNext { get; set; }
25 #region 构造函数
26 public MailUtil()
27 {
28 this.Init(1, 5, "", "", "", "");
29 }
30 public MailUtil(int index)
31 {
32 this.Init(index, 5, "", "", "", "");
33 }
34 public MailUtil(int index, int type)
35 {
36 this.Init(index, type, "", "", "", "");
37 }
38 public MailUtil(int index, int type, string gender)
39 {
40 this.Init(index, type, gender, "", "", "");
41 }
42 public MailUtil(int index, int type, string gender, string online)
43 {
44 this.Init(index, type, gender, online, "", "");
45 }
46 public MailUtil(int index, int type, string gender, string online, string province)
47 {
48 this.Init(index, type, gender, online, province, "");
49 }
50 public MailUtil(int index, int type, string gender, string online, string province, string city)
51 {
52 this.Init(index, type, gender, online, province, city);
53 }
54 #endregion
55 private void Init(int index, int type, string gender,string online, string province, string city)
56 {
57 index = 1;
58 this.Index = index;
59 this.Type = type;
60 this.Gender = gender;
61 this.Online = online;
62 this.Province = province;
63 this.City = city;
64 this.HasNext = true;
65 this.Province = HttpUtility.UrlEncode(Encoding.GetEncoding("utf-8").GetBytes(this.Province));
66 this.City = HttpUtility.UrlEncode(Encoding.GetEncoding("utf-8").GetBytes(this.City));
67
68 }
69 private string VisitSite()
70 {
71 string url = "http://blog.163.com/findFriend.do";
72 StringBuilder sb = new StringBuilder();
73 sb.AppendFormat("{0}?index={1}&type={2}",url,this.Index,this.Type);
74 if(this.Gender!="") sb.AppendFormat("&{0}",this.Gender);
75 if(this.Province!="")sb.AppendFormat("&{0}",this.Province);
76 if(this.City!="")sb.AppendFormat("&{0}",this.City);
77 if(this.Online!="")sb.AppendFormat("&{0}",this.Online);
78
79 string html = UUHttpHelper.GetHtml(sb.ToString(), "gbk");
80 return html;
81 }
82 public Dictionary<string,LinkMan> Extract()
83 {
84 LinkMan linkman =null;
85
86 Dictionary<string, LinkMan> linkmans = new Dictionary<string, LinkMan>();
87 string html = VisitSite();
88 this.HasNext = html.Contains("下一页") ;
89 if (!this.HasNext) return linkmans;
90 MatchCollection matches = new Regex(@"openurl\('(?'base64'[^']+)','profile'\)""\s*class=""nick""\>(?'nick'[^<>]*)\s*\</a\>", RegexOptions.IgnoreCase | RegexOptions.IgnorePatternWhitespace).Matches(html);
91 foreach (Match match in matches)
92 {
93 linkman = new LinkMan();
94 linkman.Base64String = match.Groups["base64"].Value;
95 string unicode=Encoding.GetEncoding("Gbk").GetString(Convert.FromBase64String(linkman.Base64String));
96 linkman.Email = unicode.Contains("@126")?(string.Format("{0}.com",unicode)):(string.Format("{0}@163.com",unicode));
97 linkman.Nick = match.Groups["nick"].Value;
98 linkmans.Add(linkman.Base64String, linkman);
99 }
100 matches = new Regex(@"<a\s*[^>]*>(?'area'[^<]*)</a>\s*</div>\s*</div>\s*<div class=""nav"">\s*<a\s*href=""javascript:void\(0\);""\s*(οnclick=""gFindFriend.openurl\s*\('(?'base64'[^']+)','blog'\)"")*>", RegexOptions.IgnoreCase).Matches(html);
101 foreach (Match match in matches)
102 {
103 linkmans[match.Groups["base64"].Value].Area = match.Groups["area"].Value;
104 }
105 this.Index++;
106 return linkmans;
107
108 }
109
110 }
111 /// <summary>
112 /// 联系人信息
113 /// </summary>
114 public class LinkMan
115 {
116 /// <summary>
117 /// 昵称
118 /// </summary>
119 public string Nick { get; set; }
120 /// <summary>
121 /// 邮箱地址前缀的base64字符串
122 /// </summary>
123 public string Base64String { get; set; }
124 /// <summary>
125 /// 邮箱地址
126 /// </summary>
127 public string Email { get; set; }
128 /// <summary>
129 /// 性别
130 /// </summary>
131 public string Sex { get; set; }
132 /// <summary>
133 /// 地区
134 /// </summary>
135 public string Area { get; set; }
136 /// <summary>
137 /// 省份
138 /// </summary>
139 public string Province { get; set; }
140 /// <summary>
141 /// 城市
142 /// </summary>
143 public string City { get; set; }
144 /// <summary>
145 /// 年龄
146 /// </summary>
147 public int Age { get; set; }
148 }
149 public class TestClass
150 {
151 public static bool Paused = false;
152 static MailUtil mailUtil = new MailUtil();
153 static Dictionary<string, LinkMan> linkmans = new Dictionary<string, LinkMan>();
154 public static Dictionary<string, LinkMan> TestMethod(System.Windows.Forms.ListView listView)
155 {
156
157 System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate
158 {
159 while (mailUtil.HasNext)
160 {
161
162 Dictionary<string, LinkMan> list = mailUtil.Extract();
163 foreach (KeyValuePair<string, LinkMan> kvp in list)
164 {
165 if (!linkmans.ContainsKey(kvp.Key))
166 {
167 linkmans.Add(kvp.Key, kvp.Value);
168 System.Windows.Forms.ListViewItem viewItem = listView.Items.Add(linkmans.Count.ToString());
169 viewItem.SubItems.Add(kvp.Value.Nick);
170 viewItem.SubItems.Add(kvp.Value.Email);
171 viewItem.SubItems.Add(kvp.Value.Area);
172 }
173 }
174 if (Paused)
175 break;
176 }
177 if (linkmans.Count == 0) throw new Exception("没有找到任何数据");
178
179 }));
180 thread.Start();
181 return linkmans;
182 }
183 }
184
185 }
这个类调用起来相当简单,直接new一个实例之后,调用Extract()就可以了。但是这样的话就只能获取所有结果的第一页了,后面还有很多的页面就无法抓去了,因此要将里面一个url地址的index参数加1,并且还要判断返回的html页里面是不是含有“下一页”,没有的话就不用继续再抓了
所以我在后面结尾处又加上了一个测试类,供大家直接调用,省去了很多麻烦。因为是静态类,所直接这样调用就可以了。
TestClass.TestMethod(listView1);//这个中间的listView1是你实际上添加到你代码中的控件名称,当然,//前提是这个控件已经添加好了相对应的colume
因此,必须在对多线程进行人为干预和控制,所以对上篇文章中提到的测试类和方法进行了改进,增加了一个开关逻辑变量Paused,判断这个变量以决定线程是否继续,当然您可能有比这更好的方法,我这里只是抛砖引玉罢了。
1public class TestClass
2 {
3 public static bool Paused = false;
4 static MailUtil mailUtil = new MailUtil();
5 static Dictionary<string, LinkMan> linkmans = new Dictionary<string, LinkMan>();
6 public static Dictionary<string, LinkMan> TestMethod(System.Windows.Forms.ListView listView)
7 {
8
9 System.Threading.Thread thread = new System.Threading.Thread(new System.Threading.ThreadStart(delegate
10 {
11 while (mailUtil.HasNext)
12 {
13
14 Dictionary<string, LinkMan> list = mailUtil.Extract();
15 foreach (KeyValuePair<string, LinkMan> kvp in list)
16 {
17 if (!linkmans.ContainsKey(kvp.Key))
18 {
19 linkmans.Add(kvp.Key, kvp.Value);
20 System.Windows.Forms.ListViewItem viewItem = listView.Items.Add(linkmans.Count.ToString());
21 viewItem.SubItems.Add(kvp.Value.Nick);
22 viewItem.SubItems.Add(kvp.Value.Email);
23 viewItem.SubItems.Add(kvp.Value.Area);
24 }
25 }
26 if (Paused)
27 break;
28 }
29 if (linkmans.Count == 0) throw new Exception("没有找到任何数据");
30
31 }));
32 thread.Start();
33 return linkmans;
34 }
35 }
接下来是改一下调用的方式,这里面的button是你添加点击的按钮。
1private void button1_Click(object sender, EventArgs e)
2 {
3 if (this.button1.Text == "开始提取")
4 {
5 this.button1.Text = "中断";
6 TestClass.Paused = false;
7 links = TestClass.TestMethod(listView1);
8 }
9 else
10 {
11 this.button1.Text = "开始提取";
12 TestClass.Paused = true;
13 }
14
15
16 }
保存数据通常我们用数据库,本地数据库有access,当然我们这里不用数据库用到xml技术就可以了。
xml的操作其实很简单,这里为了我们以后调用方便,我还是把xml进行了一个简单的封装处理。
1 using System;
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Xml;
5 using System.Runtime.CompilerServices;
10 namespace System
11 {
12 public class U_Xml
13 {
14 public string FileName { get; set; }
15 public XmlDocument XmlDoc { get; set; }
16 public XmlElement RootElement { get; set; }
17 public U_Xml()
18 {
19 }
20 public U_Xml(string fileName ,string rootElement)
21 {
22 this.FileName = fileName;
23 this.XmlDoc = new XmlDocument();//创建xml文档
24 XmlDeclaration xmlDeclaration = this.XmlDoc.CreateXmlDeclaration("1.0", "utf-8", null);
25 this.XmlDoc.AppendChild(xmlDeclaration);//添加申明
26 this.RootElement =this.XmlDoc.CreateElement(rootElement);//创建根元素
27 this.XmlDoc.AppendChild(this.RootElement); //添加根元素
28
29 }
30
31 public static XmlDocument LoadFromFile(string fileName)
32 {
33 XmlDocument xmlDoc = new XmlDocument();
34 xmlDoc.Load(fileName);//加载已经存在的文件
35 return xmlDoc;
36 }
37 public void Save(string fileName)
38 {
39 if (!string.IsNullOrEmpty(fileName))
40 this.FileName = fileName;
41 if (this.XmlDoc == null) throw new Exception("没有创建xml文档,无法保存");
42 this.XmlDoc.Save(this.FileName);
43 }
44 public void Save()
45 {
46 Save("");
47 }
48 }
49 /// <summary>
50 /// 这个类,用到扩展方法,用起来相当方便,有点像jquery的链式编程
51 /// </summary>
52 public static class Methods
53 {
54
55 /// <summary>
56 /// 添加节点
57 /// </summary>
58 /// <param name="arr">
59 /// 第一个参数:xml实例化类
60 /// 第二个参数:节点名字
61 /// <returns></returns>
62 public static XmlNode AddNode(this XmlElement _xmlElement, params object[] arr)
63 {
64
65 U_Xml u_Xml=(U_Xml)arr[0];
66 XmlNode xmlNode =u_Xml.XmlDoc.CreateNode(XmlNodeType.Element,(string)arr[1],"");
67 _xmlElement .AppendChild(xmlNode);//添加节点
68 return xmlNode;
69
70 }
71 /// <summary>
72 /// 添加节点
73 /// </summary>
74 /// <param name="arr">
75 /// 第一个参数:xml实例化类\n
76 /// 第二个参数:节点名字
77 /// </param>
78 /// <returns></returns>
79 public static XmlNode AddNode(this XmlNode _xmlElement, params object[] arr)
80 {
81 U_Xml u_Xml = (U_Xml)arr[0];
82 XmlNode xmlNode = u_Xml.XmlDoc.CreateNode(XmlNodeType.Element, (string)arr[1],"");
83 _xmlElement.AppendChild(xmlNode);//添加节点
84 return xmlNode;
85 }
86 /// <summary>
87 /// 添加属性
88 /// </summary>
89 /// <param name="arr">
90 /// 第一个参数:xml实例化类\n
91 /// 第二个参数:属性名称\n
92 /// 第三个参数:属性值
93 /// </param>
94 /// <returns></returns>
95 public static XmlAttribute AddAttibute(this XmlElement _xmlElement, params object[] arr)
96 {
97 U_Xml u_Xml = (U_Xml)arr[0];
98 XmlAttribute xmlAttribute = u_Xml.XmlDoc.CreateAttribute((string)(arr[1]));
99 xmlAttribute.Value = (string)arr[2];
100 _xmlElement.Attributes.Append(xmlAttribute);
101 return xmlAttribute;
102 }
103 /// <summary>
104 /// 添加属性
105 /// </summary>
106 /// <param name="arr">
107 /// 第一个参数:xml实例化类\n
108 /// 第二个参数:属性名称\n
109 /// 第三个参数:属性值
110 /// </param>
111 /// <returns></returns>
112 public static XmlAttribute AddAttibute(this XmlNode _xmlElement, params object[] arr)
113 {
114 U_Xml u_Xml = (U_Xml)arr[0];
115 XmlAttribute xmlAttribute = u_Xml.XmlDoc.CreateAttribute((string)(arr[1]));
116 xmlAttribute.Value = (string)arr[2];
117 _xmlElement.Attributes.Append(xmlAttribute);
118 return xmlAttribute;
119 }
120 public static XmlNode ReName(this XmlElement _xmlElement, params object[] arr)
121 {
122 string value = (string)arr[2];
123
124 U_Xml u_Xml=(U_Xml)arr[0];
125 XmlNode xmlElement = u_Xml.XmlDoc.CreateNode("element",value,"");
126 if (u_Xml.XmlDoc[(string)arr[1]] == null) throw new Exception("不存在该节点");
127 xmlElement.InnerText = u_Xml.XmlDoc[(string)arr[1]].InnerText;
128 u_Xml.XmlDoc[(string)arr[1]].ParentNode.ReplaceChild(xmlElement,u_Xml.XmlDoc[(string)arr[1]]);
129 u_Xml.Save();
130 return xmlElement;
131 }
132 public static XmlNode ReName(this XmlNode _xmlElement, params object[] arr)
133 {
134 string value = (string)arr[2];
135
136 U_Xml u_Xml = (U_Xml)arr[0];
137 XmlNode xmlElement = u_Xml.XmlDoc.CreateNode("element", value, "");
138 xmlElement.InnerText = u_Xml.XmlDoc[(string)arr[1]].InnerText;
139 u_Xml.XmlDoc[(string)arr[1]].ParentNode.ReplaceChild(xmlElement, u_Xml.XmlDoc[(string)arr[1]]);
140 u_Xml.Save();
141 return xmlElement;
142 }
143 public static void Delete(this XmlNode _xmlElement)
144 {
145 if (_xmlElement == null) throw new Exception("该节点不存在");
146 if(_xmlElement.ParentNode.HasChildNodes) _xmlElement.ParentNode.RemoveChild(_xmlElement);
147 }
148 }
149
150 }
1 private void button2_Click(object sender, EventArgs e)
2 {
3 U_Xml u_Xml = new U_Xml(Environment.CurrentDirectory + "\\Config\\MailInfo.xml", "MailSet");
4 foreach (KeyValuePair<string, LinkMan> kvp in links)
5 {
6 XmlNode xmlNode= u_Xml.RootElement.AddNode(u_Xml, "Email");
7 xmlNode.AddAttibute(u_Xml, "Nick", kvp.Value.Nick);
8 xmlNode.AddAttibute(u_Xml, "Area", kvp.Value.Area);
9 xmlNode.InnerText = kvp.Value.Email;
10 }
11 u_Xml.Save();
12
13 }
首先,我们还是先介绍一下通过程序发送邮件的几种方式。
1.Smtp方式.
这种方式比较傻瓜化,在C#里调用起来也相对简单(其他语言像java也有类似的Pocket(包)可以直接调用,相当简单)。通常在.net里,存在两个线程的类可以使用:
一个是System.Net.Mail,另一个就是Sytem.Web.Mail,当然还有没有其他类我也没去细究过了。然而后面那个类现在VS里已经提示说过时了,那就用前面的那个类吧。
调用方法如下,先添加对System.Net.Mail的引用。代码如下:
1 /// <summary>
4 /// </summary>
5 public class MailSender
6 {
7 public SmtpClient Client { get; set; }
8 public MailMessage Msg { get; set; }
9 private string[] To { get; set; }
10 private string SmtpServer { get; set; }
11 private int Port { get; set; }
12 public MailSender()
13 {
14
15 }
16
17 public MailSender(string from, string smtpServer, string username, string password)
18 {
19 this.Init(from, smtpServer,username,password);
20 }
21 private void Init(string from, string smtpServer,string username,string password)
22 {
23 this.Msg = new MailMessage();
24 this.Client = new SmtpClient(smtpServer);
25 this.SmtpServer = smtpServer;//发送邮箱的Smtp服务器地址
26 this.Client.UseDefaultCredentials = true;
27 this.Client.Credentials = new NetworkCredential(username, password);//这里是必须填写的邮箱登录用户名和密码
28 this.Msg.From = new MailAddress(from);//设置邮件发送地址
29 this.Msg.IsBodyHtml = true;//这里最好设置为html格式的邮件正文
30 this.Msg.BodyEncoding = Encoding.UTF8;//邮件正文编码
31 this.Msg.SubjectEncoding = Encoding.UTF8;
32 this.Msg.DeliveryNotificationOptions = DeliveryNotificationOptions.OnFailure;//当邮件发送失败的时候通知一下
33
34 }
35 public void Send(string subject,string body,string[] attachmentPaths)
36 {
37
38
39 for (int i = 0; i < attachmentPaths.Length; i++)
40 {
41 this.Msg.Attachments.Add(new Attachment(attachmentPaths[i]));
42 }
43 this.Msg.Subject = subject;
44 this.Msg.Body = body;
45 this.Client.Send(this.Msg);
46
47 }
48 }
这个发送邮件的封装类没有起到封装的作用,还等待各位去补充完善,代码很简单,调用的话,请接着看下面的代码
2 /// copyright:No
5 /// </summary>
6 /// <param name="sender"></param>
7 /// <param name="e"></param>
8 private void button3_Click(object sender, EventArgs e)
9 {
10 try
11 {
12 MailSender mailSender = new MailSender("wxp146@qq.com", "smtp.qq.com", "wxp146", "******");//Rember that it's your password
13 links = new Dictionary<string, LinkMan>();
14 for (int i = 0; i < listView1.CheckedItems.Count; i++)
15 {
16 ListViewItem listViewItem = listView1.Items[listView1.CheckedItems[i].Index];
17 LinkMan linkMan = new LinkMan();
18 string id = listViewItem.Text;
19 string nick = listViewItem.SubItems[1].Text;
20 string email = listViewItem.SubItems[2].Text;
21 string area = listViewItem.SubItems[3].Text;
22 linkMan.Nick = nick;
23 linkMan.Email = email;
24 linkMan.Area = area;
25 mailSender.Msg.To.Add(linkMan.Email);
26
27
28 links.Add(linkMan.Email, linkMan);
29
30 }
31
32 /*
33 * mailSender.Msg.Subject = "这是一封测试信,可以直接放垃圾箱!";
34 mailSender.Msg.Body = "你好:测定试一下,打扰之处请见谅!<a href=\"HTTP://www.uu102.com\">曙光营销技术论坛</a>";
35 mailSender.Client.Send(mailSender.Msg);
36 *
37 */
38 mailSender.Send("这是一封测试信,可以直接放垃圾箱!",
39 "你好:测定试一下,打扰之处请见谅!<a href=\"HTTP://www.uu102.com\">曙光营销技术论坛</a>",
40 new string[] { Environment.CurrentDirectory + "\\Config\\MailInfo.xml" });
41 MessageBox.Show("邮件发送成功");
42 }
43 catch (WebException e1)
44 {
45 MessageBox.Show(string.Format("邮件发送失败,请检查设置{0}",e1.Message));
46 }
47 }
至于上面的代码突然出现的button3是一个点击发送的按钮。以上就是我们要介绍的第一种邮件发送方式
2.模拟登陆邮箱管理页面,然后模拟发送。
典型的做法有登录QQ邮箱然后发帖的,网上很多人都在研究这种方式。当然,由于时间和精力的关系,我就没直接贴出代码了,有兴趣的朋友可以联系本人,一起交流探讨一下。
3.利用IIS的Smtp邮箱服务发送
这种方法大概是最麻烦的了,要求在自己的机器上装这装那,还不一定会成功。当然,如果你有个人的网站服务器的话,这个也不失为一个办法。