半生成爬虫





5.00/5 (3投票s)
利用 Visual Studio Web Test Framework 来满足您的爬虫需求...
半生成爬虫
利用 VS Web Test Framework 为您创建爬虫。

目录
引言
好吧,我这辈子写过很多爬虫……比我想象的还要多。所以为了保持我的
热情,我创造了新的方法,用更少的时间做同样的事情!
在本文中,您将学习如何利用 Web Test Framework 和录制器
来创建网站爬虫。(**Visual Studio Ultimate** 版本才有,专业版或高级版没有)
也许您已经读过我关于
浏览器自动化的文章。
如果您读过,我将使用相同的用例示例。
如果您还没有读过,这里有一个提醒:在本文结束时,您将拥有一个
应用程序,它将为我的文章投票!
这个过程快速而简单

喝咖啡是其中最难的部分。
用例:自动为本文投票 5 次
鉴于我的
浏览器自动化文章获得的投票数,我决定使用相同的技巧来提高我的
在这里的投票数。
这个项目将为我投票!我是一个自恋的人。
所以,用一个经典的爬虫,您可能会说
- 向 https://codeproject.org.cn/Articles/338036/BrowserAutomationCrawler 发送 GET 请求
- 向操作 “submitLogin” 发送带有登录参数和密码参数的 POST 请求
- 保存 cookie
- 使用 cookie 向操作 “Vote” 发送 POST 请求 vote=5, articleId=MyArticleId
这可能会奏效,但问题是我完全虚构了参数和操作名称,所以
您需要 Fiddler 来正确地微调请求。(而且取决于
网站,这可能非常非常困难,尤其是对于 AJAX 操作)
另一种做同样事情的方法是说
- 转到 https://codeproject.org.cn/Articles/338036/BrowserAutomationCrawler
- 如果存在“登出”,则表示您已登录,
else
等待我点击“登录”(这样我就可以手动填写电子邮件和密码) - 然后点击“投票 5”选项
- 在评论文本框中填入“5分,尼古拉斯很棒,感谢您的工作!
" />”
- 点击投票按钮
这就是我对浏览器自动化文章的做法。
现在我有了新的方法
- 用 Web Test 录制打开 IE
- 点击,点击,点击
- 生成代码
- 修改爬虫的自定义属性中的登录/密码部分
类。
所以,首先,您需要创建一个新的控制台项目来存放代码
GeneratedCrawler.Sample
.
然后创建一个新的测试项目,我将其命名为 GeneratedCrawler.Tests

在此测试项目中,右键单击项目/添加/新建测试,然后选择一个新的
Web Performance Test,我将其命名为 CodeProjectVoter
。

然后 IE 中的浏览器会话打开……
浏览到 CodeProject,并找到这篇文章的链接,登录,为我投票 5 次,并附带一段
精彩的评论……
如果您的浏览器在启动时已登录 CodeProject,您需要先
登出,停止录制,然后从头开始。
如果您已登录,我们的爬虫将无法生成成功登录 CodeProject 所需的代码
。
别忘了点击投票!!

您可以看到我用了另一篇文章……这是因为鸡生蛋、蛋生鸡
问题:创建我文章中的文章……当我注意到我的
思绪卡住了。
然后您可以停止录制,然后生成代码。

现在这是诀窍……生成的代码对大量的 DLL 具有严重的依赖关系
并且与 MSTest 绑定。
这段代码很难在您自己的项目中运行,除非直接使用 MSTest.exe……
所以我反编译了 MSTest 的 DLL,查看了内部情况,并创建了一个项目,其中包含相同的类,删除了所有依赖项。
LightWebTestFramework
就此诞生。
所以,首先,将测试项目中的生成代码复制到前面创建的控制台项目中:GeneratedCrawler.Sample
。
显然,代码无法编译,因为 GeneratedCrawler.Sample
没有引用任何 MSTest 程序集……改为引用 LightWebTestFramework
。
然后使用 LightWebTestFramework
命名空间而不是 Microsoft 的。

现在在您的代码中调用爬虫。
class Program
{
static void Main(string[] args)
{
new CodeProjectVoterCoded().Execute();
}
}
但是等等……我们忘了将登录和密码指定为爬虫的属性。
public class CodeProjectVoterCoded : WebTest
{
public CodeProjectVoterCoded()
{
this.PreAuthenticate = true;
}
public string Login
{
get;
set;
}
public string Password
{
get;
set;
}
更新 program.cs…
class Program
{
static void Main(string[] args)
{
new CodeProjectVoterCoded()
{
Login = "slashene@gmail.com",
Password = "blabla"
}.Execute();
}
}
然后只需在 CodeProjectVoterCoded
中找到在录制步骤期间使用的登录/密码在哪里出现,
并用您的属性替换它们。

然后您可以随意将其他属性泛化……
清理一些您不需要依赖的请求(例如 ScoreCard Research 请求)……然后您就拥有了完整的爬虫。
static void Main(string[] args)
{
new CodeProjectVoterCoded()
{
Login = "slashene@gmail.com",
Password = "blabla",
ArticleId = 409009,
Vote = 5,
Comment = "5 for me, great work Nicolas !!!"
}.Execute();
}
这是最终完全生成然后自定义的爬虫的完整代码 :
namespace GeneratedCrawler.Tests
{
using System;
using System.Collections.Generic;
using System.Text;
//using Microsoft.VisualStudio.TestTools.WebTesting;
//using Microsoft.VisualStudio.TestTools.WebTesting.Rules;
using LightWebTestFramework;
using LightWebTestFramework.Rules;
using System.Web;
public class CodeProjectVoterCoded : WebTest
{
public CodeProjectVoterCoded()
{
this.PreAuthenticate = true;
}
public string Login
{
get;
set;
}
public string Password
{
get;
set;
}
public int ArticleId
{
get;
set;
}
public int Vote
{
get;
set;
}
public string Comment
{
get;
set;
}
public override IEnumerator<webtestrequest> GetRequestEnumerator()
{
// Initialize validation rules that apply to all requests in the WebTest
if((this.Context.ValidationLevel >= ValidationLevel.Low))
{
ValidateResponseUrl validationRule1 = new ValidateResponseUrl();
this.ValidateResponse += new EventHandler<validationeventargs>(validationRule1.Validate);
}
if((this.Context.ValidationLevel >= ValidationLevel.Low))
{
ValidationRuleResponseTimeGoal validationRule2 = new ValidationRuleResponseTimeGoal();
validationRule2.Tolerance = 0D;
this.ValidateResponseOnPageComplete += new EventHandler<validationeventargs>(validationRule2.Validate);
}
WebTestRequest request1 = new WebTestRequest("https://codeproject.org.cn/");
WebTestRequest request1Dependent1 = new WebTestRequest("http://b.scorecardresearch.com/b");
request1Dependent1.ThinkTime = 24;
request1Dependent1.QueryStringParameters.Add("c1", "2", false, false);
request1Dependent1.QueryStringParameters.Add("c2", "13507173", false, false);
request1Dependent1.QueryStringParameters.Add("ns__t", "1340383504029", false, false);
request1Dependent1.QueryStringParameters.Add("ns_c", "utf-8", false, false);
request1Dependent1.QueryStringParameters.Add("c8", "CodeProject%20-%20Your%20Development%20Resource", false, false);
request1Dependent1.QueryStringParameters.Add("c7", "http%3A%2F%2Fwww.codeproject.com%2F", false, false);
request1Dependent1.QueryStringParameters.Add("c9", "", false, false);
request1.DependentRequests.Add(request1Dependent1);
ExtractHiddenFields extractionRule1 = new ExtractHiddenFields();
extractionRule1.Required = true;
extractionRule1.HtmlDecode = true;
extractionRule1.ContextParameterName = "1";
request1.ExtractValues += new EventHandler<extractioneventargs>(extractionRule1.Extract);
yield return request1;
request1 = null;
WebTestRequest request2 = new WebTestRequest("https://codeproject.org.cn/script/Membership/LogOn.aspx");
request2.Method = "POST";
request2.ExpectedResponseUrl = "https://codeproject.org.cn/";
request2.QueryStringParameters.Add("rp", "%2f", false, false);
FormPostHttpBody request2Body = new FormPostHttpBody();
request2Body.FormPostParameters.Add("FormName", this.Context["$HIDDEN1.FormName"].ToString());
request2Body.FormPostParameters.Add("Email", Login);
request2Body.FormPostParameters.Add("Password", Password);
request2.Body = request2Body;
yield return request2;
request2 = null;
WebTestRequest request4 = new WebTestRequest("https://codeproject.org.cn/Script/Ratings/Ajax/RateItem.aspx");
request4.QueryStringParameters.Add("obid", ArticleId.ToString(), false, false);
request4.QueryStringParameters.Add("obtid", "2", false, false);
request4.QueryStringParameters.Add("obstid", "1", false, false);
request4.QueryStringParameters.Add("rvv", Vote.ToString(), false, false);
request4.QueryStringParameters.Add("rvc", HttpUtility.UrlEncode(Comment), false, false);
yield return request4;
request4 = null;
}
}
}
如果登录/密码错误,您不会收到任何异常或崩溃,只是不起作用,我留给您处理。
如果您需要任何信息以满足您自己的需求,请不要忘记生成的爬虫是 WebTest 的子类,它带有一些很酷的属性。

结论
能力越大,责任越大,我希望您能为正义而爬取!