应用ruby打造个性化的有道单词本 (一)

来源:互联网 发布:thinkphp视频网站源码 编辑:程序博客网 时间:2024/06/05 08:53

这两天需要把有道单词本中的单词导出到本地,有道本身也提供了这样的功能,但十分单一,只能导出单词拼写,音标,中文语义,不方便学习和记忆单词。所以用ruby写了这个爬虫,其中整个过程中最主要要解决的有三个问题:

  1. URL重定向
  2. 模拟登录有道词典
  3. HTTP访问中cookie的管理

http重定向

我们打开有到词典的首页,会在搜索框下面发现一个”我的单词本“的链接,点开它,但单词本的页面并没有显示出来,现在你看到的一定是登录界面。对呀,每个人的单词本都是不一样的,没有身份验证怎么能行呢。就在刚刚的这个过程中发生了URL的重定向。我们可以做如下测验:

main_uri=URI("http://dict.youdao.com")main_page=Net::HTTP.get_response(main_uri)main_html=Nokogiri::HTML(main_page.body)words_str=nilmain_html.search("a").each do |tag|    words_str=tag.attribute("href") if tag.content=="单词本"endwords_uri=URI(words_str)puts Net::HTTP.get(words_uri)

则会看到打印到控制台的消息是:

The URL has moved <a href="http://account.youdao.com/login?service=dict&back_url=http://dict.youdao.com/wordbook/wordlist%3Fkeyfrom%3Dnull">here</a>

这正是我们呢在浏览器中可以看到的东西,当我们还没登录是,点击单词本的链接就会先弹出来一个登录界面,然后在我们输入登录信息之后就又会返回到单词本的页面,这期间发生了两次URL的重定向。
在发生重定向后,返回的HTTP头中location字段会显示更新后的URL,rubydoc中有详细的处理过程,在本例中可以如下写:

def fetch(uri)    page=Net::HTTP.get_response(uri)    case page    when Net::HTTPSuccess then page    when Net::HTTPRedirection then        location=page['location']        fetch(URI(location))    endend

所以以后为了避免重定向的问题,就用fetch方法代替GET方法了,至此我们解决了第一个问题。


模拟登录

模拟登录的处理比重定向麻烦了许多,分析登录界面,用户的登录信息是嵌入到一个表单当中的,除了用户名和密码,按钮和复选框以外,其他都是hidden属性,所以不可见,所以在提交的时候这些hidden字段按照name=>value的样子提交了就行,只是加入自己的信息就可以。在整个html文件中找到表单就需要Nokogiri这个包了。表单也填好了,那就把信息提交到服务器端吧,net/http中有一个post_form方法,把所有的表单按照Hash的形式传入方法,就可以提交到指定的URL了。恩,这个问题就也可以解决了。等等,URL?哪个URL?提交到哪里?是第一步重定向的那个URL么?那看一下正真的浏览器是怎么做的吧
这里写图片描述
没错,你看到的请求网址才是正真的提交表单的URL。
具体实现的代码可以写成:

words_page=fetch(words_uri)words_uri=words_page.uriwords_page=Nokogiri::HTML(words_page.body)params={}words_page.xpath("//form//input").each do |t|        params[t.attribute("name").to_s]=t.attribute("value").to_s if t.attribute("name")!=nilendparams["username"]="××××"params["password"]="××××"form_action=URI "https://logindict.youdao.com/login/acc/login"words_page=Net::HTTP.post_form(form_action,params)

迫不及待的运行,期待着一个新的页面跳入眼帘,但是如果不意外,当你打印word_page.body的时候一定还会是一个重定向的页面,而且又定向到了之前的登录页面。这一切现象都表明我们的登录失败了。因为我已经做过了,所以我会建议你关掉浏览器的javascript支持,看能不能模拟到我们的登录过程。如果这样做,就算是你的浏览器都不能成功登录,只有一遍又一遍的重定向页面,登录了好几遍,眼前还是那个熟悉的登录页面。你一定已经知道是怎么回事了。没错,就是javascrip,它只是隐藏在了我们的浏览器和远程的服务器中间了,说不定我们提交的数据是已经被它动过手脚的呢。
查看登录页面的源代码:

function validate(disableSubmit){    var form = document.f;    var name = form.username.value.trim();    var password = form.password.value;    if((name.length < 1 || name == _hint)        || (password.length < 1)        || password.length > 16) {        //alert("请输入正确的网易邮箱用户名 ")        showWarning1(true);        return false;    }    form.password.value = hex_md5(form.password.value);    var savelogin = yd_get_elem("savelogin");    if (savelogin.checked){        form.cf.value = 7;    }    if(disableSubmit) {        form.submit.disabled = true;    }    return true;}

这是验证用户登录数据的一个javascript程序,顺便还给我们的密码加了密。哦,这下明白了,我们也应该提交加密后的密码,就像javascript做的那样。还要注意的是保存登录这个复选框的影响表单中的cf字段,要格外小心。这下登录成功了吧,打印word_page.body也不会重定向到登录页面了,而是正确的转到了你的单词本,不管怎么说,离成功又尽了一步呢。


管理cookie

HTTP协议是不会记忆状态的,所以如果这个时候我们用GET方法获取你的单词本的html文件,又会重定向回该死的登录页面。所以在登录成功后一定要管理好服务器给我们的钥匙–cookiecookie在服务器的返回的http报文中的set-cookie字段指定,用来记录特定的状态。

cookies=words_page.header['set-cookie']new_uri=URI.parse(words_page['location'])http=Net::HTTP.new(new_uri.host,new_uri.port)res=http.get(new_uri.path,{'cookie'=>cookies})res=Nokogiri::HTML(res.body)

现在打印res.body是不是我们需要的单词本的页面就下载下来了,接下来做的就是从页面中提取我们感兴趣的数据,用好nokogiri这个包就行了。未完,请移步打造个性化有道单词本(二)

0 0
原创粉丝点击