Advertisement

当爬虫遇上重定向

阅读量:

爬虫之处理重定向问题

问题导论
什么是重定向?
在这里插入图片描述
简单的说,重定向就是请求被转发到其他页面去了
在生活中,当我们打开一个网址时,往往需要等待稍长一段时间,而最终 的网址也发生的 变化,这就是重定向。如果我们日常用浏览器浏览网站倒是没有什么问题,但是,当我们使用爬虫来爬取这样的页面时,真正的网址就会被忽略。那么,我们该如何处理这样的情况呢?

实例分析
下面我们以两个个真实的网址,我们要提取它的title,提取的表达式很简单:

复制代码
    /html/head/title/text() #使用的xpath

主体代码

代码一:

复制代码
    import requests  #导入模块
    #声明头部
    header= {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"
    }
    
    html=requests.get("http://med.tongji.edu.cn",headers=header)
    
    print(html.text)

相信大家都使用过这样的代码

让我们来看看结果

复制代码
    <script type="text/javascript">
    //平台、设备和操作系统
    var system ={
        win : false,
        mac : false,
        xll : false
    };
    //检测平台
    var p = navigator.platform;
    system.win = p.indexOf("Win") == 0;
    system.mac = p.indexOf("Mac") == 0;
    system.x11 = (p == "X11") || (p.indexOf("Linux") == 0);
    if (system.win || system.mac || system.xll) {
        window.location.href = "/Web/Home";
    }else{
        window.location.href="/Mobile/Home";
    }
    </script>

上面返回了一段js代码,其中window.location.href中的字符串就是我们真正的目标网页

(可以通过浏览器验证,我们也可以看到实际上还有PC端和移动端两种网页形式)

代码二:(更换了网址)

复制代码
    import requests
    
    header= {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:62.0) Gecko/20100101 Firefox/62.0"
    }
    
    html=requests.get("http://sem.tongji.edu.cn",headers=header)
    
    print(html.text)

结果如下:

复制代码
    <meta http-equiv="refresh" content="0.1;url=http://sem.tongji.edu.cn/semch/">

这里我们又看到了一种重定向的方式,即使用“http-equiv=‘refresh’”字段来引导新的网址

上面的重定向都会使我们找不到title,导致进一步爬取失败

3.方法剖析

对于以上两种比较常见的重定向方式

方法一:requests.get(url,allow_redirects=False)

allow_redirects=False的意义为拒绝默认的301/302重定向从而可以通过

html.headers[‘Location’]拿到重定向的URL。

方法二:方法一是我参考一片博文发现的,方法可谓十分的方便,下面的方法(建议方法一不起

作用时使用)

我们可以用正则表达式将目标网址找出来,由于重定向具有标识如refresh、window.location.href

等,所以在正则中加入它们将有助于找到目标网址,同时,由于不知道网页传回的重定向类型,

所以我们还需要将两个正则都匹配一下,选出其中有结果的那个

4.代码实现

使用正则时注意表达式的匹配方式

情况一:

复制代码
    pat1='http-equiv="refresh".*?url=["]?(.*?)"' #使用正则匹配refresh字段

这里在目标网址前我加上了["]?,是为了当url在其他情况中被放到外部,单独成一个字段

如:url="http://sem.tongji.edu.cn/semch/"(上面的是放在字段content里面)时,也能将目

标网址匹配出来

情况二:

复制代码
    pat2='window.location.href=".*?(\w.*?)"'   #使用正则匹配window.location.href

这里同样有一个注意点,之前的window.location.href中,我们看到的是/Web/Home

但是有一种情况,即出现的是Web/Home(没有前面的斜杠),如果不使用\w,两种情况在后面连

接网址时会出现误差,所以我们统一匹配"Web/Home"

下面进行匹配结果的处理,代码如下:

复制代码
    from lxml import etree
    
    #使用正则匹配refresh字段
    pat1='http-equiv="refresh".*?url=["]?(.*?)"'
    ret1 =re.compile(pat1).findall(empty_urls[i])
    
    #使用正则匹配window.location.href
    pat2='window.location.href=".*?(\w.*?)"'
    ret2=re.compile(pat2).findall(empty_urls[i])
    
    #取两者中不为空的项(即存在项)
    ret=ret1 if ret1 else ret2
    
    #如果还是为空,那么打印提示信息(此时我们就不能用上面的方法了)
    if not ret:
    print("Sorry,temporarily we can find its real title")
    else:
    #如果结果存在,对匹配结果进行分析,如果网址完整,就直接使用,不完整则进行网址的拼接
    if ret[0].startswith("http"):
        pass
    else:
        ret[0]=i+"/"+ret[0]
    try:
        #获取真正的网址源代码
        real_data=requests.get(url)
         #将源码转化为能被XPath匹配的格式
        selector=etree.HTML(real_data)
        #匹配title
        real_title=selector.xpath(xpath)[0]
        print(i,"\'s real title is ",real_title.strip())
    except Exception as e:
        #异常处理
        print("Emmmm,there is an error:"+str(e))

在上面的两个网址的实验中,通过重定向的处理方式,我们得到了正确的结果

复制代码
    http://sem.tongji.edu.cn 's real title is  xxxxxxx
    http://med.tongji.edu.cn 's real title is  xxxxxxx

5.总结:
1.上面方法的探讨,都是基于起初爬取时产生的错误,并且要一步一步的输出相关信息,同时结合

资料而来,做爬虫时关键是要有耐心,虽然我们有强大的模块和工具来处理问题,但是也避免不

了一些意外的发生(就像一开始我就没考虑过还有重定向)

2.上面的讨论还是基于一些规模较小的爬虫,对于使用scrapy框架的朋友们,建议直接使用框架提

供的解决重定向的方式
3.事实上,还有重定向的方式,虽然同样使用js的方式,但是不直接隐藏在html中,而是以单独文

件传送,我的建议是直接抓包分析(推荐使用fiddle4),看清主机和服务器之间传输的到底是什

么,这样就能节省很大麻烦

以上就是我在学习爬虫中遇到过的重定向,以后如果有新的情况还将补充扩展,不足之处还请指

出,方便改进

全部评论 (0)

还没有任何评论哟~