最近做了一个英汉小翻译的东东,用的是VC,ADO + Access访问数据库,单词数据库是从金山打字通2002弄来的。后来想了想,想再加个在线翻译的功能,记得经常使用GOOGLE翻译网站的在线翻译,也蛮好用的。于是用Ethereal抓包工具抓了一下它的包,发现浏览器发出去的包格式如下:

POST /translate_t?langpair=en|zh-CN HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-silverlight, application/QVOD, */*
Referer: http://www.google.cn/language_tools
Accept-Language: zh-cn
Content-Type: application/x-www-form-urlencoded
Accept-Encoding: gzip , deflate 
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1; .NET CLR 2.0.50727)
Host: translate.google.com
Content-Length: 46
Connection: Keep-Alive
cache-Control: no-cache
Cookie: __utmc=195145449; __utma=195145449.421253565.1205461371.1205592024.1205592348.11; __utmz=195145449.1205585337.9.8.utmccn=(referral)|utmcsr=hao123.com|utmcct=/ss/fy.htm|utmcmd=referral; __utmb=195145449

hl=zh-CN&ie=UTF-8&text=bad&langpair=en%7Czh-CN

以前也曾接触过一点HTTP协议的知识,HTTP协议并不难,实际上只不过是互相发送些命令,这些命令都是ASCII码的字符串。不过也还总是有些地方模糊不清。现在就来做个笔记,把已经清楚的一些东西记下来,日积月累就好了:

上面的这段HTTP协议请求包数据其实是由两部分组成的:分别是HTTP请求协议头和HTTP用户数据段。HTTP请求协议头和HTTP用户数据段之间用空行隔开。另外还有一个要注意的地方是每一行的结尾都有个回车换行符(VC中\r\n或者是VB中的vbCrlf),不然怎样叫做一行呢?呵呵。

POST /translate_t?langpair=en|zh-CN HTTP/1.1
方法 处理该HTTP请求的页面URL(相对路径) 协议版本号

POST表示用HTTP协议的POST方法,HTTP协议常用的方法还有GET,我们来看一个GET方法的HTTP请求包格式:

GET /search?hl=zh-CN&q=%E4%B8%AD%E5%9B%BD%E4%BA%BA&btnG=Google+%E6%90%9C%E7%B4%A2&meta=&aq=f HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-silverlight, application/QVOD, */*
Referer: http://www.google.cn/
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1; .NET CLR 2.0.50727)
Host: www.google.cn
Connection: Keep-Alive
Cookie: PREF=ID=77ecc97ca62c7506:NW=1:TM=1205461328:LM=1205461328:S=J2uiHSrM9WZw7SBK; NID=8=Y8WD-oZWm6VqlKQPJn4NAaFJ6OZH9m7DHwGuYHa4NTczuMmCjOlnCn7WYxLAYJq-ZsGKGBSRWTZdQAwbt7zuzIPPxVqKp3zCb93z5GuHHxxblzwcTGQUmcMADxh7v3eX

这是在www.google.cn搜索页面输入了“中国人”之后,浏览器发送出来的HTTP请求包。

GET /search?hl=zh-CN&q=%E4%B8%AD%E5%9B%BD%E4%BA%BA&btnG=Google+%E6%90%9C%E7%B4%A2&meta=&aq=f HTTP/1.1
方法 处理该HTTP请求的页面URL(相对路径) 协议版本号

可以发现,www.google.cn搜索页面使用的GET方法(每个页面所使用的方法可以从该网页的源代码中查看到)。
可以看到,两个方法之间的区别是GET方法不带HTTP用户数据段,GET方法的HTTP用户数据段包含在了这里
/search?hl=zh-CN&q=%E4%B8%AD%E5%9B%BD%E4%BA%BA&btnG=Google+%E6%90%9C%E7%B4%A2&meta=&aq=f

解释一下上面这个URL:
/search是处理该GET请求的页面地址,使用的是相对路径,?开始后面的就是发送的用户数据段了,其实就是该页面中各元素的值。它们之间用&分隔。

hl=zh-CN
q=%E4%B8%AD%E5%9B%BD%E4%BA%BA
btnG=Google+%E6%90%9C%E7%B4%A2
meta=
aq=f

你可以用IE浏览器打开www.google.cn这个网页,然后点IE里的查看菜单,再点源文件,在打开的脚本文件中分别查找hl、q、btnG、meta、aq等字符串。可以发现,

<input name=hl type=hidden value=zh-CN> 名字为hl的隐藏(type=hidden)输入框,值value为zh-CN

<input autocomplete="off" maxlength=2048 name=q size=55 title="Google 搜索" value=""> 名字为q的输入框,这个就是你在搜索时输入的那个框,初始值value为空,当你输入若干字符后点“Google 搜索”按钮,该控件的value值就为你输入的字符了。

<input name=btnG type=submit value="Google 搜索"> 名字为btnG的提交按钮(submit),就是那个“Google 搜索”按钮。

<input name=btnI type=submit value="手气不错"> 另外一个名字为btnI的提交按钮,“手气不错”按钮。

<input id=all type=radio name=meta value="" checked><label for=all>所有网页  </label> 名字为meta的单选钮(radio),值value为空。旁边还有几个单选钮就不列出了。

<form action="/search" name=f> 以上这些控件都是包含在一个form中的,当用户单击这个form中的submit按钮时,这个form中的数据(各控件的值)就会被浏览器提交到action="/search"这个页面去处理。

好了,我们理解了GET方法,回到我们POST中来。跑题了,呵呵。

Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash,

application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-silverlight, application/QVOD, */*
接收的数据类型,格式为:主类型/子类型

Referer: http://www.google.cn/language_tools
应该是从哪个页面转向过来的(也就是提交该HTTP请求的页面)

Accept-Language: zh-cn
接收方使用的语言,zh-cn应该代表简体中文

Content-Type: application/x-www-form-urlencoded
网页内容(用户数据段)所属的类型

Accept-Encoding: gzip, deflate
接收方的数据编码方式,gzip经过压缩的

User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1; .NET CLR 2.0.50727)
用户浏览器类型

Host: translate.google.com
主机名字

Content-Length: 46
网页内容(用户数据段)的长度

Connection: Keep-Alive
连接状态,Keep-Alive保持连接

cache-Control: no-cache
不使用cache

Cookie: __utmc=195145449; __utma=195145449.421253565.1205461371.1205592024.1205592348.11; __utmz=195145449.1205585337.9.8.utmccn=(referral)|utmcsr=hao123.com|utmcct=/ss/fy.htm|utmcmd=referral; __utmb=195145449
Cookie内容

(注意:以上这些都是我个人的理解,并不一定正确。需要正确的解释请参考HTTP协议的介绍。)

最关键的还是用户数据段这地方,改动一下就可以模拟IE浏览器POST数据到GOOGLE翻译网站了。
hl=zh-CN&ie=UTF-8&text=bad&langpair=en%7Czh-CN

hl=zh-CN
ie=UTF-8
text=bad
langpair=en%7Czh-CN

<input type=hidden name=hl value="zh-CN"> 名字为hl的隐藏(type=hidden)输入框,值value为zh-CN

<input type=hidden name=ie value="UTF8"> 名字为ie的隐藏(type=hidden)输入框,值value为UTF8

<textarea name=text wrap=SOFT dir="ltr" id=source>bad</textarea> 名字为text的文本框(textarea),就是我们输入需要翻译的原文的那个框

<select name=langpair>
<option value="ar|en">阿拉伯文到英语</option>
......
<option value="en|zh-CN" selected>英语到中文(简体)</option>
......
</select>
名字为langpair的列表组合框,当我们选择英语到中文(简体)的时候它的值value就等于en|zh-CN了,上面langpair=en%7Czh-CN这里的%7C就是|的百分号加十六进制编码。

好了,介绍到这里,我们下面来做个例子试验一下!

#include <stdio.h> 
#include <winsock2.h> //winsock库头文件
#include <windows.h>

#pragma comment(lib,"WS2_32.lib") //连接winsock库文件

int main()

WSADATA stWsaData = {0};
SOCKET hSocket = 0;
sockaddr_in stRemoteAddr = {0};
char cSendData[4096] = {0}; //发送数据缓冲区
char cRecvData[10240] = {0}; //接收数据缓冲区
intnRetBytes = 0;

::WSAStartup(MAKEWORD(2,0),&stWsaData); //初始化winsock库

hSocket = ::socket(AF_INET,SOCK_STREAM,0); //创建TCP套接字
if(hSocket == INVALID_SOCKET)
return 0;

stRemoteAddr.sin_family = AF_INET; //填充sockaddr_in结构
//64.233.189.104为ping www.google.com得到的IP,暂时懒得写转换IP函数了
stRemoteAddr.sin_addr.S_un.S_addr = inet_addr("64.233.189.104"); 
stRemoteAddr.sin_port = htons(80);

if(::connect(hSocket,(sockaddr *)&stRemoteAddr,sizeof stRemoteAddr) == -1) //连接到服务器
...{
::closesocket(hSocket); //关闭TCP套接字
::WSACleanup(); //释放winsock库
return 0;
}

//构造HTTP请求数据包的请求头
strcpy(cSendData,"POST /translate_t?langpair=en|zh-CN HTTP/1.1\r\n"); 
strcat(cSendData,"Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-silverlight, application/QVOD, */*\r\n"); 
strcat(cSendData,"Referer: http://www.google.cn/language_tools\r\n"); 
strcat(cSendData,"Accept-Language: zh-cn\r\n"); 
strcat(cSendData,"Content-Type: application/x-www-form-urlencoded\r\n"); 
//strcat(cSendData,"Accept-Encoding: gzip ,deflate\r\n"); 
strcat(cSendData,"User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1; .NET CLR 2.0.50727)\r\n"); 
strcat(cSendData,"Host: translate.google.com\r\n"); 
strcat(cSendData,"Content-Length: 46\r\n");
strcat(cSendData,"Connection: Keep-Alive\r\n"); 
strcat(cSendData,"cache-Control: no-cache\r\n"); 
//strcat(cSendData,"Cookie: __utmc=195145449; __utma=195145449.421253565.1205461371.1205592024.1205592348.11; __utmz=195145449.1205585337.9.8.utmccn=(referral)|utmcsr=hao123.com|utmcct=/ss/fy.htm|utmcmd=referral; __utmb=195145449\r\n");

//请求头与用户数据段之间的分隔行
strcat(cSendData,"\r\n");

//构造HTTP请求数据包的用户数据段
strcat(cSendData,"hl=zh-CN&ie=UTF-8&text=bad&langpair=en%7Czh-CN");

printf("HTTP协议请求头包格式: %s",cSendData);

::send(hSocket,cSendData,sizeof cSendData,0); //发送

nRetBytes = ::recv(hSocket,cRecvData,sizeof cRecvData,0); 
printf(" 总共收到了%d数据!",nRetBytes);

//保存网页到文件(注意:返回的数据中包含HTTP响应头,而HTTP网页文件是不应该有这个的,应该去掉,这里我省略了)
FILE * fp = fopen("C:翻译结果网页.htm","wb");
fwrite(cRecvData,nRetBytes,1,fp); 
fclose(fp);

::closesocket(hSocket); //关闭TCP套接字
::WSACleanup(); //释放winsock库
return 0; 
}

上面这个仅仅是翻译英文单词“bad”的功能,实现完整的例子还是由读者自己来做吧。^_^需要做的只是修改text字段的值,另外还有就是相应的Content-Length也应该改为你用户数据段实际的长度。

还有两个问题:1,我为什么把上面的两段注释呢?Accept-Encoding: gzip , deflate是返回的网页的内容是经过压缩了的,我们就要自己解压缩,偷懒的做法是让服务器返回不压缩的网页。另外那句Cookie要不要无关紧要。

2,当你尝试着用中文去翻译成英文的时候,你会遇到URL编码的问题,直接发送中文过去是错误的,必须经过URL编码(%E4%B8%AD%E5%9B%BD%E4%BA%BA的形式),这个还不止,还加上网页编码的问题,像百度和GOOGLE就是个样例:百度网页使用的是GB2313编码,而GOOGLE使用的却是UTF-8编码,针对不同的网页编码需要根据不同的编码进行转换。

像在搜索页面输入“中国人”三个中文,

百度的编码是:http://www.baidu.com/s?wd=%D6%D0%B9%FA%C8%CB&cl=3。

“中国人”的GB2312编码:D6 D0 B9 FA C8 CB(16进制,一个中文两个字节)

而GOOGLE的编码却是:http://www.google.cn/search?hl=zh-CN&q=%E4%B8%AD%E5%9B%BD%E4%BA%BA&meta=&aq=f

“中国人”的UTF-8编码:E4 B8 AD E5 9B BD E4 BA BA(16进制,一个中文三个字节)

呵呵,这些就先搁着了,留待以后解决。

//*********************************以下内容为转成Delphi 源码:

unit uSocket;

interface

uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, IdWinSock2;

function getStr(): string;

implementation

function getStr(): string;
var
stWsaData: TWSAData;
hSocket: TSocket;
stRemoteAddr: TSockAddr;
cSendData: Pchar; //array[0..4095] of Char;
cRecvData: Pchar; //array[0..10239] of Char;
nRetBytes: integer;
iReturn: integer;
pData: string;
begin
Result := '';
iReturn := WSAStartup(MAKEWORD(2, 0), stWsaData); //初始化winsock库
hSocket := socket(AF_INET, SOCK_STREAM, 0); //创建TCP套接字
if (hSocket = INVALID_SOCKET) then exit;
ZeroMemory(@stRemoteAddr, sizeof(stRemoteAddr));
stRemoteAddr.sin_family := AF_INET; //填充sockaddr_in结构
//64.233.189.104为ping www.google.com得到的IP,暂时懒得写转换IP函数了
stRemoteAddr.sin_addr.S_addr := inet_addr('64.233.189.104');
stRemoteAddr.sin_port := htons(80);
if connect(hSocket, @stRemoteAddr, sizeof(stRemoteAddr)) = -1 then //连接到服务器
begin
closesocket(hSocket); //关闭TCP套接字
WSACleanup(); //释放winsock库
exit;
end;
GetMem(cSendData, 4096);
ZeroMemory(cSendData, 4096);
GetMem(cRecvData, 10240);
ZeroMemory(cRecvData, 10240);
try
//构造HTTP请求数据包的请求头
pData := 'POST /translate_t?langpair=en|zh-CN HTTP/1.1' + #13#10 +
'Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-silverlight, application/QVOD, */*' + #13#10 +
'Referer: http://www.google.cn/language_tools' + #13#10 +
'Accept-Language: zh-cn' + #13#10 +
'Content-Type: application/x-www-form-urlencoded' + #13#10 +
//'Accept-Encoding: gzip ,deflate'+ #13#10 +
'User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; InfoPath.1; .NET CLR 2.0.50727)' + #13#10 +
'Host: translate.google.com' + #13#10 +
'Content-Length: 46' + #13#10 +
'Connection: Keep-Alive' + #13#10 +
'cache-Control: no-cache' + #13#10 +
//'Cookie: __utmc=195145449; __utma=195145449.421253565.1205461371.1205592024.1205592348.11; __utmz=195145449.1205585337.9.8.utmccn=(referral)|utmcsr=hao123.com|utmcct=/ss/fy.htm|utmcmd=referral; __utmb=195145449' + #13#10 +
//请求头与用户数据段之间的分隔行
#13#10 +
// 构造HTTP请求数据包的用户数据段
'hl=zh-CN&ie=UTF-8&text=bad&langpair=en%7Czh-CN';
StrPCopy(cSendData, pData);
send(hSocket, cSendData^, Length(cSendData), 0); //发送
nRetBytes := recv(hSocket, cRecvData^, 10240, 0);
Result :=StrPas(cRecvData);
//释放pchar变量
finally
FreeMem(cSendData);
FreeMem(cRecvData);
closesocket(hSocket); //关闭TCP套接字
WSACleanup(); //释放winsock库
end;
end;

end.

http://hi.baidu.com/liu494021458/blog/item/841ccbefd2f22c202cf5345b.html

http://www.delphitop.com/html/wangluo/1344.html

另一个例子:

http://www.experts-exchange.com/questions/26208481/Delphi-7-Google-Translate-API-UTF-8-response-issue.html

delphi 利用HTTP的POST方法做个在线翻译的小工具 good的更多相关文章

  1. 17、 利用扇贝网:https://www.shanbay.com/, 做个测单词的小工具。

    先说下,我可以说完全没有看题目要求,我只看了下扇贝网的单词测试工具就开始编码了,写出来的代码尽可能的模仿了网站上的效果. 因为把问题搞复杂了,在这个练习上耽误了很长时间,最后都不想写了,所以代码有些混 ...

  2. 利用ncurses库开发终端工具箱(1)—— ToDoList小工具开发

    准备工作 腾讯云服务器(Ubuntu),C++编程语言 由于想输出界面中包含中文,所以安装库 libncursesw5,依次输入下面三行命令 sudo apt-get install libncurs ...

  3. 利用java实现可远程执行linux命令的小工具

    在linux的脚本中,如果不对机器做其他的处理,不能实现在linux的机器上执行命令.为了解决这个问题,写了个小工具来解决这个问题. 后面的代码是利用java实现的可远程执行linux命令的小工具,代 ...

  4. Delphi 利用TComm组件 Spcomm 实现串行通信

    Delphi 利用TComm组件 Spcomm 实现串行通信 摘要:利用Delphi开发工业控制系统软件成为越来越多的开发人员的选择,而串口通信是这个过程中必须解决的问题之一.本文在对几种常用串口通信 ...

  5. Delphi异常处理的基本原则和方法

    Delphi异常处理的基本原则和方法 一.异常的来源. 在Delphi的应用程序中,下列的情况都比较有可能产生异常.(1)文件处理(2)内存分配(3)Windows资源(4)运行时创建对象和窗体(5) ...

  6. (转载)EhLib 在 Delphi 7 下的安装方法

    EhLib 在 Delphi 7 下的安装方法 1.将 EhLib 解压到一个目录,如:E:\VCL\EhLib: 2.将 EhLib 安装目录下 Common 目录.DataService 目录下的 ...

  7. 用Delphi画圆角Panel的方法(使用CreateRoundRectRgn创造区域,SetWindowRgn显示指定区域)

    用Delphi画圆角Panel的方法: procedure TForm1.Button5Click(Sender: TObject);var fhr :Thandle;beginfhr:=Create ...

  8. linux利用sendmail发送邮件的方法

    Linux利用sendmail发送邮件, 方法1 安装sendmail即可使用, mail -s "test" user@sohu.com bin/mail会默认使用本地sendm ...

  9. 反射中的一个问题点:利用Method执行main方法特殊的地方

    利用Method执行main方法 问题: 启动Java程序的main方法的参数是一个字符串数组,即public static void main(String[] args),通过反射方式来调用这个m ...

随机推荐

  1. ThinkPHP快捷函数

    16个快捷函数用法 1.A() 实例化控制器  格式:[资源://][模块/]控制器A($name,$layer='',$level='')@param string $name 资源地址   @pa ...

  2. Python 第十三篇之一:前端页面 js和dome

    一:JavaScript: JavaScript是一门编程语言,浏览器内置了JavaScript语言的解释器,所以在浏览器上按照JavaScript语言的规则编写相应代码之,浏览器可以解释并做出相应的 ...

  3. web Listener

    在web应用内部会不断地发生各种事件例如,web应用被启动,web应用停止,用户session开始,用户session 结束,用户请求到达,通常这些事件对开发者而言是透明的.实际上ServletAPI ...

  4. (Problem 6)Sum square difference

    Hence the difference between the sum of the squares of the first ten natural numbers and the square ...

  5. 基于visual Studio2013解决C语言竞赛题之0418位数操作

      题目 解决代码及点评 /************************************************************************/ /* 18. 给 ...

  6. Android 涂鸦最佳实践

    Android中实现手势画图一般都两种方式,一是直接在View上绘制,而是使用SurfaceView. 两者还是有一些差别的.简介下. View:显示视图,内置画布,提供图形绘制函数.触屏事件.按键事 ...

  7. 二叉树的前序和中序得到后序 hdu1710

    今天看学长发过来的资料上面提到了中科院机试会有一个二叉树的前序中序得到后序的题目.中科院的代码编写时间为一个小时,于是在七点整的时候我开始拍这个题目.这种类型完全没做过,只有纸质实现过,主体代码半个小 ...

  8. 判断程序是否在VMWare内运行

    现在有许多用户都喜欢用虚拟机来测试他们的软件,以避免对真实机器环境造成损害.但是在虚拟机中,有些功能是受限,甚至不可能完成的,因此,需要在程序中判断虚拟机的环境,如果程序在虚拟机内运行,则就要把虚拟机 ...

  9. 列表标题栏添加CheckBox(自定义HanderView的时候实现)

    前段时间项目上的要求,要实现一个列表(见下图1).类似网页上的列表,可以通过选中标题栏的复选框,实现全选或者全不选的功能.但是看了很久,都没看到Qt哪个方法可以实现在标题栏添加控件. 图1 要实现这样 ...

  10. JQuery遍历json数组的3种方法

    这篇文章主要介绍了JQuery遍历json数组的3种方法,本文分别给出了使用each.for遍历json的方法,其中for又分成两种形式,需要的朋友可以参考下 一.使用each遍历 $(functio ...