1. 程式人生 > >爬取大眾點評之獲取商家地址

爬取大眾點評之獲取商家地址

昨天爬取大眾點評的文章

昨天試探性的爬取了大眾點評的數字資訊,但一般我們獲取的資料中,不止是這些數字資訊。在基本資訊裡面,地址也是一個很重要的資料。於是今天嘗試一下怎麼獲取地址。

思路和數字是一樣的,概括就是,通過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改變。如果複製程式碼執行報錯,可能已經失效,還請留言,我更新一下。