Python初學者之網絡爬蟲(二)

聲明:本文內容和涉及到的代碼僅限於我的學習,任何人不得做爲商業用途。轉載請附上此文章地址php

本篇文章Python初學者之網絡爬蟲的繼續,最新代碼已提交到https://github.com/octans/PythonPracticehtml

1. 上篇回顧

上篇文章Python初學者之網絡爬蟲中我從花椒的熱門推薦頁面入手,進而獲取到主播我的信息和對應的直播歷史視頻。python

首先看一下上一篇文章中對huajiao.com的主播和視頻的爬取成果:mysql

# getUserCount
10179
# getLiveCount
111574
到目前已收集了10179個主播信息,和這些主播的111574個視頻信息。這裏數據量小的緣由是我只收集了花椒熱門推薦下面的主播,這個頁面每次展現60個系統推薦的主播。
 

到目前爲止我新作了以下事情:git

  • 對MySql的讀寫操做進行了封裝
  • 編碼風格聽從PEP8
  • 爬取沃米優選網(http://video.51wom.com/)的主播信息
  • 爬取一下網(http://www.yixia.com/)的主播信息和視頻信息

其中對MySql的封裝代碼單獨放到了文件mysql.py下,作爲一個module使用,這個module雖然簡單,但已經實現了select,insert,delete等操做,對MySql封裝感興趣的同窗能夠參考, 但請不要用於生產環境。推薦去使用和閱讀數據庫類peewee。
接下來將繼續講述我在數據抓取上的開發經歷。github

2. 爬取的數據源和邏輯

最終目標:收集到各大直播平臺的主播信息和歷史播放記錄,進而對數據進行聚合分析。
當前已完成:對花椒網的數據收集。
沃米優選網(http://video.51wom.com/)是一個網紅數據聚合的網站,它收集了各個直播平臺(花椒,熊貓,秒拍,鬥魚,映客,一直播,美拍)的熱門主播信息。因此我但願能從它這裏獲取到各個平臺的熱門主播信息,以後拿着主播id去對應的直播平臺去爬取更詳細的信息。ajax

3. 爬取沃米優選網的主播列表頁

列表頁http://video.51wom.com/截圖以下:
qq20161211-2251292x
初看這是一個列表頁,而且底部有分頁連接,點擊分頁時觸發表單提交sql

 

3.1 分析結論和構思程序邏輯

當點擊底部分頁時,使用chrom開發者工具,看到有XHR請求以下截圖:
qq20161211-2318152x數據庫

從截圖和一些測試能夠分析出:json

  • a) 要請求第二頁之後的數據,須要將相應的cookie和csrf數據提交給網站;
  • b) 提交的方式是POST的」multipart/form-data」;
  • c) 提交的參數有_csrf, stage-name, platform, industry等;
  • d) 請求的返回結果是一個表格列表的html代碼;

對於cookie容易拿到,但_csrf如何獲取呢?
查看頁面源碼,發現網站在生成列表頁時已經將csrf的值寫入了表單;同一個csrf值在後續請求中能夠屢次使用

<input type="hidden" name="_csrf" value="aWF6ZGMzclc9EAwRK3Y4LhobNQo6eEAdWwA0IFd1ByUDNTgwClUEZw==">

由以上分析,程序的邏輯應該這樣,

  • a) 先請求主播列表的首頁,獲取到csrf值和cookie
  • b) 將csrf和cookie值保存,用於下次請求
  • c) 請求主播列表的第二頁,第三頁等
  • d) 將獲取到的表格列表的html代碼使用BeautifulSoup進行解析,遍歷每一個行,行裏的每一個列
  • e) 將獲取到的數據寫入mysql

3.2 python編碼獲取沃米優選網的主播信息

a) 構造基礎類class Website, 以後爲每一個網站創建一個class,繼承Website

  • 有些請求返回的是html代碼,類裏設置好html解析器;
  • 有些請求返回的是json串,基類裏設置好json的解析器;
  • 請求每一個網站時,須要設置不一樣的header,將header放在基類;
  • 對post的Content-Type:multipart/form-data方式進行函數封裝;
  • 對post的Content-Type: application/x-www-form-urlencoded方式分別進行函數封裝;
  • 這裏面儘可能把各類不一樣的請求方式寫成函數,而不使用type參數的形式,便於子類清晰的調用;

注意如下代碼爲了節省篇幅,不是完整代碼,也非PEP8代碼規範

class Website:
    ### 使用requests.session()可以自動處理cookies
    session = requests.session()

    ### 設置html解析器
    htmlParser = BeautifulSoup

    ### 設置json解析器
    jsonParser = json

    ### 設置headers
    headers = {
        'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/'
                      '54.0.2840.98 Safari/537.36'
    }
    ### 直接發起get請求
    def get(self, url, params=None):
        if params is None:
            params = {}
        return self.session.get(url, params=params, headers=self.headers)

    ### 發起get請求並返回解析後的html對象
    def get_html(self, url, params=None):
        r = self.get(url, params)
        return self.htmlParser(r.text, 'html.parser')

    ### 發起get請求並返回解析後的json對象
    def get_json(self, url, params=None):
        r = self.get(url, params)
        return self.jsonParser.loads(r.text)

    ### 發起post請求,以Content-Type:multipart/form-data方式
    def post_multi_part(self, url, params):
        kwargs = dict()
        for (k, v) in params.items():
            kwargs.setdefault(k, (None, v))
        r = self.session.post(url, files=kwargs, headers=self.headers)
        return self.htmlParser(r.text, "html.parser")

b) 構造class WoMiYouXuan, 封裝對網站沃米優選的請求

  • 方法first_kiss()用於第一次請求網站,獲取到csrf值由屬性self.csrf保存;
  • first_kiss()另外一個做用是獲取到cookie,雖然沒有顯示處理,由於requests.session()幫咱們處理了,自動獲取自動提交;
  • 注意在一個實例裏,只需調用一次first_kiss()便可,以後就能夠屢次調用其餘的頁面請求函數了;
  • csrf和cookie是由關聯的,網站會校驗,都要提交;
  • 方法parse_actor_list_page()是具體分析主播的列表html代碼,這是一個細緻活;
  • 方法spider_actors是骨架函數,循環訪問每一個分頁數據並將結果寫入mysql;
class WoMiYouXuan(Website):
    ### 發起post請求時須要將csrf發給網站
    csrf = ''
    def __init__(self):
        self.first_kiss()

    ### 首次訪問該網站獲取到csrf值並保存到self.csrf, 供其餘post請求直接使用
    def first_kiss(self):
        url = 'http://video.51wom.com/'
        html = self.get_html(url)
        self.csrf = html.find('meta', {'name': 'csrf-token'}).attrs['content']
    
    ### 從主播列表頁獲取主播信息
    def parse_actor_list_page(self, page=1):
        ### 構造參數->發起post請求
        url = 'http://video.51wom.com/media/' + str(page) + '.html'
        keys = ('_csrf', 'stage-name', 'platform', ' industry', 'price', 'follower_num', 'follower_area',
                'page', 'is_video_platform', 'sort_by_price', 'type_by_price')
        params = dict()
        for key in keys:
            params.setdefault(key, '')
        params['_csrf'] = self.csrf
        params['page'] = str(page)
        html = self.post_multi_part(url, params)
        
        ### 解析主播列表
        trs = html.find('div', {'id': 'table-list'}).table.findAll('tr')
        trs.pop(0)  # 去除標題行
        actor_list = list()
        for tr in trs:
            ### 後面太多了,有興趣的同窗去看源碼吧

    ### 骨架函數,循環訪問每一個分頁數據並將結果寫入mysql
    def spider_actors(self):
        page = 1
        tbl_actor = WMYXActor()
        while True:
            ret = self.parse_actor_list_page(page)
            for actor in ret['items']:
                actor['price_dict'] = json.dumps(actor['price_dict'])
                tbl_actor.insert(actor, replace=True)
            if ret['items_count'] * ret['page'] < ret['total']:
                page += 1
            else:
                break

方法parse_actor_list_page()具體分析主播列表的html代碼,這是一個細緻活;感覺一下代碼截圖
qq20161211-2353172x

3.3 知識點總結

a) 表單提交的POST方式
一般只提交一些kv數據時,使用application/x-www-form-urlencoded方式;
一般上傳文件時,使用multipart/form-data方式,但此種方式也是能夠提交kv類數據的,好比上面的獲取主播列表數據時就是使用此方式。
b) Python的網絡請求庫Requests
這個庫太好用了!而且可以對cookie自動處理,好比我在基類Website中的使用方式; 而且使用它構造multipart/form-data方式的post請求也很方便,好比方法Website::post_multi_part()
c) Python中使用正則匹配字符串中的整數,以下代碼:

avg_watched = tds[6].get_text(strip=True)  # 平均觀看人數
mode = re.compile(r'\d+')
tmp = mode.findall(avg_watched)

d) 使用try, except機制來實現相似php裏的isset(),以下代碼:

# 判斷是否有逗號,好比8,189
try:
   index = string.index(',')
   string = string.replace(',', '')
except ValueError:
   string = string

e) 必定要注意python中的’1’和1是不同的,須要你本身來作字符串和數字的類型轉換

4. 爬取秒拍網的主播和視頻信息

在沃米優選網拿到了各個直播平臺的主播id, 先實現對一下網(http://www.yixia.com/)的抓取,獲取對應的主播和視頻信息。
一下網的我的主頁地址爲http://www.yixia.com/u/uid, 這個uid就是主播id, 以下截圖:
qq20161212-0019252x

4.1 分析結論和構思程序邏輯

  • a) 在主播我的主頁可以拿到主播的我的信息,如頭像,暱稱,粉絲數等,還能拿到主播的視頻列表;
  • b) 視頻列表的加載方式是瀑布流方式,意味着走的是ajax接口;
  • c) 視頻列表接口返回的數據是html代碼,仍然須要用BeautifulSoup解析;
  • d) 請求視頻列表接口時須要提交suid參數,這個參數值須要用uid在主播我的頁獲取;

4.2 python編碼一下網的主播信息和視頻列表

  • 構造class YiXia(Website),
  • 方法parse_user_page()拿着uid去獲取主播我的信息;
  • 方法get_video_list()按分頁獲取視頻列表數據
class YiXia(Website):
    ### 訪問主播頁面,也是視頻列表頁,從該頁面獲取到suid和主播我的信息
    def parse_user_page(self, uid):
        print(self.__class__.__name__ + ':parse_user_page, uid=' + uid)
        user = dict()
        user['uid'] = uid
        url = 'http://www.yixia.com/u/' + uid
        bs = self.get_html(url)

        div = bs.find('div', {'class': 'box1'})
        user['nickname'] = div.h1.a.get_text(strip=True)  # 暱稱

        stat = div.ol.get_text(strip=True)
        stat = re.split('關注\||粉絲', stat)
        user['follow'] = stat[0].strip()  # 關注數
        user['followed'] = stat[1].strip()  # 粉絲數
        ### ------這裏省略不少代碼----

        return user

    ### AJAX請求視頻列表
    def get_video_list(self, suid, page=1):
        url = 'http://www.yixia.com/gu/u'
        payload = {
            'page': page,
            'suid': suid,
            'fen_type': 'channel'
        }
        json_obj = self.get_json(url, params=payload)
        msg = json_obj['msg']
        msg = BeautifulSoup(msg, 'html.parser')

        ### 解析視頻標題
        titles = list()
        ps = msg.findAll('p')
        for p in ps:
            titles.append(p.get_text(strip=True))  # 視頻標題

        ### 解析視頻贊和評論數
        stats = list()
        divs = msg.findAll('div', {'class': 'list clearfix'})
        for div in divs:
            tmp = div.ol.get_text(strip=True)
            tmp = re.split('贊|\|評論', tmp)
            stats.append(tmp)

        ### 解析視頻其餘數據
        videos = list()
        divs = msg.findAll('div', {'class': 'D_video'})
        for (k, div) in enumerate(divs):
            video = dict()
            video['scid'] = div.attrs['data-scid']

        ### ------這裏省略不少代碼------

        return videos

    ### 骨架函數,獲取每一個視頻的每一個分頁數據
    def spider_videos(self, suid, video_count):
        page = 1
        current = 0
        tbl_video = YiXiaVideo()
        while current < int(video_count):
            print('spider_videos: suid=' + suid + ', page=' + str(page))
            videos = self.get_video_list(suid, page)
            for video in videos:
                tbl_video.insert(video, replace=True)
            current += len(videos)
            page += 1
        return True

4.3 知識點總結

大部分仍是3.3裏的知識點,這裏重點注意字符串和整形,浮點型數字的轉換。好比粉絲數’2.3萬’是一個字符串,須要轉成浮點數2.3或者整數23000;再好比’8,189’須要轉成8189.

5. 程序結果

如下截圖爲採集到的一下網視頻數據:
qq20161212-0102152x

6. 知識點參考

這裏列出我記錄下來的參考連接:

qrcode_for_gh_61c6224cfae9_258

相關文章
相關標籤/搜索