当爬虫遇上重定向
爬虫之处理重定向问题
问题导论
什么是重定向?

简单的说,重定向就是请求被转发到其他页面去了
在生活中,当我们打开一个网址时,往往需要等待稍长一段时间,而最终 的网址也发生的 变化,这就是重定向。如果我们日常用浏览器浏览网站倒是没有什么问题,但是,当我们使用爬虫来爬取这样的页面时,真正的网址就会被忽略。那么,我们该如何处理这样的情况呢?
实例分析
下面我们以两个个真实的网址,我们要提取它的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),看清主机和服务器之间传输的到底是什
么,这样就能节省很大麻烦
以上就是我在学习爬虫中遇到过的重定向,以后如果有新的情况还将补充扩展,不足之处还请指
出,方便改进
