爬取大众点评之初步试探

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

而今天的网站的反爬机制是目前我见过的最有水平的,网址: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: Mon Nov 26 14:55:02 2018
python: Anaconda 3.6.5
author: kanade
email: [email protected]
"""
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
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_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')
svg_kj_url = re.search(regex_kj_svg, css_html).group(1)
svg_kj_url = 'http:' + svg_kj_url

svg_kj_resp = requests.get(svg_kj_url)
svg_kj_html = svg_kj_resp.text
number = re.search(r'\d{10}', svg_kj_html).group()


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])

kjs = {temp[i][0]:number[i] for i in range(10)}


#x = svg_kj_doc('text').attr.x
#x = [int(i) for i in x.split(' ')]

doc = pyquery.PyQuery(html)

regex = r'\d|kj-\w{4}'
review_html = doc('#reviewCount').html()
temp = re.findall(regex, review_html)
for n,i in enumerate(temp):
    if i != '1':
        temp[n] = kjs[i]
review = int(''.join(temp))
print('评论数:', review)

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)

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)
    
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) 

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) 

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拿到评论的一段代码,再用正则提取出数据。在测试的时候,如果两次运行间隔小的话,会跳出验证码。拿到这些数据并不需要登陆,所以可以直接换代理。

猜你喜欢

转载自blog.csdn.net/Qwertyuiop2016/article/details/84554517