Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

exoticknight's blog


年寿有时而尽,荣乐止乎其身,二者必至之常期,未若文章之无穷。

记一次奇怪的爬虫经历

前言

由于某些原因,我需要去获取一些国家旅游景点的信息。

找到国家旅游局的网站,然后找到一个 5A 风景区目录。

网址:http://www.cnta.gov.cn:8000/Forms/TravelCatalog/TravelCatalogList.aspx?catalogType=view&resultType=5A

于是去 pyspider 的 demo 页新建一个项目:5stat,就去爬了。

分析页面

网页比较特殊,看起来是用 dotnet 写的,翻页是按钮调用 js 代码实现的。跳转后还是同一个网址。

这里就要用到 pyspider 支持的页面载入后运行 js 脚本的功能。

先分析翻页按钮干了什么。

如下图,调用一个名为 __doPostBack 的函数。

__doPostBack

在页面上寻找这个函数,看到函数体如下:

var theForm = document.forms['form1'];  
if (!theForm) {  
    theForm = document.form1;
}
function __doPostBack(eventTarget, eventArgument) {  
    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {
        theForm.__EVENTTARGET.value = eventTarget;
        theForm.__EVENTARGUMENT.value = eventArgument;
        theForm.submit();
    }
}

函数将 theForm 里面的 __EVENTTARGET 值设置为 PageNavigator1$LnkBtnNext 之后就提交了。

找到 theForm 对应的元素,看见有三个隐藏域, __EVENTTARGET__EVENTARGUMENT__VIEWSTATE

theForm

附近还有一个隐藏域 __EVENTVALIDATION。看名字就觉得要提交。

于是试试只提交这三个值看看会不会报错。

在 chrome 上安装 postman 这个应用,打开。

postman

修改方式为 POST,填上地址和三个域的值,send。

postman result

OK,返回了正确的页面,也就是可行了。

爬虫脚本

嗯 pyspider 的爬虫脚本怎么写就不详述了,不会的看文档。

着重列出爬虫执行的 js 脚本的功能。

function() {  
    var flag = 'y';
    if ( document.querySelector('#PageNavigator1_LnkBtnNext').getAttribute('disabled') ) {
        flag = 'n';
    }
    return document.form1.__VIEWSTATE.value + '~' + document.form1.__EVENTVALIDATION.value + '~' + flag;
}

如此一来在回到爬虫脚本中的时候就能得到下一页跳转的参数了。

奇怪的地方来了

因为 pyspider 的文档说明对于每个项目内的相同网址会忽略,于是按照教学提示给网址加了个 #。很明显这样的网址不会改变请求的参数(使用一些其他技术的情况下除外)。

之后不再使用这个方法,因为 pyspider 判断是否同网址实质上是简单地将网址 md5 一下生成任务 id,以此来判断是否同一个爬虫任务。后来用的方法是直接重写任务 id 的生成。

然而在爬下来的数据中却发现有除了旅游地点外的酒店信息。

原来同一个页面也有五星级饭店的信息。如下图,注意最后有一个 #

五星级饭店链接

点击后跳转到一个网址:http://www.cnta.gov.cn:8000/Forms/TravelCatalog/TravelCatalogList.aspx?catalogType=hotel&resultType=%u4E94 的页面。

五星级饭店

看起来跟旅游地点差不多,新建一个项目 5hotel,直接复制粘贴之前的代码,就是改了一下网址。

期间还将任务 id 的生成重写了一下,这样即使请求同一个网址也没问题了。

然而运行的结果却失败了。

failed

在 content 中很明显看出页面获取不全,然而代码是直接复制的,页面也是相同结构的,为什么会出现这个问题呢?

然后我就被困扰了两天,接着就没在去管,盘算以后自己实现个爬虫再爬好了。

转机

今天我再上去看,爬 5A 风景区的项目一直稳定运行。

五星级饭店的却还是无法抓取全部页面。

然后我鬼使神差地给网址加了一个 #。网址从:

www.cnta.gov.cn:8000/Forms/TravelCatalog/TravelCatalogList.aspx?catalogType=hotel&resultType=%u4E94

变成

www.cnta.gov.cn:8000/Forms/TravelCatalog/TravelCatalogList.aspx#?catalogType=hotel&resultType=%u4E94

然后就能爬了!!!

shenmegui???!!!

success

我也搞不清楚究竟是 pyspider 的问题还是 phantomjs 的问题还是 dotnet 的问题了。


About the author

exoticknight


Discussions

comments powered by Disqus