【Python】Mac環境下爬取國內Android應用市場指定應用下載量

Mac環境Python配置

  1. 安轉最新版 Python 3.6.4Mac OS X 64-bit/32-bit installer

  2. 安裝最新MacPyCharm 2017.3.3

  3. 打開PyCharm,新建Project,新建Python File
    Python File

  4. 點擊File/ Default Settings/ Project Interpreter/ 選擇你當前的項目,然後選擇”+”號(Install)
    Install

  5. 搜索並安裝用於網頁解析庫BeautifulSoup的bs4和beautifulsoup4、HTTP庫requests網頁解析庫lxml以及用於正則表達式re,關於正則表達式入門可以參考唯心不易博主的『Python 正則表達式入門(初級篇)

應用搜索

  1. 使用Chrome瀏覽器打開安卓應用市場頁面,這裏用百度手機助手舉例,搜索欄中輸入目標應用關鍵詞,如『123』。

  2. 跳轉後觀察地址欄:
    http://shouji.baidu.com/s?wd=123&data_type=app&f=header_software%40input,其實我們只需要http://shouji.baidu.com/s?wd=123,而末尾的『123』就是剛纔搜索的關鍵詞。
    Search123

  3. 若只更改『123』爲『CSDN』,頁面就會跳轉到『CSDN』的搜索結果:http://shouji.baidu.com/s?wd=CSDN,所以通過修改 = 號後面的變量(部分網站是 search/ 或 app/ ),我們可以快速獲取相關搜索頁面,這個變量我們取名爲AppName
    SearchCSDN

  4. 最終,我們的搜索網址searchUrl爲固定值http://shouji.baidu.com/s?wd= + AppName。部分網站鏈接稍微複雜,多搜索幾次,找到不變的部分,修改剩餘部分即可。

  5. 有了網址和關鍵詞,我們可以初步編寫腳本實現搜索功能:

#引入相關庫
from bs4 import BeautifulSoup
import requests
import re

# 手動爬蟲方法
def ManualCrawl():
    # 交互式輸入應用名作爲參數
    appName = input("please print app's name:")
    # 輸入完成後執行方法
    CaculateDownloadTimes(appName)

# 傳入搜索應用名
def CaculateDownloadTimes(AppName):
    # 完整的搜索鏈接
    searchUrl = 'http://shouji.baidu.com/s?wd=' + AppName
    # 獲取網頁html碼
    htmlData = requests.get(searchUrl)
    # 解析網頁
    soup = BeautifulSoup(htmlData.text, 'lxml')

信息提取

  1. 搜索結果有了,怎麼對結果進行分析呢?右擊頁面,點擊『檢查』,網頁會出現一片區域(即上文註釋中的html代碼),像這樣:
    Check1

  2. 鼠標在紅框區域內上下移動,可以發現左邊會根據鼠標懸浮位置不同,高亮不同區域。我們找到第一個應用『CSDN』的名字所對應的html代碼塊:
    Check2

  3. 右擊代碼塊 -> Copy -> Copy selector,可以得到應用名在html中的路徑:”#doc > div.yui3-g > div > div > ul > li:nth-child(1) > div > div.info > div.top > a”,通過select方法我們可以得到第一個搜索結果的應用名firstRecordName :

firstRecordName = soup.select(
                'body > div:nth-of-type(2) > div:nth-of-type(2) > div > div > ul > li:nth-of-type(1) > div > div:nth-of-type(2) > div:nth-of-type(1)')[
                0].get_text().encode('latin1').decode('utf-8').strip()
print('firstRecordName: ' + firstRecordName)

有以下幾點需要注意:
- nth-child 要替換成 nth-of-type
- 可以發現路徑不盡相同,因爲完全用 Copy selector 的路徑可能會發生莫名的錯誤,這時候就需要手寫路徑:一般從 body 開始,然後若< a b >內的前半段a唯一,則直接寫a即可;若不唯一,要寫出其序列n,從1開始: a:nth-of-type(n)
- get_text方法是將selectsoup[0]轉換成str格式,有些網站需要先用 latin1 編碼,再用 utf-8 解碼才能正常顯示。

示例代碼

from bs4 import BeautifulSoup
import requests
import re

# 網上說可以繞過反爬蟲,但是加了沒啥效果,應用寶依舊顯示無相關搜索內容
headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/537.36 (KHTML, like Gecko)'}
# 傳入安卓市場名、搜索應用名
def DownloadTimes(MarketName, AppName):
    # 木螞蟻
    if MarketName == 'mumayi' or MarketName == '木螞蟻' or MarketName == 'ant':
        searchUrl = 'http://s.mumayi.com/index.php?q=' + AppName
        # 獲取網頁源碼
        htmlData = requests.get(searchUrl, headers=headers)
        # print('content:' + htmlData.text)
        # 解析網頁
        soup = BeautifulSoup(htmlData.text, 'lxml')
        # 獲取應用名字
        try:
            firstRecordName = soup.select(
                '#allbody > div.main960.pos_rel > div.w670.fl > div:nth-of-type(2) > ul.applist > li:nth-of-type(1) > h3 > a:nth-of-type(1)')[
                0].get_text().strip().split()[0]
            # print('firstRecordName: ' + firstRecordName)
            # 獲取下載量
            try:
                times = soup.select(
                    '#allbody > div.main960.pos_rel > div.w670.fl > div:nth-of-type(2) > ul.applist > li:nth-of-type(1) > a.agray > ul > li.num')[
                            0].get_text()[5:-1]     #[x:-y]表示: 切除掉從左往右x個字符,同時切除掉從右往左y個字符

                print("'" + firstRecordName + "'" + "在'木螞蟻'的下載量: " + times)
            except:
                print(MarketName + ' search error')

        except:
            print("在'木螞蟻'中未找到與'" + AppName + "'相關的內容")
    # *****************需要在獲取下載量之前判斷搜索結果*****************
    # 樂商店
    elif MarketName == 'lenovomm' or MarketName == '樂商店' or MarketName == 'le':
        searchUrl = 'http://www.lenovomm.com/search/index.html?q=' + AppName
        # 獲取網頁源碼
        htmlData = requests.get(searchUrl, headers=headers, verify=False)
        # print('content:' + htmlData.text)
        # 解析網頁
        soup = BeautifulSoup(htmlData.text, 'lxml')

        try:
            # 獲取第一個搜索內容的名字
            tip = soup.select('body > div.w1000.bcenter > p')[0].get_text()
            # print(tip)
            if tip == '抱歉,沒有找到相關應用。':
                print("'樂商店'沒有找到相關應用")
                # 終止程序
                quit()
            firstRecordName = soup.select(
                'body > div.w1000.bcenter > div.border1.h-100.boxShadow.fl.searchAppsBox > ul > li:nth-of-type(1) > div.appDetails > p.f16.ff-wryh.appName > a')[
                0].get_text().strip()
            # print(firstRecordName)
            # 獲取下載量
            try:
                times = soup.select(
                    'body > div.w1000.bcenter > div.border1.h-100.boxShadow.fl.searchAppsBox > ul > li:nth-of-type(1) > div.appInfo.tcenter.pr > p:nth-of-type(1) > span')[
                            0].get_text()[:-3]

                print("'" + firstRecordName + "'" + "在'樂商店'的下載量: " + times)
            except:
                print(MarketName + ' search error')

        except:
            print("在'樂商店'中未找到與'" + AppName + "'相關的內容")
    # *******************需要latin1、utf-8解碼*******************
    # 智匯雲
    elif MarketName == 'zhihuiyun' or MarketName == '智匯雲' or MarketName == 'huawei':
        searchUrl = 'http://app.hicloud.com/search/' + AppName
        # 獲取網頁源碼
        htmlData = requests.get(searchUrl, headers=headers)
        # 解析網頁
        soup = BeautifulSoup(htmlData.text, 'lxml')
        # 獲取第一個搜索內容的名字
        try:
            firstRecordName = soup.select(
                'body > div.lay-body > div.lay-main > div.lay-left.corner > div > div > div:nth-of-type(2) > div.game-info.whole > h4 > a')[
                0].get_text().encode('latin1').decode('utf-8').strip()
            # print(firstRecordName)

            try:
                times = soup.select(
                    'body > div.lay-body > div.lay-main > div.lay-left.corner > div > div > div:nth-of-type(2) > div.game-info.whole > div.app-btn > span')[
                    0].get_text().encode('latin1').decode('utf-8')

                times = times.split('次')[0].split(':')[1]
                print("'" + firstRecordName + "'" + "在'智匯雲'的下載量: " + times)
            except:
                print(MarketName + ' search error')

        except:
            print("在'智匯雲'中未找到與'" + AppName + "'相關的內容")
    # ********************需要跳轉2級頁面獲取下載量********************
    # 當貝
    elif MarketName == 'dangbei' or MarketName == '當貝':
        searchUrl = 'http://www.dangbei.com/app/plus/search.php?kwtype=0&q=' + AppName

        # 獲取網頁源碼
        htmlData = requests.get(searchUrl, headers=headers)
        # print('content:' + htmlData.text)
        # 解析網頁
        soup = BeautifulSoup(htmlData.text, 'lxml')

        try:
            # 獲取第一個搜索內容的名字
            firstRecordName = soup.select('#softList > li:nth-of-type(1) > div > div.softInfo > p.title > a')[
                0].get_text().strip()
            # 正則表達式找出擡頭與紅色關鍵詞之間的跳轉鏈接文本
            # 正則表達式的運用可以參考這篇博客: https://www.cnblogs.com/chuxiuhong/p/5885073.html
            pattern0 = re.compile(r'<a href=".+' + "<font color='red'>")
            linkUrls = pattern0.findall(htmlData.text)
            # print(linkUrls[0])
            # 獲取二級頁面鏈接
            linkUrl = 'http://www.dangbei.com' + linkUrls[0].split()[1].split('"')[1]
            # print(linkUrl)

            # 進入二級頁面,以獲取下載量
            htmlData = requests.get(linkUrl, headers=headers)
            soup = BeautifulSoup(htmlData.text, 'lxml')
            # 獲取下載量
            try:
                times = soup.select(
                    '#softAbs > div.info > p:nth-of-type(4) > span.lInfo')[
                    0].get_text().encode('latin1').decode('utf-8').strip()

                times = times.split(':')[1]
                print("'" + firstRecordName + "'" + "在'當貝'的下載量:" + times)
            except:
                print(MarketName + ' search error')

        except:
            print("在'當貝'中未找到與'" + AppName + "'相關的內容")
    # 輸入有誤,重新輸入市場名
    else:
        print("input market's name error")
        ManualCrawl()

# 手動輸入
def ManualCrawl():
    marketName = input("please print market's name:")
    appName = input("please print app's name:")
    # 輸入完成後執行方法
    DownloadTimes(marketName, appName)

# 各類數組
marketsArr = ['木螞蟻', '樂商店', '智匯雲', '當貝']
appsArr = ['A', '天', '3', '心']
exampleAppName = ['QQ', '微信', '王者榮耀', '抖音', '愛奇藝', '優酷']

# 自動檢測, 以市場區分
def AutoCrawlUponMarkets():
    # 設置計數器
    count = 0
    for marketName in marketsArr:
        for appName in appsArr:
            count = count + 1
            DownloadTimes(marketName, appName)
            if count >= len(appsArr):
                print('------------------------------------------------------------')
                count = 0

# 自動檢測, 以應用名區分
def AutoCrawlUponApps():
    # 設置計數器
    count = 0
    for appName in exampleAppName:
        for marketName in marketsArr:
            count = count + 1
            DownloadTimes(marketName, appName)
            if count >= len(marketsArr):
                print('------------------------------------------------------------')
                count = 0

# 更換方法名後Run
AutoCrawlUponApps()

Git下載

包括”百度”、”360”等18個Android手機、TV應用市場。
DTHunter

存在的問題

樂視、應用寶、91、Google Play皆顯示搜索失敗。
若有建議或意見,歡迎大家與我交流!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章