(數據科學學習手札47)基於Python的網絡數據採集實戰(2)

1、簡介html

  立刻大四了,最近在暑期實習,在數據挖掘的主業以外,也幫助同事作了不少網絡數據採集的內容,接下來的數篇文章就將一一羅列出來,來續寫幾個月前開的這個網絡數據採集實戰的坑。正則表達式

 

2、馬蜂窩評論數據採集實戰json

2.1 數據要求windows

  此次咱們須要採集的數據是知名旅遊網站馬蜂窩下重慶區域內全部景點的用戶評論數據,以下圖所示:瀏覽器

  思路是,先獲取全部景點的poi ID,即每個景點主頁url地址中的惟一數字:cookie

這一步和(數據科學學習手札33)基於Python的網絡數據採集實戰(1)中作法相似,即在下述界面:網絡

翻頁抓取對應每一個景點poi ID的部分便可:app

比較簡單,這裏再也不贅述,最終整理成數據框,景點名和poi ID一一對應。dom

  接着根據獲得的poi ID,再對每個景點下的評論數據分別進行採集,但和以前遇到的最簡單的靜態網頁不一樣,這裏的評論數據是有js控制的,即當咱們在景點頁面內點擊評論區塊的下一頁按鈕,界面會刷新並顯示下一頁的評論內容,但瀏覽器url欄中的url地址並沒有改變,這就須要用更深刻的方式來獲取評論區域數據的真實url地址。工具

2.2 目標url地址的獲取

  以洪崖洞頁面爲例,點擊頁面內的蜂蜂點評進入評論內容區域:

當咱們點擊評論數據區域下方的下一頁時,評論內容翻頁刷新,但瀏覽器地址欄中的url地址並無發生改變:

這時咱們就須要找到控制評論數據區域的真實請求地址,在瀏覽器中按下F12,打開開發者工具,點擊network項:

選擇JS,這時能夠發現下面並沒有內容,由於這裏只會記錄打開開發者工具後頁面內新增的內容,這時咱們點擊評論區域下方的後一頁按鈕,隨着界面內容的更新,下方network中隨即出現了以下內容:

這就是請求評論區域內容的真實url地址,點擊它,進入以下內容:

至此,咱們便找到了控制評論區域發起請求的真實地址和相關屬性,接下來咱們先提取一下這些內容中咱們須要的部分,爲正式的採集作好準備;

2.3 假裝瀏覽器

   要假裝瀏覽器,咱們須要將上圖中的Request Headers下除了cookies的內容複製下來,整理成一個叫作headers的字典以下(其中起關鍵做用的是User-Agent,其餘的能夠不記錄):

再將Request Headers下的cookies中由;分隔的內容一樣整理成一個叫作cookies的字典以下:

這兩個參數咱們會在requests包中的get方法中傳入,接下來咱們來觀察翻頁請求url的規律;

 

2.4 探索url規律

  咱們找到下列內容中紅圈指示的地方:

上面紅圈中的內容是當前評論區域發起請求的真實url地址,下面紅圈的內容是在當前url中的關鍵參數,很明顯,params是一個字典,其中poi_id顧名思義即爲當前景點(洪崖洞)對應的poi ID,page對應的則是當前評論內容所在的頁數,just_comment這個參數我觀察到在任何頁中都不變,這裏咱們讓它持續爲1便可。

 

2.5 測試

  瞭解到上述內容後,結合當前的url地址,能夠獲得下列替換規則:

http://pagelet.mafengwo.cn/poi/pagelet/poiCommentListApicallback=jQuery181042269431321668516_1534601996232&params=%7B%22poi_id%22%3A%226653%22%2C%22page%22%3A2%2C%22just_comment%22%3A1%7D&_=1534602025986

只須要控制紅色區域內容的替換,咱們便可實現對評論內容資源的請求,下面咱們來作個測試,這裏以解放碑(對應poi ID 1690)下第13頁評論爲例,按照上述規則,咱們將網址分紅素材和實時參數兩部分,下面的url即爲經過拼接最終獲得的url地址:

url_left = 'http://pagelet.mafengwo.cn/poi/pagelet/poiCommentListApi?callback=jQuery18109093089574473625_1532513669920&params=%7B%22poi_id%22%3A%22'
url_middle = '%22%2C%22page%22%3A'
url_right = '%2C%22just_comment%22%3A1%7D&_=1532513718986'

url = url_left+'1690'+url_middle+'13'+url_right

咱們在Python中進行測試,對上述url地址發起請求:

import requests


#設置請求頭文件
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36',
    'Accept':'*/*',
    'Accept-Encoding':'gzip, deflate',
    'Accept-Language':'zh-CN,zh;q=0.9',
    'Connection':'keep-alive',
    'Referer':'http://www.mafengwo.cn/poi/6653.html'
}

#設置cookies
cookie = {
    'PHPSESSID':'1nivct21bumab1adia6i7k1a82',
    'mfw_uuid':'5b583768-bf26-19ea-0187-96a00686bd4d',
    'uva':'s%3A78%3A%22a%3A3%3A%7Bs%3A2%3A%22lt%22%3Bi%3A1532508009%3Bs%3A10%3A%22last_refer%22%3Bs%3A6%3A%22direct%22%3Bs%3A5%3A%22rhost%22%3Bs%3A0%3A%22%22%3B%7D%22%3B',
    '__mfwurd':'a%3A3%3A%7Bs%3A6%3A%22f_time%22%3Bi%3A1532508009%3Bs%3A9%3A%22f_rdomain%22%3Bs%3A0%3A%22%22%3Bs%3A6%3A%22f_host%22%3Bs%3A3%3A%22www%22%3B%7D',
    '__mfwuuid':'5b583768-bf26-19ea-0187-96a00686bd4d',
    'UM_distinctid':'164d0987902bb2-0bb3f4e59cf01f-3e3d560e-1fa400-164d098790328c',
    'oad_n':'a%3A3%3A%7Bs%3A3%3A%22oid%22%3Bi%3A1029%3Bs%3A2%3A%22dm%22%3Bs%3A15%3A%22www.mafengwo.cn%22%3Bs%3A2%3A%22ft%22%3Bs%3A19%3A%222018-07-25+16%3A40%3A08%22%3B%7D',
    '__mfwlv':'1532508009',
    '__mfwvn':'1',
    '__mfwlt':'1532508103'

}
url_left = 'http://pagelet.mafengwo.cn/poi/pagelet/poiCommentListApi?callback=jQuery18109093089574473625_1532513669920&params=%7B%22poi_id%22%3A%22'
url_middle = '%22%2C%22page%22%3A'
url_right = '%2C%22just_comment%22%3A1%7D&_=1532513718986'

url = url_left+'1690'+url_middle+'13'+url_right
r = requests.get(url=url, headers=headers, cookies=cookie)

獲得網頁內容以下:

這裏的網頁內容還未通過轉碼,這裏咱們使用下述方式轉碼,並將\替換爲空字符:

'''對相應的網頁內容進行轉碼'''
html = r.content.decode('unicode-escape').replace('\\','')

獲得html爲:

能夠看到,須要的中文內容已經提取完畢,接下來咱們須要作的是對咱們感興趣的內容進行提取,,這裏咱們感興趣的是每條評論的文本內容、評分以及評論時間,這裏使用正則表達式來提取:

 

import re
from bs4 import BeautifulSoup

obj = BeautifulSoup(html,'lxml')

# # '''利用findAll定位目標標籤及其屬性並返回其字符形式結果'''
text = list(obj.findAll('p', {'class': "rev-txt"}))
star = list(obj.findAll('span'))
Time = list(obj.findAll('span', {'class': "time"}))

#將每一條評論對應的內容提取出來
control = 0
for m in range(len(star)):
    try:
        if 'star' in str(star[m]):
            '''設置不一樣的正則規則來提取目標內容'''
            print(re.findall('[0-5]+', str(star[m]))[0])
            print(re.sub('[a-zA-Z="\-<> \/\n\r]+', '', str(text[control])))
            print(re.findall('<.*?>(.*?)<.*?>',str(Time[control]))[0])
            control += 1
    except Exception as e:
        print('error')

經過上面的測試,咱們成功獲取到該測試頁內的所需內容,下面附上完整採集的代碼,只是加上一些錯誤處理機制、隨機暫停防ban機制和一些保存數據的內容:

 

2.6 完整的採集程序

  正式採集部分沿用前面測試中的思想,具體代碼以下:

'''這個腳本用於對JS腳本控制翻頁的動態網頁進行爬取'''

import requests
import time
import random
from bs4 import BeautifulSoup
import re
import json
import pandas as pd


#設置請求頭文件
headers = {
    'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.119 Safari/537.36',
    'Accept':'*/*',
    'Accept-Encoding':'gzip, deflate',
    'Accept-Language':'zh-CN,zh;q=0.9',
    'Connection':'keep-alive',
    'Referer':'http://www.mafengwo.cn/poi/6653.html'
}

#設置cookies
cookie = {
    'PHPSESSID':'1nivct21bumab1adia6i7k1a82',
    'mfw_uuid':'5b583768-bf26-19ea-0187-96a00686bd4d',
    'uva':'s%3A78%3A%22a%3A3%3A%7Bs%3A2%3A%22lt%22%3Bi%3A1532508009%3Bs%3A10%3A%22last_refer%22%3Bs%3A6%3A%22direct%22%3Bs%3A5%3A%22rhost%22%3Bs%3A0%3A%22%22%3B%7D%22%3B',
    '__mfwurd':'a%3A3%3A%7Bs%3A6%3A%22f_time%22%3Bi%3A1532508009%3Bs%3A9%3A%22f_rdomain%22%3Bs%3A0%3A%22%22%3Bs%3A6%3A%22f_host%22%3Bs%3A3%3A%22www%22%3B%7D',
    '__mfwuuid':'5b583768-bf26-19ea-0187-96a00686bd4d',
    'UM_distinctid':'164d0987902bb2-0bb3f4e59cf01f-3e3d560e-1fa400-164d098790328c',
    'oad_n':'a%3A3%3A%7Bs%3A3%3A%22oid%22%3Bi%3A1029%3Bs%3A2%3A%22dm%22%3Bs%3A15%3A%22www.mafengwo.cn%22%3Bs%3A2%3A%22ft%22%3Bs%3A19%3A%222018-07-25+16%3A40%3A08%22%3B%7D',
    '__mfwlv':'1532508009',
    '__mfwvn':'1',
    '__mfwlt':'1532508103'

}

'''JS腳本發起的真實的網址請求對應的網址內容模板(及除去幾個動態參數以外的死板的url內容)'''
url_left = 'http://pagelet.mafengwo.cn/poi/pagelet/poiCommentListApi?callback=jQuery18109093089574473625_1532513669920&params=%7B%22poi_id%22%3A%22'
url_middle = '%22%2C%22page%22%3A'
url_right = '%2C%22just_comment%22%3A1%7D&_=1532513718986'


data = pd.read_excel(r'C:\Users\windows\Desktop\summer_project\GIS\data\馬蜂窩重慶景點評論數據(2018-7-26採集)\chongqing_scene.xlsx')

'''讀入poi_id數據以在循環中進行url的構建'''

Q = {}
poi_id_list = []
scene_name = []
for key,value in zip(data['id'],data['景點名稱']):
    Q[str(key)] = str(value)
    poi_id_list.append(str(key))
    scene_name.append(value)


comment = []
S = []
id = []
t = []

count = 1
for i in poi_id_list:
    print('{}採集開始'.format(Q[i]))

    '''構造包含poi_id內容的url前半部份內容'''
    url_first = url_left+i+url_middle
    for j in range(1,10000):
        try:
            '''構造包含翻頁信息的完整url內容'''
            url_first = url_left + i + url_middle
            url = url_first + str(j) + url_right

            '''向構造好的真實網頁發起請求'''
            r = requests.get(url=url, headers=headers, cookies=cookie)

            '''對相應的網頁內容進行轉碼'''
            html = r.content.decode('unicode-escape')

            '''判斷當前景點全部有效評論頁面是否已被爬取完成'''
            if '暫無內容' in str(html):
                print('本景點評論數據已被爬完!')
                break
            else:
                '''將網頁內容中的單\替換成空'''
                html = html.replace('\\', '')
                '''利用bs4對網頁內容進行CSS解析'''
                obj = BeautifulSoup(html, 'lxml')

                # # '''利用findAll定位目標標籤及其屬性並返回其字符形式結果'''
                text = list(obj.findAll('p', {'class': "rev-txt"}))
                star = list(obj.findAll('span'))
                Time = list(obj.findAll('span', {'class': "time"}))

                '''設置一個複雜周密的錯誤處理機制以防止長時間爬蟲任務中可能出現的各類錯誤中斷任務主體'''
                control = 0
                for m in range(len(star)):
                    try:
                        if 'star' in str(star[m]):
                            '''設置不一樣的正則規則來提取目標內容'''
                            token = re.findall('[0-5]+', str(star[m]))[0]
                            comment.append(re.sub('[a-zA-Z="\-<> \/\n\r]+', '', str(text[control])))
                            t.append(re.findall('<.*?>(.*?)<.*?>',str(Time[control]))[0])
                            S.append(int(token))
                            id.append(i)
                            print('-'*100)
                            print('總第{}條評論被採集'.format(str(count)))
                            print('-' * 100)
                            count += 1
                            control += 1
                        else:
                            pass
                    except Exception as e:
                        pass

            '''設置隨機睡眠機制以防止被ban'''
            print('='*100)
            print('{}的'.format(Q[i]),'第{}頁被採集完'.format(str(j)))
            print('=' *100)
            time.sleep(random.randint(2,4))
        except Exception as e:
            pass
    print('{}採集結束'.format(Q[i]))



'''寫出數據到數據框'''
df = pd.DataFrame({'id':id,
                   'comment':comment,
                   'S':S,
                   'Time':t})

df.to_excel('raw_data.xlsx',index=False)

運行結果:

最終獲得的評論數據集格式以下:

 

  以上就是關於本文的所有內容,若有不解之處,望指出。

相關文章
相關標籤/搜索