如何以编程方式将链接和数据提交到 DZone 和其他网站






4.79/5 (8投票s)
文章详细介绍了 Web 抓取和 HttpWebRequest 的使用步骤。
引言
本文更像是一篇关于如何以编程方式在网站上执行某些操作的指南。我以向 DZone 提交链接为例来说明这个概念。如今,人们非常强调将您的帖子、链接、文章提交或与整个社区分享,因此我认为 DZone 链接提交的这个例子会很好。实现整个概念的核心是 HttpWebRequest
和 HttpWebResponse
对象。由于我使用的是 .NET Framework,所以我提到了这些类。但实际上,这就像发送一个 HTTP 请求并分析响应一样简单。所以你可以使用手头上的任何工具。我将解释我为得出解决方案所遵循的每一步。这些步骤几乎适用于所有类型的应用程序。
使用网站执行操作
首先,您需要分析您正在执行的操作,网站如何发送请求,以及返回什么样的响应。这两个分析步骤是驱动整个解决方案的关键。让我们以从 DZone 网站提交新链接为例。您单击“添加新链接”,然后会被带到一个新页面,要求您登录。然后您登录,会被带到一个页面,您可以在其中为 URL、标题、描述、标签等提供值。然后单击“提交”按钮,就完成了。因此,基于此,以下是您需要以编程方式执行的步骤。
- 提交添加链接的请求
- 捕获重定向到登录页
- 执行网站登录
- 登录失败后发送添加到页面的请求
现在,要查看浏览器在执行所有这些操作时正在做什么,请启动 **Fiddler** 等工具并监视这些操作的所有请求/响应。如果您能够模仿这些操作,那么您就可以开始了。现在让我们看看您将如何以编程方式执行此操作。
提交添加请求
您将使用 HttpWebRequest
对象向 http://www.dzone.com/links/add.html 发送请求。此时,您不必担心指定任何其他参数,如标题、URL 等,因为您的请求将不会成功,因为您尚未登录该网站。从技术上讲,您尚未与该网站建立经过身份验证的会话。
捕获重定向到登录页
当您发送未经授权的请求添加链接时,该网站会将您重定向到登录页面。这意味着当您发送 HTTP 请求访问 add.html 页面时,服务器会发送一个 HTTP 响应,状态码为 **302**,表示响应正在被重定向。并且随着该响应,它会在响应的 **Location** 标头中发送重定向位置。因此,以编程方式,您需要提交一个请求,查找响应状态码,并找到 **Location** 标头。代码如下所示
static string GetLoginUrl(CookieContainer cookies, string targetUrl)
{
int hops = 1;
int maxRedirects = 20;
bool foundIt = false;
HttpWebRequest webReq;
string loginUrl = targetUrl;
do
{
webReq = WebRequest.Create(loginUrl) as HttpWebRequest;
webReq.CookieContainer = cookies;
webReq.AllowAutoRedirect = false;
string msg = string.Format("Hope[(0) - {1}", hops++, loginUrl);
Debug.WriteLine(msg);
HttpWebResponse webResp = webReq.GetResponse() as HttpWebResponse;
webResp.Close();
if (webResp.StatusCode == HttpStatusCode.Found)
{
loginUrl = webResp.Headers["Location"] as String;
}
else
{
foundIt = (webResp.StatusCode == HttpStatusCode.OK);
break;
}
} while (hops <= maxRedirects);
return foundIt ? loginUrl : string.Empty;
}
请注意,代码在一个 while
循环中,原因是有些网站实际上可能会将您重定向到几个页面,然后再将您发送到最终的登录页面。因此,我将循环限制为 20 次跳转。
Cookie
这是整个实现中最重要的部分。当您开始与网站建立会话时,它会在响应中发送一些 cookie。并且它期望在后续请求中发送其中的一些 cookie,以确保您已打开经过身份验证的会话。如果您查看上面的代码,我已经将 CookieContainer
对象附加到请求中,以确保收集所有响应中发送的 cookie。然后可以将此容器附加到后续请求。
执行登录
当您在网站上执行登录时,它会向服务器提交一个 FORM
,其中包含一些键值对,这些键值对包含验证用户所需的数据。您可以使用 Internet Explorer 工具栏、FireBug 或任何其他工具来检查页面的 HTML,以找到 FORM
标签以及需要发送的值。我使用 FireBug 检查了该部分,以找出我需要的值。以下图像显示了结果
您可以看到有一个 FORM
,其 POST
操作指向 /links/j_acegi_security_check。您会发现它有两个文本框,元素名称分别为 j_username
和 j_password
,它们接受登录信息,并用于使用 POST
请求提交数据。所以这些是你执行登录操作所需的信息。下面的代码显示了如何完成此操作
RequestAttributes reqAttribs = new RequestAttributes();
reqAttribs.OverrideConfigurationSettings = true;
reqAttribs.AllowSecureSiteCrawl = true;
reqAttribs.AutoRediectEnabled = false;
reqAttribs.MaxRedirects = 100;
reqAttribs.IsPost = true;
reqAttribs.RequestUrl = "http://www.dzone.com/links/j_acegi_security_check";
reqAttribs.CookieContainer = container;
reqAttribs.RequestParameters.Add("j_username", "xxxxxx");
reqAttribs.RequestParameters.Add("j_password", "xxxxxx");
HttpProtocol obHttp = new HttpProtocol(reqAttribs);
HttpProtocolOutput obOutput = obHttp.GetProtocolOutput();
登录是否成功?
在执行完上述请求并收到响应后,现在您将问的大问题是,我如何检查登录是否成功?您不能依赖响应的状态码,因为如果它是 **200**,则表示请求成功。有几件事您可以检查。有些网站会将您重定向到一个登陆页面,因此您可以检查您是否收到 **302** 响应码。或者,一种可靠的检查方法是解析响应,看看页面上是否有一个登录框。例如,对于 DZone.com 网站,您可以检查页面上是否存在一个具有 name
属性且值为 j_username
的标记节点,或者任何对登录页面唯一的标记。如果您找到该节点,则表示登录失败。这是我为我的应用程序使用的一些示例代码。
static bool CheckLoginStatus(HttpProtocolOutput loginRespOutput)
{
ParserStream obStream =
new ParserStream(new System.IO.MemoryStream
(loginRespOutput.Content.ContentData));
Source obSource =
new InputStreamSource(obStream, null,
loginRespOutput.Content.ContentData.Length);
Page obPage = new Page(obSource);
obPage.Url = "http://www.dzone.com/links/j_acegi_security_check";
Lexer obLexer = new Lexer(obPage);
Parser obParser = new Parser(obLexer);
HasAttributeFilter filter = new HasAttributeFilter("name", "j_username");
NodeList oNodes = obParser.ExtractAllNodesThatMatch(filter);
return (oNodes.Count == 0);
}
提交带有授权会话的新请求
在整个登录和重定向过程中,请务必保留 cookie 容器,以便它继续收集所有 cookie。您将需要此 cookie 容器来发送请求以提交您的链接。现在,您只需要向目标 URL 发送一个新的 POST
请求,并附带适当的 FORM
参数,如 **title**、**URL** 和 **description**。
示例项目
示例项目和此处所示代码的其他先决条件可在 **ByteBlocks** 找到:ByteBlocks。