爬取大众点评之获取商家地址

昨天爬取大众点评的文章

昨天试探性的爬取了大众点评的数字信息,但一般我们获取的数据中,不止是这些数字信息。在基本信息里面,地址也是一个很重要的数据。于是今天尝试一下怎么获取地址。

思路和数字是一样的,概括就是,通过css文件里的偏移量找到class属性和svg文件中的汉字的对应关系。唯一的不同在于数字的svg文件只有一行10个数字,而地址中的svg文件包含200多个汉字。

地址的class属性大部分是以bi-开头的(部分是数字以kj-开头),点击地址中的字的html代码,如下图:在这里插入图片描述看到右边有css,背景图片就是avg文件。网页打开如下图:
在这里插入图片描述
首先通过正则拿到所有文字。然后从css文件匹配出所有bi-开头的属性,这里在处理上,我本来想根据x、y偏移量排序,这样不是就和上面的文字位置相对应了,但是最后发现提取出的文字有253个,而bi-开头的class属性却只有246个。也就是说有些文字并不会用到,只是起迷惑的效果。

这样就只能找x、y偏移量的规律,发现x坐标除以14就是这一行的第x/14个字(从0开始),y坐标减去7除以30就是第几行了(从0开始)。而用正则匹配出的所有汉字是8个字符串的列表。那么我可以直接通过第几行第几列作为索引找到它对应的汉字。比如四这个汉字就可以通过L[0][2]得到(字符串也是可以直接中括号取值的)。这就简单多了。

下一步这需要从网页中拿到地址的文字和class属性了,想这样的。

<span class="item" itemprop="street-address" id="address">
  口
  <span class="bi-UcC4"></span>
  <span class="bi-MxyS"></span>
  <span class="bi-sJpz"></span>
  <span class="bi-0k4r"></span>
  <span class="kj-QXcp"></span>
  <span class="bi-DZXt"></span>
  (
  <span class="bi-r15F"></span>
  近
  <span class="bi-3ZYO"></span>
  )
  </span>

这样的网页结构我使用正则和pyquery都无法拿到包含顺序的文字和class属性(哪位大神懂的话还请留言指教),只能想到另一个工具xpath了。
而xpath提取却很简单,只需要这样:

//span[@id="address"]/text() | //span[@id="address"]/span/@class

这样匹配出来的正好是要的结果,将css属性换成前面拿到的密码本解密拼接就得到了地址的信息。

代码如下:

# -*- coding: utf-8 -*-
"""
date: Tue Nov 27 09:48:08 2018
python: Anaconda 3.6.5
author: kanade
email: [email protected]
"""
import requests
import re

class DianPingSpider(object):
    '''
    获取商家信息
    '''
    def __init__(self, url='http://www.dianping.com/shop/586341'):
        html = self.get_index_html(url)
        css_html = self.get_css_html(html)
        # 数字密码本class属性的开头两个字母,比如kj
        numcb = re.search(r'id="reviewCount".*?>[\s\S]*?class="(\w\w)-\w{4}"', html).group(1)
        # 文字密码本class属性的开头两个字母
        charcb = re.search(r'id="address".*?>[\s\S]*?class="(\w\w)-\w{4}"', html).group(1)
        self.kjs = self.get_kjs(css_html, numcb)
        self.bis = self.get_bis(css_html, charcb)
              
    def get_index_html(self, url):
        '''
        获取初始网页
        '''
        headers = {
            'Host':'www.dianping.com',
            'User-Agent':'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.61 Safari/537.36'
         }
        resp = requests.get(url, headers=headers)
        print(resp.status_code)
        html = resp.text
        print(len(html))
        return html
        
    def get_css_html(self, html):
        '''
        获取css文件的内容
        '''
        # 从html中提取css文件的url
        regex = re.compile(r'(s3plus\.meituan\.net.*?)\"')
        css_url = re.search(regex, html).group(1)
        css_url = 'http://' + css_url
        # 得到css文件的内容
        resp = requests.get(css_url)
        css_html = resp.content.decode('utf-8')
        return css_html
        
    def get_kjs(self, css_html, numcb):
        '''
        获取kj开头的class属性对应显示的文字字典
        '''
        # 从css_html中提取kj的svg文件url
        regex = re.compile(r'\[class\^="%s-"\][\s\S]*?url\((.*?)\)'%numcb)
        svg_url = re.search(regex, css_html).group(1)
        if svg_url.startswith('//'):
            svg_url = 'http:' + svg_url
        # 得到svg文件内容
        resp = requests.get(svg_url)
        svg_html = resp.text
        # 从svg内容中提取10位数字
        number = re.search(r'\d{10}', svg_html).group()
        # 匹配出以kj-开头的class属性中的偏移量
        regex_kj = re.compile(r'\.(%s-\w{4})[\s\S]*?-(\d+)'%numcb)
        kjs = re.findall(regex_kj, css_html)
        # 根据偏移量排序
        kjs.sort(key=lambda x:int(x[1]))
        # 将class属性其真正显示的数字组成字典
        kjs = {i[0]:number[n] for n,i in enumerate(kjs)}
        
        return kjs
    
    def get_bis(self, css_html, charcb):
        '''
        获取bi开头的class属性真正显示的汉字
        '''
        # 提取相应的svg文件的url
        regex = re.compile(r'\[class\^="%s-"\][\s\S]*?url\((.*?)\)' % charcb)
        svg_url = re.search(regex, css_html).group(1)
        if svg_url.startswith('//'):
            svg_url = 'http:' + svg_url
        # 得到svg的内容
        resp = requests.get(svg_url)
        svg_html = resp.text
        # 提取svg文件中的所有文字信息
        regex = re.compile(r'<text[\s\S]*?>(\w+)<')
        content = regex.findall(svg_html)
        # 提取css_html中以bi-开头的class属性的偏移量
        regex = re.compile(r'(%s-\w{4})[\s\S]*?-(\d+)\.0px -(\d+)\.0px' % charcb)
        css = regex.findall(css_html)
        # 将偏移量转化为content内容的索引,不要问为什么,自己试试就知道了。
        # 规律而已,并将class属性和索引内容组成字典
        bis = {i[0]:content[int((int(i[2])-7)/30)][int(i[1])//14] for i in css}
        
        return bis
    
    

if __name__ == '__main__':
    dp = DianPingSpider()
    print(dp.bis)
    print(dp.kjs)
 

效果(键为css属性,值为属性实际显示出的文字):

{
 'jl-QPUK': '皇', 'jl-uZVj': '军', 'jl-BxqA': '迁', 'jl-Q0Yh': '生',
 'jl-MhJh': '秦', 'jl-1sFU': '旗', 'jl-2BCV': '场', 'jl-GNXA': '汾', 
 'jl-8SkE': '川', 'jl-MX6w': '桂', 'jl-Nuxg': '徽', 'jl-mDhK': '淮',
 'jl-y3lT': '放', 'jl-hQuw': '林', 'jl-jjiL': '坊', 'jl-GG4Z': '南', 
 'jl-lNEy': '衢', 'jl-HSIX': '公', 'jl-ccv6': '港', 'jl-5o0i': '黄',
 'jl-1NqB': '湖', 'jl-ZaS9': '内', 'jl-Embx': '二', 'jl-PZ1t': '河', 
 'jl-dtaB': '无', 'jl-Jzv1': '友', 'jl-MeGl': '安', 'jl-CEZ4': '乐',
 'jl-5pF7': '庄', 'jl-cm6w': '远', 'jl-pl8v': '衡', 'jl-WAY1': '沿', 
 'jl-mcI4': '家', 'jl-gPN9': '尔', 'jl-kI7q': '封', 'jl-fgKN': '藏',
 'jl-cvmd': '谊', 'jl-Ijek': '头', 'jl-JZxD': '莞', 'jl-Erz3': '保', 
 'jl-uuVH': '环', 'jl-p6ts': '宾', 'jl-66bs': '海', 'jl-ruFV': '上',
 'jl-al8V': '定', 'jl-i8Vt': '厦', 'jl-uTZe': '襄', 'jl-ktdy': '泉',
 'jl-fgD1': '春', 'jl-ihvS': '都', 'jl-AFcv': '名', 'jl-ttK5': '德', 
 'jl-1Xp6': '大', 'jl-Wt6K': '创', 'jl-RTFY': '山', 'jl-JbGA': '七', 
 'jl-md7e': '街', 'jl-Dvwh': '杭', 'jl-ncXJ': '教', 'jl-m7w5': '康', 
 'jl-6SNC': '盐', 'jl-BC4H': '重', 'jl-Mjwk': '市', 'jl-7nIE': '治', 
 'jl-Quzf': '深', 'jl-ubxa': '辽', 'jl-XhFl': '机', 'jl-3jN3': '云',
 'jl-R4R1': '农', 'jl-5VLj': '感', 'jl-ON6t': '进', 'jl-kV5O': '体',
 'jl-DlfW': '成', 'jl-9uni': '拥', 'jl-UHG1': '波', 'jl-YyWW': '站', 
 'jl-eod5': '兴', 'jl-PhSa': '肃', 'jl-kw2v': '新', 'jl-xKe8': '晋',
 'jl-KHZ6': '孝', 'jl-JMqW': '遵', 'jl-K7XY': '园', 'jl-Nbcr': '潍', 
 'jl-ZInM': '学', 'jl-KTMd': '烟', 'jl-UoqP': '夏', 'jl-jxjI': '交', 
 'jl-v53r': '业', 'jl-jRSW': '文', 'jl-QGW4': '中', 'jl-AJav': '龙', 
 'jl-FsZi': '凰', 'jl-8UhW': '吉', 'jl-oggh': '迎', 'jl-1kyQ': '昆',
 'jl-Ieip': '通', 'jl-Lud6': '汕', 'jl-l4OL': '健', 'jl-CRlk': '连',
 'jl-NPF7': '赣', 'jl-UvVw': '乌', 'jl-4zf1': '合', 'jl-VkOh': '六', 
 'jl-GmNO': '常', 'jl-V9CY': '银', 'jl-oAq3': '岳', 'jl-7k9R': '隆',
 'jl-I7mE': '五', 'jl-UDxl': '齐', 'jl-d844': '木', 'jl-V9y4': '鞍',
 'jl-XSfw': '育', 'jl-9yn1': '台', 'jl-a31R': '道', 'jl-Rt9k': '村', 
 'jl-tBgb': '湾', 'jl-GucB': '宜', 'jl-dz4K': '珠', 'jl-I9Uk': '东',
 'jl-AkkM': '惠', 'jl-5aVQ': '扬', 'jl-KiFX': '西', 'jl-8WJs': '和',
 'jl-hdLu': '区', 'jl-5DTk': '充', 'jl-nNgq': '镇', 'jl-Wipb': '建',
 'jl-4rnc': '锦', 'jl-aARz': '乡', 'jl-3iem': '陕', 'jl-cTLz': '淄', 
 'jl-pW2z': '宿', 'jl-zP85': '四', 'jl-an02': '福', 'jl-B28K': '绵', 
 'jl-TdwG': '祥', 'jl-EV7T': '化', 'jl-PZr5': '临', 'jl-7Xhj': '门', 
 'jl-fTL1': '江', 'jl-H9RC': '向', 'jl-kNxX': '信', 'jl-odxj': '红',
 'jl-qpWd': '甘', 'jl-lpCT': '岛', 'jl-Ojh1': '徐', 'jl-X0jp': '振', 
 'jl-fiFx': '曙', 'jl-l9pv': '工', 'jl-Cq50': '凤', 'jl-Jgk4': '石', 
 'jl-02WJ': '弄', 'jl-TvEt': '八', 'jl-ZJw2': '风', 'jl-aQL5': '金', 
 'jl-di6l': '香', 'jl-kQTA': '哈', 'jl-7V9Y': '济', 'jl-IyA3': '才',
 'jl-URwF': '华', 'jl-D2ja': '富', 'jl-lwtp': '省', 'jl-VlC7': '博',
 'jl-S8Xy': '梅', 'jl-vkRZ': '宁', 'jl-eD8e': '绍', 'jl-myTb': '光', 
 'jl-75k8': '庆', 'jl-KMpl': '团', 'jl-QCpk': '开', 'jl-psBD': '太', 
 'jl-rY7Y': '三', 'jl-0CAN': '沙', 'jl-8ODZ': '民', 'jl-M2Jo': '朝', 
 'jl-z14X': '湛', 'jl-oaGC': '义', 'jl-Uikz': '威', 'jl-0vd9': '清', 
 'jl-gPvB': '佛', 'jl-XtjU': '年', 'jl-aHZ4': '锡', 'jl-lzRo': '爱', 
 'jl-4UnI': '汉', 'jl-ULmU': '苏', 'jl-dufd': '楼', 'jl-bjnr': '鲁', 
 'jl-KxzU': '昌', 'jl-E2VB': '蒙', 'jl-NM4f': '古', 'jl-vU40': '天',
 'jl-3gbV': '州', 'jl-Kmwj': '关', 'jl-GRm3': '长', 'jl-FIc0': '谐',
 'jl-lXSW': '武', 'jl-jxay': '花', 'jl-e66k': '胜', 'jl-mHPS': '前', 
 'jl-J8j6': '茂', 'jl-bvni': '利', 'jl-MdN9': '嘉', 'jl-Dvmk': '京', 
 'jl-qCuY': '县', 'jl-ArGp': '韶', 'jl-G2dJ': '主', 'jl-1RGm': '疆', 
 'jl-vkFr': '黑', 'jl-vpEV': '郑', 'jl-MEPp': '心', 'jl-mrBy': '城', 
 'jl-OlhM': '津', 'jl-He0S': '府', 'jl-EoB7': '澳', 'jl-EDnM': '广', 
 'jl-vFcQ': '路', 'jl-kcQm': '设', 'jl-ck5T': '结', 'jl-QYLY': '明', 
 'jl-Vvan': '泰', 'jl-gio3': '青', 'jl-4KAv': '贵', 'jl-FbaI': '圳',
 'jl-aNtQ': '解', 'jl-Q71j': '号', 'jl-Tnsf': '十', 'jl-VAc5': '廊',
 'jl-olju': '九', 'jl-IBqO': '滨', 'jl-V9dW': '阳', 'jl-NnfK': '层', 
 'jl-2yXK': '人', 'jl-gOPY': '肇', 'jl-Lo0d': '永', 'jl-P3O9': '幸', 
 'jl-EL6Z': '源', 'jl-dUux': '冈', 'jl-1Shu': '一', 'jl-CLGR': '洛', 
 'jl-r2EN': '沈', 'jl-3cC9': '北', 'jl-SMQf': '平', 'jl-roWg': '邢',
 'jl-nop4': '浙', 'jl-LOsX': '温', 'jl-nQA7': '肥', 'zl-FhcV': '9', 
 'zl-Jvp2': '4', 'zl-gc5M': '2', 'zl-TohQ': '6', 'zl-htaN': '8',
 'zl-giSW': '3', 'zl-Bthl': '1', 'zl-tvPf': '7', 'zl-JTyc': '0', 
 'zl-Cg3x': '5'
 }

2018-11-29更新:今天发现商家和电话的css属性和标签居然和昨天不一样,一天变一次也太骚了。
已更新代码,可以应对每天的css改变。如果复制代码运行报错,可能已经失效,还请留言,我更新一下。

猜你喜欢

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