破解花式反爬之大众点评-上

常规的反爬机制有访问频率限制、cookie限制、验证码、js加密参数等。目前解决不了的js加密是今日头条的_signature参数、新版12306登陆时的callback参数等,还有一个很奇怪的京东的s参数(在搜索结果的ajax中,返回的结果根据s参数的不同而不同,目前没有发现规律)。

而今天的网站的反爬机制是目前我见过的最有水平的,网址:http://www.dianping.com/, 以上的反爬机制它都有,而且一些关键信息全部通过css来控制图片的偏移量显示出来。

随便选择一个商家,比如 http://www.dianping.com/shop/92465668, 其中商家名称下的一些信息中的数字全部是以图片的形式显示的,每个数字都是,就连地址中的文字大部分也是图片,还有评论中的文字等。
比如1706条评论的HTML代码如下:

<span id="reviewCount" class="item">
  1
  <span class="kj-ouAp"></span>
  <span class="kj-YuRe"></span>
  <span class="kj-QXcp"></span>条评论
</span>

首先找到对应的css文件
//s3plus.meituan.net/v1/mss_0a06a471f9514fc79c981b5466f56b91/svgtextcss/499267ad9083423317049ce463a855fe.css 搜索一下上面的class,发现一下代码:

span[class^="kj-"] {
	width: 14px;
	height: 30px;
	margin-top: -9px;
	background-image: url(//s3plus.meituan.net/v1/mss_0a06a471f9514fc79c981b5466f56b91/svgtextcss/f6cfde1f84946b2e60fe15c9d0470ae3.svg);
	background-repeat: no-repeat;
	display: inline-block;
	vertical-align: middle;
	margin-left: -6px;
}
.kj-ouAp {
	background: -120.0px -7.0px;
}

背景图片中的svg文件显示的是10个数字,而.kj-ouAp的css样式则控制背景图片的位置,因为span标签限定了大小,所以正好显示svg图片中的某一个数字。

爬虫思路:要想得到相应的数据,需要先请求初始网页得到css文件和相应数据的span标签的css属性(其中1是直接显示的,也要拿到),再从css文件中提取除svg文件和一些css属性的偏移量,这里只需要第一个偏移量如 -120px,因为图片只有一行,第二个偏移量都是一样的。

svg文件是xml定义的,可以文本格式打开直接得到10个数字的文本和对应的位置。再通过上面拿到的span标签的css属性和位置比较一下,转化为相应的数字(这里有一个简单思路,不需要位置,直接去掉负号排序,那么css属性就和拿到的10个数字相对应了)

代码如下:

# -*- coding: utf-8 -*-
"""
date: Tue Nov 27 09:48:08 2018
python: Anaconda 3.6.5
author: kanade
email: kanade@blisst.cn
"""
import requests
import re
import pyquery

headers = {
        'Host':'www.dianping.com',
        'Referer':'http://www.dianping.com/beijing/ch10/g110',
        'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.61 Safari/537.36'
   }

url = 'http://www.dianping.com/shop/92465668'
# 请求商家地址
resp = requests.get(url, headers=headers)
html = resp.text
# 从url中正则匹配css文件的url
css_url_regex = re.compile(r'href="//(s3plus.meituan.net.*?)\"')
css_url = re.search(css_url_regex, html).group(1)
css_url = 'http://' + css_url
# 获取css文件
css_resp = requests.get(css_url)
regex_kj_svg = re.compile(r'span\[class\^="kj-"\][\s\S]*?url\((.*?)\)')
css_html = css_resp.content.decode('utf-8')
# 从css文件中匹配出svg文件的url
svg_kj_url = re.search(regex_kj_svg, css_html).group(1)
svg_kj_url = 'http:' + svg_kj_url
# 获取表示数字的svg文件
svg_kj_resp = requests.get(svg_kj_url)
svg_kj_html = svg_kj_resp.text
# 匹配出svg图片的10个数字
number = re.search(r'\d{10}', svg_kj_html).group()
# 匹配出以kj-开头的class属性
regex_kjs_css = re.compile(r'\.(kj-\w{4})[\s\S]*?-(\d+)')
kjs = re.findall(regex_kjs_css, css_html)
kjs = {i[0]:int(i[1]) for i in kjs}
# 根据偏移量排序
temp = sorted(kjs.items(), key=lambda x:x[1])
# 将class属性与其表示的数字组成字典
kjs = {temp[i][0]:number[i] for i in range(10)}

# pyquery对象
doc = pyquery.PyQuery(html)
# 先拿出评论的HTML,在用正则匹配
regex = r'\d|kj-\w{4}'
review_html = doc('#reviewCount').html()
temp = re.findall(regex, review_html)
# 将匹配出的结果的class属性替换为相应的数字
for n,i in enumerate(temp):
    if i != '1':
        temp[n] = kjs[i]
# 拼接结果,转化为整型数字
review = int(''.join(temp))
print('评论数:', review)
# 先拿出评论的HTML,在用正则匹配
avgprice_html = doc('#avgPriceTitle').html()
temp = re.findall(regex, avgprice_html)
for n,i in enumerate(temp):
    if i != '1':
        temp[n] = kjs[i]
avgprice = int(''.join(temp))
print('平均价格:', avgprice)
# 先拿出口味评分的HTML,在用正则匹配
kouwei_html = doc('#comment_score .item:first-child').html()
temp = re.findall(regex, kouwei_html)
for n,i in enumerate(temp):
    if i != '1':
        temp[n] = kjs[i]
kouwei = '.'.join(temp)
print('口味评分:', kouwei)
# 先拿出环境评分的HTML,在用正则匹配 
huanjing_html = doc('#comment_score .item:nth-child(2)').html()
temp = re.findall(regex, huanjing_html)
for n,i in enumerate(temp):
    if i != '1':
        temp[n] = kjs[i]
huanjing = '.'.join(temp)
print('环境评分', huanjing) 
# 先拿出服务评分的HTML,在用正则匹配
fuwu_html = doc('#comment_score .item:nth-child(2)').html()
temp = re.findall(regex, fuwu_html)
for n,i in enumerate(temp):
    if i != '1':
        temp[n] = kjs[i]
fuwu = '.'.join(temp)
print('服务评分', fuwu) 
# 先拿出电话的HTML,在用正则匹配
tel_html = doc('.expand-info.tel').html()
temp = re.findall(regex, tel_html)
for n,i in enumerate(temp):
    if i != '1':
        temp[n] = kjs[i]
temp.insert(3, '-')
tel = ''.join(temp)
print('电话', tel) 

因为需要拿到文本1和span标签的class属性,用pyquery暂时不知道怎么拿到数据,主要是还要顺序。所以我采用pyquery拿到评论的一段代码,再用正则提取出数据。在测试的时候,如果两次运行间隔小的话,会跳出验证码。拿到这些数据并不需要登陆,所以可以直接换代理。

2018-11-27更新:
1、今天去看昨天的代码,这是我写的代码吗,我怎么看不懂。于是只能重新理了一下思路,加了注释。
2、今天如果不加cookie就访问不了,不知道为什么

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值