點評網的反爬設置在咱們爬取點評網頁的時候給咱們形成了不小的障礙。在網頁上咱們看到的是這樣的css
網頁上能夠看到這家餐廳有1405條評論,人均387。但在分析頁面源碼的時候,咱們卻看不到網頁上的數字,看到是這樣的代碼node
點評網對數字作了處理,一些數字的信息像評論條數、人均、評分等都作了反爬保護。上面的網頁中評論條數是1405條,但在頁面源碼中,除了第一個數字1之外,後面的數字咱們看不到,都是一些像隨機編碼同樣的css class。python
若是咱們仔細分析這個css class,實際上是不難發現背後的原理的。git
經過開發者工具,咱們找到這個css的定義,能夠看到是下面這樣的數組
background-image屬性裏面是一個url,咱們在瀏覽器裏打開它,看到它的內容是瀏覽器
lc-mY1i 這個css class裏面是一個background屬性,定義了背景圖片偏移的位置。bash
因此點評網上顯示數字的原理就是經過設置不一樣的偏移位置,顯示背景圖片相應位置上的數字。咱們能夠想象背景圖片的前面有一個窗口,窗口的大小恰好夠顯示一個數字。窗口是固定不動的,背景圖片在後面移動,移動到不一樣的位置就能顯示這個位置上的數字。svg
進一步分析背景圖片,咱們能夠發現,這是一個SVG圖片,圖片中的數字能夠在svg的源碼中看到,以下函數
理解了原理後,咱們用代碼來實現一下解析的過程。工具
首先咱們從點評的網頁上找出css文件的url,代碼以下
def get_css():
url = "http://www.dianping.com/shanghai/ch10"
r = requests.get(url, headers=headers)
content = r.content.decode("utf-8")
matched = re.search(r'href="([^"]+svgtextcss[^"]+)"', content, re.M)
if not matched:
raise Exception("cannot find svgtextcss file")
css_url = matched.group(1)
css_url = fix_url(css_url)
return css_url
複製代碼
隨後咱們從css裏找到背景圖片的路徑,並獲取SVG圖片中的每一個數字
def get_svg(css_url):
r = requests.get(css_url, headers=headers)
content = r.content.decode("utf-8")
matched = re.search(r'span\[class\^="lc\-"\].*?background\-image: url\((.*?)\);', content)
if not matched:
raise Exception("cannot find svg file")
svg_url = matched.group(1)
svg_url = fix_url(svg_url)
r = requests.get(svg_url, headers=headers)
content = r.content.decode("utf-8")
matched = re.search(r'class="textStyle">(\d+)</text>', content)
if not matched:
raise Exception("cannot find digits")
digits = list(matched.group(1))
return digits
複製代碼
這個函數返回一個數組,數組的內容是SVG圖片中的全部數字。
對於點評網頁中的用css class表示的數字,咱們來解析一下css class和數字之間的對應關係
def get_class_offset(css_url):
r = requests.get(css_url, headers=headers)
content = r.content.decode("utf-8")
matched = re.findall(r'(\.[a-zA-Z0-9-]+)\{background:(\-\d+\.\d+)px', content)
result = {}
for item in matched:
css_class = item[0][1:]
offset = item[1]
result[css_class] = offset
return result
複製代碼
這個函數返回的是一個字典,它的key是css class的名字,value是css class對應的數字在背景圖片中的偏移量。
接下來,咱們以評論條數爲例,來獲取點評上一個頁面裏每家餐廳的評論條數。先定義函數,用於獲取評論條數
def get_review_num(page_url, class_offset, digits):
r = requests.get(page_url, headers=headers)
content = r.content.decode("utf-8")
root = etree.HTML(content)
shop_nodes = root.xpath('.//div[@id="shop-all-list"]/ul/li')
for shop_node in shop_nodes:
name_node = shop_node.xpath('.//div[@class="tit"]/a')[0]
name = name_node.attrib["title"]
review_num_node = shop_node.xpath('.//div[@class="comment"]/a[@class="review-num"]/b')[0]
num = 0
if review_num_node.text:
num = num * 10 + int(review_num_node.text)
for digit_node in review_num_node:
css_class = digit_node.attrib["class"]
offset = class_offset[css_class]
index = int((float(offset)+7)/-12)
digit = int(digits[index])
num = num * 10 + digit
last_digit = review_num_node[-1].tail
if last_digit:
num = num * 10 + int(last_digit)
print("restaurant: {}, review num: {}".format(name, num))
複製代碼
而後調用函數,爬一下頁面中每家餐廳的評論條數
css_url = get_css()
digits = get_svg(css_url)
class_offset = get_class_offset(css_url)
url = "http://www.dianping.com/shanghai/ch10/g116"
get_review_num(url, class_offset, digits)
複製代碼
運行代碼後,獲得以下的結果
restaurant: 1886汽車主題德國餐廳(環宇薈店), review num: 1021
restaurant: Mia Fringe迷芬奇餐廳&酒吧, review num: 152
restaurant: Oyster EXPO江月蠔庭西餐生蠔吧(世博源店), review num: 1405
restaurant: 寶萊納餐廳(陸家嘴店), review num: 7854
restaurant: Pizza Marzano瑪尚諾(港匯店), review num: 7527
restaurant: love&salt牛排館, review num: 86
restaurant: Da Ivo 意大利魔鏡餐廳, review num: 3497
restaurant: Mr Nice好好先生餐廳(月星環球港店), review num: 9052
restaurant: L'ATELIER de Joël Robuchon, review num: 2821 restaurant: Stone Sal 言鹽西餐廳, review num: 62 restaurant: 夏朵花園, review num: 3031 restaurant: 殼裏西餐廳Coquille Seafood Bistro, review num: 322 restaurant: ICHA Chateau Bar & Restaurant(酒吧創意料理), review num: 496 restaurant: 菲斯特花園西餐廳, review num: 655 restaurant: 寶麗嘉酒店Cafe Bellagio(寶麗嘉西餐廳), review num: 598 複製代碼
對照網頁上的數據,能夠看到,餐廳的評論條數都被正確的解析出來了。
本文已同步更新到公衆號【Python與數據分析】,歡迎關注~