爬取大眾點評網(wǎng)的某城市美食店鋪數(shù)據(jù)

前言

某旅游城市在今年的十一期間再次火爆了一把疑务,城市的各種美食確實讓人垂涎欲滴暗甥。因此宝与,個人萌生了爬取該城市美食店鋪信息的想法焚廊。

一、確定爬取的url

1.首先用瀏覽器打開大眾點評網(wǎng)站www.dianping.com,然后點擊城市鏈接 习劫,再點擊美食鏈接進(jìn)入城市美食頁面咆瘟。

地址為:http://www.dianping.com/changsha/ch10/p0

2.改變頁碼,發(fā)現(xiàn)只有最后的px會發(fā)生變化榜聂。因此搞疗,只需要簡單構(gòu)建循環(huán)即可構(gòu)造url地址嗓蘑。

二须肆、開始抓取


1.直接抓取

查看網(wǎng)頁源代碼,發(fā)現(xiàn)可以搜索到網(wǎng)頁中存在的信息桩皿,因此基本可以確定是靜態(tài)網(wǎng)頁豌汇。


僅僅添加useragent直接進(jìn)行抓取后,發(fā)現(xiàn)出現(xiàn)了302狀態(tài)碼進(jìn)行了跳轉(zhuǎn)泄隔。這種情況下拒贱,應(yīng)該是需要增加請求頭信息。

2.構(gòu)造請求頭

1.查看網(wǎng)頁請求頭如下圖!


幾次刷新頁面后發(fā)現(xiàn),改變的數(shù)據(jù)僅僅是cookie逻澳,其它請求頭信息均為固定信息闸天。每次cookie均發(fā)生了改變,但是并沒有進(jìn)行兩次加載斜做,說明cookie信息應(yīng)該是本地生成的苞氮。cookie信息中有兩項信息會發(fā)生改變,


其中瓤逼,_lxsdk_s每次的改變很有規(guī)律笼吟,為最后兩位數(shù)加20或者21(具體是20還是21自己可以刷新幾次看一下)。但是另一項就比較復(fù)雜霸旗,看起來像是時間戳贷帮,但是嘗試后發(fā)現(xiàn)并不是。既然是本地生成的诱告,那么查看js應(yīng)該可以看到生成方式撵枢,個人嘗試了一下,無奈js能力有限精居。

但是诲侮,既然是本地生成的,那么我們可以進(jìn)行分析箱蟆,在瀏覽器和頁面內(nèi)容均沒有發(fā)生改變的情況下沟绪,改變的只有加載時間,我們可以大膽猜測這個數(shù)據(jù)還是和時間有關(guān)系空猜。而且response headers中還有一項Set-Cookie绽慈,里面的數(shù)據(jù)僅有時間在發(fā)生改變,基本可以確認(rèn)Hm_lpvt的改變是由時間引起的辈毯。


```python

17-Nov-2022 07:22:07 GMT 1605596557

17-Nov-2022 07:31:29 GMT 1605597742

17-Nov-2022 07:31:59 GMT 1605598305

```

如圖坝疼,查看幾次請求信息,最后觀察到每一次的Hm_lpvt的改變量為前次請求的時間間隔谆沃,其實也就是每次請求的Hm_lpvt信息钝凶,實際上是與上一次請求時間以及第一次請求時間相關(guān)的。

那么我們可以就此構(gòu)造cookie唁影「荩考慮到萬一某次請求出現(xiàn)問題方便再次構(gòu)造cookie,因此本人將構(gòu)造cookie需要的三項數(shù)據(jù)寫入txt文件据沈,每次進(jìn)行讀取和寫入操作哟沫。

```python

`def get_cookie():

? ? new = """fspop=test; _lxsdk_cuid=175a63b9732c8-045b6a35f56618-230346d-1fa400-175a63b9733c8; _lxsdk=175a6

? ? 3b9732c8-045b6a35f56618-230346d-1fa400-175a63b9733c8; _hc.v=500ca2b8-99ea-2512-79b9-579fd5ee6aab.1604811726; s_View

? ? Type=10; _lx_utm=utm_source%3DBaidu%26utm_medium%3Dorganic; cy=344; cye=changsha; _dp.ac.v=136a299d-309b-401a-93d4-

? ? 15565e9b3ec9; ua=dpuser_8849637005; ctu=6785bc81e314ca567cbe3eeabeca236ead9ce92b79a054a5ce52c4d63f36ce8d; Hm_lvt_602b80cf8079ae6591966cc70a3940e7=1604811726,1604835093,1604897708; _lxsdk_s=175ab5b8dc6-68-5d-822%7C%7C{}; Hm_lpvt_602b80cf8079ae6591966cc70a3940e7={}

? ? """

? ? with open("cookie.txt", 'r') as f:

? ? ? ? for line in f.readlines():

? ? ? ? ? ? time_cookie = json.loads(line)

? #將時間轉(zhuǎn)換成字符串

? ? timearray1 = time.strptime(time_cookie[1]['time'], "%Y-%m-%d %H:%M:%S")

? ? timearray0 = time.strptime(time_cookie[0]['time'], "%Y-%m-%d %H:%M:%S")

? #求前兩次的時間差

? ? timedelta = int(time.mktime(timearray1)) - int(time.mktime(timearray0))

? #構(gòu)造新的cookie需要的數(shù)據(jù)

? ? new_cookie = time_cookie[1]['cookie'] + timedelta

? ? timearray = time.localtime(int(time.time()))

? ? new_time = time.strftime("%Y-%m-%d %H:%M:%S", timearray)

? ? new_num = time_cookie[1]['num'] + 20

? #將新的cookie存入文件,并刪除第一項cookie

? ? time_cookie.append({'time':new_time, 'cookie':new_cookie,'num' : new_num})

? ? time_cookie.pop(0)

? ? cookie_json = json.dumps(time_cookie)

? ? with open("cookie.txt", 'w', ) as f:

? ? ? ? f.write(cookie_json)

? ? return new.format(str(new_num), str(new_cookie))

cookie =get_cookie()

```

構(gòu)造cookie后再次進(jìn)行請求锌介,果然嗜诀,請求成功猾警。

3.抓取信息

為了方便分析頁面內(nèi)容,因此將網(wǎng)頁保存在了本地隆敢。在此僅進(jìn)行了店鋪名稱发皿,店鋪得分,店鋪評論數(shù)和人均消費信息的抓取拂蝎。使用了pyquery和re模塊進(jìn)行網(wǎng)頁解析雳窟。

```python

with open('dianping.html', 'r', encoding='utf-8') as f:

? ? b = ''

? ? for line in f.readlines():

? ? ? ? b += line

doc = pq(b)

shop_items = doc('#shop-all-list')

shoplist =shop_items('li').items()

shops = []

for shop in shoplist:

? ? shop_dict = {}

? ? shop_name = re.search("<h4>(.*?)</h4>", str(shop), re.S).group(1)

? ? shop_star = re.search(r'<div class="star_score.*?">(.*?)</div>', str(shop), re.S).group(1)

? ? comments = re.search(r'"shop_iwant_review_click.*?<b>(.*?)</b>', str(shop), re.S).group(1)

? ? consume = re.search(r'class="mean-price".*?<b>(.*?)</b>', str(shop), re.S).group(1)

? ? shop_dict['shop_name'] = shop_name

? ? shop_dict['star'] = shop_star

? ? shop_dict['comments'] = comments

? ? shop_dict['consume'] = consume

? ? shops.append(shop_dict)

pprint.pprint(shops)

```

抓取的結(jié)果如圖所示:


發(fā)現(xiàn)評論數(shù)和人均消費的數(shù)字,除了‘1’以外均無法正常顯示匣屡。我們再次查看網(wǎng)頁元素封救。


4.信息解密

如上圖所示,網(wǎng)頁部分內(nèi)容進(jìn)行了加密捣作,無法正常爬取誉结。可以看到右上角位置有css的指示券躁,打開此鏈接惩坑,如下圖


其中有很多個字體文件,但是每個字體文件連接后面均說明了文件內(nèi)容也拜,我們需要的就是shopNum(店鋪數(shù)字)字體文件內(nèi)容以舒。

點擊此鏈接進(jìn)行下載,并使用fontTools進(jìn)行轉(zhuǎn)換后查看慢哈。

```python

from fontTools.ttLib import TTFont

font = TTFont(r'C:\Users\zhaohengcai\Downloads\fb3304fa.woff')

font.saveXML('local_font.xml')

```

查看內(nèi)容如圖


對照店鋪源代碼與網(wǎng)頁顯示內(nèi)容蔓钟,最后可以發(fā)現(xiàn)與上圖的藍(lán)色框內(nèi)的數(shù)據(jù)對應(yīng)。但是3-11對應(yīng)的網(wǎng)頁顯示內(nèi)容為3-9和0卵贱,我們需要自己進(jìn)行轉(zhuǎn)換滥沫。

```python

passage = """

? ? <GlyphID id="3" name="unie486"/>

? ? <GlyphID id="4" name="unieb23"/>

? ? <GlyphID id="5" name="unif298"/>

? ? <GlyphID id="6" name="unif240"/>

? ? <GlyphID id="7" name="unif001"/>

? ? <GlyphID id="8" name="unie997"/>

? ? <GlyphID id="9" name="unif59a"/>

? ? <GlyphID id="10" name="uniee87"/>

? ? <GlyphID id="11" name="unif876"/>

? ? """

keylist = re.findall('<GlyphID id="(.*?)" name="uni(.*?)"/>', passage)

num_dict = {}

for item in keylist:

? ? if int(item[0]) != 11:

? ? ? ? num_dict[r'\u' + item[1]] = int(item[0]) - 1

? ? else:

? ? ? ? num_dict[r'\u' + item[1]] = 0

print(num_dict)

返回去將解析網(wǎng)頁內(nèi)容的代碼進(jìn)行適當(dāng)修改,使用re進(jìn)行替換键俱,

num_key = {'\ue486': '2', '\ueb23': '3', '\uf298': '4', '\uf240': '5', '\uf001': '6', '\ue997': '7', '\uf59a': '8', '\uee87': '9', '\uf876': '0'}

with open('dianping.html', 'r', encoding='utf-8') as f:

? ? b = ''

? ? for line in f.readlines():

? ? ? ? b += line

doc = pq(b)

shop_items = doc('#shop-all-list')

shoplist =shop_items('li').items()

shops = []

for shop in shoplist:

? ? shop_dict = {}

? ? shop_name = re.search("<h4>(.*?)</h4>", str(shop), re.S).group(1)

? ? shop_star = re.search(r'<div class="star_score.*?">(.*?)</div>', str(shop), re.S).group(1)

? ? comments = re.search(r'"shop_iwant_review_click.*?<b>(.*?)</b>', str(shop), re.S).group(1)

? ? #將<svgmtsi class="shopNum">(.*?)</svgmtsi>的內(nèi)容進(jìn)行替換

? ? comments_key = re.findall('<svgmtsi class="shopNum">(.*?)</svgmtsi>', str(comments), re.S)

? ? for item in comments_key:

? ? ? ? pattern? = re.compile('<svgmtsi class="shopNum">{}</svgmtsi>'.format(item))

? ? ? ? comments = re.sub(pattern, num_key[item], comments)

? ? consume = re.search(r'class="mean-price".*?<b>(.*?)</b>', str(shop), re.S).group(1)

? ? #將<svgmtsi class="shopNum">(.*?)</svgmtsi>的內(nèi)容進(jìn)行替換

? ? consume_key = re.findall('<svgmtsi class="shopNum">(.*?)</svgmtsi>', str(consume), re.S)

? ? for item in consume_key:

? ? ? ? pattern = re.compile('<svgmtsi class="shopNum">{}</svgmtsi>'.format(item))

? ? ? ? consume= re.sub(pattern, num_key[item], consume)

? ? shop_dict['shop_name'] = shop_name

? ? shop_dict['star'] = shop_star

? ? shop_dict['comments'] = comments

? ? shop_dict['consume'] = consume

? ? shops.append(shop_dict)

pprint.pprint(shops)

```

運行結(jié)果如下:


三兰绣、將數(shù)據(jù)保存到數(shù)據(jù)庫

這樣我們的工作基本上就完成了,然后我們可以將數(shù)據(jù)存入數(shù)據(jù)庫:

```python

myclient = pymongo.MongoClient('localhost', 27017)

dianping_db = myclient['dianping']

dianping_col = dianping_db['dianping_food']

dianping_col.insert_one(shop_dict)

```

之后编振,我們將代碼進(jìn)行整合缀辩。

```python

import json

import pprint

import random

import re

import time

import pymongo

import requests

from pyquery import PyQuery as pq

user_agents = [

? ? 'Mozilla/5.0(Macintosh;Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36',

? ? 'Mozilla/5.0(Windows;U;MSIE 9.0;Windows NT 9.0;en-US)',

? ? 'Mozilla/5.0(Windows NT 6.1) AppleWebKit/537.2 (KHTML, like Gecko) Chrome/22.0.1216.0Safari/537.2',

? ? 'Mozilla/5.0(X11; Ubuntu;Linux i686;rv:15.0) Gecko/20100101 Firefox/15.0.1']

headers = {

? ? 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9',

? ? 'User-Agent': random.choice(user_agents),

? ? 'Connection': 'keep - alive',

? ? 'Host': 'www.dianping.com',

? ? 'Upgrade-Insecure-Requests': '1',

}

myclient = pymongo.MongoClient('localhost', 27017)

dianping_db = myclient['dianping']

dianping_col = dianping_db['dianping_food']

time_cookie = []

time_cookie.append({"time": "2020-11-17 08:07:36", "cookie": 1605571660, "num": 40})

time_cookie.append({"time": "2020-11-17 08:09:37", "cookie": 1605571671, "num": 60})

timestamp = int(time.time())

timearray = time.localtime(timestamp)

def get_cookie():

? ? new = """_lxsdk_cuid=175a63b9732c8-045b6a35f56618-230346d-1fa400-175a63b9733c8; _lxsdk=175a63b9732c8-045b6a35f56618

? ? -230346d-1fa400-175a63b9733c8; _hc.v=500ca2b8-99ea-2512-79b9-579fd5ee6aab.1604811726; s_ViewType=10; cy=344; cye=ch

? ? angsha; _dp.ac.v=136a299d-309b-401a-93d4-15565e9b3ec9; ua=dpuser_8849637005; ctu=6785bc81e314ca567cbe3eeabeca236ead

? ? 9ce92b79a054a5ce52c4d63f36ce8d; fspop=test; Hm_lvt_602b80cf8079ae6591966cc70a3940e7=1604986265,1605266096,16055297

? ? 94,1605571653; _lxsdk_s=175d38722cb-a29-e6e-f2b%7C%7C{}; Hm_lpvt_602b80cf8079ae6591966cc70a3940e7={}

? ? """

? ? with open("cookie.txt", 'r') as f:

? ? ? ? for line in f.readlines():

? ? ? ? ? ? time_cookie = json.loads(line)

? ? timearray1 = time.strptime(time_cookie[1]['time'], "%Y-%m-%d %H:%M:%S")

? ? timearray0 = time.strptime(time_cookie[0]['time'], "%Y-%m-%d %H:%M:%S")

? ? timedelta = int(time.mktime(timearray1)) - int(time.mktime(timearray0))

? ? new_cookie = time_cookie[1]['cookie'] + timedelta

? ? timearray = time.localtime(int(time.time()))

? ? new_time = time.strftime("%Y-%m-%d %H:%M:%S", timearray)

? ? new_num = time_cookie[1]['num'] + 20

? ? time_cookie.append({'time': new_time, 'cookie': new_cookie, 'num': new_num})

? ? time_cookie.pop(0)

? ? cookie_json = json.dumps(time_cookie)

? ? with open("cookie.txt", 'w', ) as f:

? ? ? ? f.write(cookie_json)

? ? return new.format(str(new_num), str(new_cookie))

def get_page(url):

? ? a = get_cookie()

? ? new_cookie = {'Cookie': a}

? ? response = requests.get(url=url, headers=headers, cookies=new_cookie)

? ? with open('dianping.html', 'w', encoding='utf-8') as f:

? ? ? ? f.write(response.text)

? ? return response

def parse(response):

? ? doc = pq(response.text)

? ? shop_items = doc('#shop-all-list')

? ? shoplist =shop_items('li').items()

? ? shops = []

? ? num_key = {'\ue486': '2', '\ueb23': '3', '\uf298': '4', '\uf240': '5', '\uf001': '6', '\ue997': '7', '\uf59a': '8', '\uee87': '9', '\uf876': '0'}

? ? for shop in shoplist:

? ? ? ? shop_dict = {}

? ? ? ? shop_name = re.search("<h4>(.*?)</h4>", str(shop), re.S).group(1)

? ? ? ? shop_star = re.search(r'<div class="star_score.*?">(.*?)</div>', str(shop), re.S).group(1)

? ? ? ? comments = re.search(r'"shop_iwant_review_click.*?<b>(.*?)</b>', str(shop), re.S).group(1)

? ? ? ? comments_key = re.findall('<svgmtsi class="shopNum">(.*?)</svgmtsi>', str(comments), re.S)

? ? ? ? for item in comments_key:

? ? ? ? ? ? pattern = re.compile('<svgmtsi class="shopNum">{}</svgmtsi>'.format(item))

? ? ? ? ? ? comments = re.sub(pattern, num_key[item], comments)

? ? ? ? consume = re.search(r'class="mean-price".*?<b>(.*?)</b>', str(shop), re.S).group(1)

? ? ? ? consume_key = re.findall('<svgmtsi class="shopNum">(.*?)</svgmtsi>', str(consume), re.S)

? ? ? ? for item in consume_key:

? ? ? ? ? ? pattern = re.compile('<svgmtsi class="shopNum">{}</svgmtsi>'.format(item))

? ? ? ? ? ? consume = re.sub(pattern, num_key[item], consume)

? ? ? ? shop_dict['shop_name'] = shop_name

? ? ? ? shop_dict['star'] = shop_star

? ? ? ? shop_dict['comments'] = comments

? ? ? ? shop_dict['consume'] = consume

? ? ? ? dianping_col.insert_one(shop_dict)

? ? ? ? shops.append(shop_dict)

? ? pprint.pprint(shops)

def main():

? ? for page_num in range(10):

? ? ? ? print('-------------正在爬取第{}頁數(shù)據(jù)--------------'.format(page_num))

? ? ? ? url = 'http://www.dianping.com/changsha/ch10/p{}'.format(page_num)

? ? ? ? response = get_page(url)

? ? ? ? parse(response)

? ? ? ? time.sleep(3)

if __name__ == '__main__':

? ? main()

```

四、總結(jié)

一踪央、本次爬取的難點主要有兩個方面:

1.cookie的構(gòu)造臀玄;

2.店鋪信息的解密。

cookie的構(gòu)造杯瞻,對擅長js解密的人應(yīng)該比較簡單镐牺,不擅長js的話,只能像我一樣進(jìn)行分析魁莉。

店鋪信息的解密,有很多人都寫了,在此不再贅述旗唁。

二畦浓、關(guān)于后續(xù)爬取的思考

其實應(yīng)該講爬取的內(nèi)容存入mysql數(shù)據(jù)庫,方便后續(xù)爬取評論內(nèi)容的時候進(jìn)行關(guān)聯(lián)检疫。使用mongoDB不方便數(shù)據(jù)的關(guān)聯(lián)讶请。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市屎媳,隨后出現(xiàn)的幾起案子夺溢,更是在濱河造成了極大的恐慌,老刑警劉巖烛谊,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件风响,死亡現(xiàn)場離奇詭異,居然都是意外死亡丹禀,警方通過查閱死者的電腦和手機(jī)状勤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來双泪,“玉大人持搜,你說我怎么就攤上這事”好” “怎么了葫盼?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長村斟。 經(jīng)常有香客問我剪返,道長,這世上最難降的妖魔是什么邓梅? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任脱盲,我火速辦了婚禮,結(jié)果婚禮上日缨,老公的妹妹穿的比我還像新娘钱反。我一直安慰自己,他們只是感情好匣距,可當(dāng)我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布面哥。 她就那樣靜靜地躺著,像睡著了一般毅待。 火紅的嫁衣襯著肌膚如雪尚卫。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天尸红,我揣著相機(jī)與錄音吱涉,去河邊找鬼刹泄。 笑死,一個胖子當(dāng)著我的面吹牛怎爵,可吹牛的內(nèi)容都是我干的特石。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼鳖链,長吁一口氣:“原來是場噩夢啊……” “哼姆蘸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起芙委,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤逞敷,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后灌侣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體推捐,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年顶瞳,在試婚紗的時候發(fā)現(xiàn)自己被綠了玖姑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡慨菱,死狀恐怖焰络,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情符喝,我是刑警寧澤闪彼,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站协饲,受9級特大地震影響畏腕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜茉稠,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一描馅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧而线,春花似錦铭污、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至誓竿,卻和暖如春磅网,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背筷屡。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工涧偷, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留簸喂,地道東北人。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓嫂丙,卻偏偏與公主長得像娘赴,于是被迫代替她去往敵國和親规哲。 傳聞我的和親對象是個殘疾皇子跟啤,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內(nèi)容