本文来自 fairjm@图灵社区 转截请注明出处


这90行代码写了大半年,本来年初就能写好的,结果懒癌发作一病不起,昨天突然惊醒,已经过了半年了,这半年来也没业余好好写代码,发的文章也是划水性质,不能再这么下去了.

初衷其实挺简单的,看自己发了1w+的微博,想着哪天不见了青春的记忆就荡然无存,实在太可惜了,保存到本地方便追溯.
目标是 http://www.wking-china.com/xpjylc/xxx/profile所有页码的内容.
那要怎么干呢,我们知道微博的异步页面渲染用的是接口返回html数据拼接的方式,一般需要构造请求然后自行拼接html,但一页就要请求两次,而且如何拼接要看js是怎么处理的...
enter image description here

实在是过于麻烦了有点劝退了,还不如自己登上去然后保存一下页面呢.于是就会有这样的操作:

    1.登陆http://www.wking-china.com/xpjylc  
    2.进入profile页面  
    3.键盘输入End一次,加载页面  
    4.再输入End一次,加载页面到这页底部.  
    5.保存   
    6.点下一页,回到步骤3.没有下一页就结束  

但这里有350多页,自己操作一定会手酸,何不让程序帮我们完成?

查阅了一下找到了个工具canopy,基于Selenium的F# web UI测试框架.
因为不知道微博做了什么限制,我需要偶尔看一下程序的进度,所以我还是需要界面的,选择了chrome的webdriver(注意需要使用最新版,不然测试插件会无法工作).

接下来我们就只需要略生硬地翻译一下上面的步骤就可以啦.

chromeDir <- @"D:\webdriver\chrome"  

首先设置一下webdriver所在的目录.

start chrome
url "http://www.wking-china.com/xpjylc"
waitFor (fun () -> 
        let now = currentUrl()
        now.Contains("/profile"))

启动chrome,跳转到weibo.com 登陆自己完成,然后点击自己的微博跳转到profile页,上面代码如果不在profile页会进行等待.

press Keys.End  

按下end 这边可能需要等待一下加载完毕再按下一个End,要按两次拿取一页完整的页面.
然后得到页面文本~

let getHtml () =
    let ele = element "html"
    ele.GetAttribute("outerHTML")

接下去我要获得下一页的地址,查看了下html得到css选择器的写法:

let getNextPageElement () =
    try
        Some(element "a.page.next")
    with
        _ -> None

... ...(省略文件保存 等等操作)

满心欢喜,写好了,开始运行,看着文件夹下html慢慢增加,还是挺开心的.
突然程序停了,看了下浏览器在加载的时候卡住了(应该是请求过多触发了限制....),刷新了下页面才继续加载.
因为程序判断了End两次只要要能获取到下一页才会继续,网页卡在加载根本没把下一页的按钮渲染出来,自然就不行了.
想了想还是应该降低速度,增大End的等待时间以及加上错误重试的功能才比较科学,再完善了一下滚到页底的方式:

let pressToProfileEnd () =
    let pressIt() =
        pressEnd()
        sleep 4
        pressEnd()
        sleep 4
        pressEnd() //保险起见 再来一次
        sleep 4
    pressIt()
    // try it again
    let notHasNext = getNextPageElement().IsNone
    if notHasNext then
        reload()
        pressIt()  

接下去就看着浏览器慢慢滚动,网页一页页被保存到目标文件了,这年末年初的坑终于也是给填上了,不免松了一口气.

(完整代码见:http://www.wking-china.com/xpjylc/fairjm/fsharpSnippet/blob/master/WeiboProfileCrawler.fs)

Ps:这90行可是包含了空行和注释~爬点东西工具对了代码也可以像python那么少啊(逃