免责声明:本文仅供学习交流,如出现任何法律问题本人概不负责!
0. 前言
与自如网奋战了 2 个周,终于有点结果了。
1. 隐藏的价格信息
打开自如网(http://www.ziroom.com/z/nl/z2.html?qwd=
),直接 F12 看了一下房屋列表的源码:
<div class="priceDetail">
<p value="" class="price">
<span class="gray-6"> (每月) <span class="hui_icon"><img width="60" height="18"
src="//static8.ziroom.com/phoenix/pc/images/201810/img_label5.png" /></span> </span> </p>
<p class="more">
<a href="//www.ziroom.com/z/vr/62035032.html" target="_blank">查看更多</a></p>
</div>
复制代码
找到了房屋详情的地址(www.ziroom.com/z/vr/62035032.html
),但是价格信息没有找到,没办法,到详情里找一下,发现价格信息的源码如下:
<span class="price"> <b></b>
<span class="room_price" id="room_price"></span>
<span class="gray-6">/月(季付)</span> </span>
复制代码
源码里竟然没有价格信息,然后在审查元素,发现价格信息应该是动态加载的。而且用的还是背景图数字。
打开这个图片的连接看一下(http://static8.ziroom.com/phoenix/pc/images/price/81c4fe87c108f9515c4ee4bafff68529s.png
)
不错,已经找到了价格的初始信息,下面打算提取图片中的数字,然后根据 html 里的偏移量计算出真正的价格。
2. 图片反 OCR 识别
图片信息提取,首先想到的 就是 OCR 识别,而且都是很标准的数字,本来以为难度级别属于 hello world 级别的,但是万万没想到低估了自如网的反爬虫工作
使用 OCR 识别,一顿操作猛如虎,发现竟然识别不了,直到我把图片转换成纯黑白的,然后才发现了问题。
url = 'http://static8.ziroom.com/phoenix/pc/images/price/9bbd4bf71c11e7c8149485d9f1ec5adbs.png'
response = requests.get(url)
im = Image.open(io.BytesIO(response.content))
im = im.convert('1')
im.show()
复制代码
发现图片竟然是下面这种镂空的,难怪 OCR 识别不出来
这就有点尴尬了,看到这种图片首先想到的就是 TensorFlow 搞一波图像识别,不过,一个小小的爬虫而已不至于用这么先进的武器吧(其实是我不会而已!^_-),然后又找了几张这种数字图片,对比之下发现,所有的图片,只要数字相同,镂空的规律是一样的,换句话说,所有图片中的数字 1 都是一种镂空的规律!!! 这就有点意思了,首先想到的就是把图片转成矩阵,然后把矩阵和具体的数字做好映射,持久化下来,之后再次请求到图片,拿图片里的矩阵与持久化后的矩阵做对比,这样就能够解析出图片里的数字了。
def analyze_img():
url = 'http://static8.ziroom.com/phoenix/pc/images/price/9bbd4bf71c11e7c8149485d9f1ec5adbs.png'
response = requests.get(url)
im = Image.open(io.BytesIO(response.content))
im = im.convert('1')
im.show()
num = [0,1,4,8,9,3,6,2,7,5]
num_dict = {}
for i in range(10):
data = im.crop((i*30,0,(i+1)*30,30)).getdata()
data = np.matrix(data,dtype='int')/255
num_dict[num[i]] =data
fp = open('num_dict.num', 'wb')
pickle.dump(num_dict, fp, protocol=-1)
fp.close()
return num_dict
复制代码
嗯,买迈进了一小步。不太明白为什么这个图片在镂空的时候没有加一些随机的元素。如果说是由于随机之后出现极端的情况导致不容易被人眼识别,但是可以随机出一个或者两个具体的位置,随机的镂空,这样既不会出现不容易被人眼识别的情况,也能很好的防止上面这种爬虫的出现。
总之,不管自如网是出于什么考虑,背景图片的数字提取是解决了。下面,就需要找一下这张图片是怎么跟房屋信息绑定的。
3. 动态加载的图片
在 F12 的 网络请求里确实找到了这张图片的请求信息,但是发现,图片的名称是随机串。而且同一个房屋信息,每次请求时的图片名称都不一样,这说明,后台是随机生成的这张图片,没有跟房屋绑定。
所以需要找一下,这张图片的请求是从什么地方发起的,请求的 URL 又是从什么地方获取的。
在 F12 里找到了这张图片的请求位置,但是对比 html 源码发现,并没有这个请求,这说明这个元素是通过 js 动态加载的。
下面就需要找一下,到底是哪个 js 加载的这些信息。重新看了一下,房屋详情页面的源码,发现源码底部,有这样一段 js 的引用。
<script type="text/javascript">
var ZRCONFIG = {
"URL_GET_LOGIN_STATE":"/user/check-login?url=",
"URL_GET_LOGIN_STATE":"/user/check-login?url=",
"URL_GET_DETAIL_STEWARD":"/detail/steward?resblock_id=",
"URL_GET_DETAIL_INFO":"/detail/info?",
"PAGE":"detail"
};
</script>
<script type="text/javascript" src="//static8.ziroom.com/fecommon/library/jquery/jquery-1.8.3.min.js"></script>
<script type="text/javascript" src="//static9.ziroom.com/phoenix/pc/js/2017/common.min.js?1555741850"></script>
<script type="text/javascript" src="//static8.ziroom.com/phoenix/pc/js/detail.min.js?1555741850"></script>
<script type="text/javascript" src="//api.map.baidu.com/api?v=2.0&ak=CB9b776692623d30a148b5c5dc2b75a6"></script>
<script src="//static8.ziroom.com/phoenix/pc/js/mappage.min.js" type="text/javascript"></script>
复制代码
看命名也可以猜到 'datail.min.js' 里应该有玄机。请求下这个 js 的源码看一下。终于找到了。
$.ajax({
type: "GET",
url: ZRCONFIG.URL_GET_DETAIL_INFO
+ "id=" + $("#room_id").val()
+ "&house_id=" + $("#house_id").val(),
success: function(data) {
if (data.code == "200") {
var dataObj = data.data;
var priceTableList = dataObj.payment;
var listArr = [];
var trClass = "";
var listStr = "";
var priceStyle = "<style>.room_price i.num{background-image:url(" + dataObj.price[0] + ");}"
+ "body.ratio2 .price i.num{background-image:url(" + dataObj.price[1]
+ ");}.pay_price i.num,.text_r i.num{background-image:url(" + priceTableList[0].rent[0] + ");}"
+ "body.ratio2 .pay_price i.num,body.ratio2 .text_r i.num{background-image:url("
+ priceTableList[0].rent[1] + ");background-size:auto 14px;}<style>";
$("head").append(priceStyle);
var priceListHtml = "";
for (var j = 0; j < dataObj.price[2].length; j++) {
priceListHtml += '<i class="num" style="background-position:-'
+ (dataObj.price[2][j] * offset_unit)
+ 'px"></i>'
}
... ... (省略其他代码)
复制代码
发现这个 js 发起了一个 ajax 请求,请求成功后,不仅设置了图片背景,而且还设置了切图的偏移量。
终于看到希望了,我们可以直接请求这个 ajax 的地址,不仅能拿到图片,还能拿到价格信息对应图片上的数字的位置。
ajax 的请求地址是 url: ZRCONFIG.URL_GET_DETAIL_INFO + "id=" + $("#room_id").val() + "&house_id=" + $("#house_id").val()
其中 ZRCONFIG.URL_GET_DETAIL_INF 值在 房屋详情里的 js 里定义的 ,值为 /detail/info?
room_id 和 house_id 在房屋详情的页面里也能找到:
<input type="hidden" value="7e9285535a7687bf2e71be617cd07466" id="hide_key" />
<input type="hidden" value="" id="user_sex" />
<input type="hidden" value="" id="user_uid" />
<input type="hidden" value="62035032" id="room_id" />
<input type="hidden" value="60321330" id="house_id" />
<input type="hidden" value="110000" id="current_city_code" />
<input type="hidden" value="" id="ly_name" />
<input type="hidden" value="" id="ly_phone" />
<input type="hidden" value="1" id="house_type" />
<input type="hidden" id="resblock_id" value="1111027374437"/>
复制代码
拼接后的最终的 URL 地址是 /detail/info?id=62035032&house_id=60321330
然后拼接上自如网的前缀,就是最终的请求地址,http://www.ziroom.com/detail/info?id=62035032&house_id=60321330
请求一下,终于解决了,价格信息的问题。
4. 完整思路及代码
下面的问题就简单了,循环分页,筛选出每页里的房屋详情 URL ,然后根据详情里的信息去请求价格信息,最后可以把房屋信息写到 Excel 里。就结束了
完整代码在这里
评论