标签:
这两天需要把有道单词本中的单词导出到本地,有道本身也提供了这样的功能,但十分单一,只能导出单词拼写,音标,中文语义,不方便学习和记忆单词。所以用ruby写了这个爬虫,其中整个过程中最主要要解决的有三个问题:
我们打开有到词典的首页,会在搜索框下面发现一个”我的单词本“的链接,点开它,但单词本的页面并没有显示出来,现在你看到的一定是登录界面。对呀,每个人的单词本都是不一样的,没有身份验证怎么能行呢。就在刚刚的这个过程中发生了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=nil
main_html.search("a").each do |tag|
words_str=tag.attribute("href") if tag.content=="单词本"
end
words_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))
end
end
所以以后为了避免重定向的问题,就用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.uri
words_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")!=nil
end
params["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
文件,又会重定向回该死的登录页面。所以在登录成功后一定要管理好服务器给我们的钥匙–cookie
,cookie
在服务器的返回的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
这个包就行了。
标签:
原文地址:http://blog.csdn.net/dd_dddd/article/details/51326169