欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

爬取大众点评之初步试探

程序员文章站 2022-03-02 19:38:37
...

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

上一篇: 动画

下一篇: 属性动画