Python爬蟲初體驗(yàn)

python不用多介紹啦,前幾天看新聞?wù)fpython在浙江都列入高考了,可見這門語言有多火。但是博主一直是秉承著需要什么就拿來用什么的想法,一直也沒怎么接觸python(之前實(shí)在沒遇到什么case push博主去擼擼python。

背景

最近偶然遇到這么一個(gè)case,博主所在的APM項(xiàng)目,非常依賴于不同機(jī)型不同iOS版本的測(cè)試,但是手頭的機(jī)器實(shí)在過少,所以一直使用Testin的測(cè)試服務(wù)。最近新版本的測(cè)試結(jié)果出來了,網(wǎng)站提供的下載入口長下面這樣:

設(shè)備結(jié)果列表.jpg

點(diǎn)擊列表的詳情進(jìn)去后才能找到下載日志的入口:


設(shè)備測(cè)試結(jié)果詳情.jpg

以前每次測(cè)試完成后都需要手動(dòng)每個(gè)設(shè)備點(diǎn)進(jìn)去下載每個(gè)日志,足足需要這樣重復(fù)操作去下載好幾十個(gè)日志,作為一個(gè)懶漢程序員實(shí)在不能忍受這么浪費(fèi)生命的事情:)

這不最近python這么火么,趁機(jī)上手?jǐn)]一次python爬蟲,希望執(zhí)行一個(gè)腳本就能下載全部的日志文件。

python入門的教程是在廖雪峰大神的博客看的,快速了解了下python的語法,腳本語言不用深究,能拿來干活就行。具體網(wǎng)上了解python爬蟲的正確姿勢(shì)的過程在這就不表了。

分析路徑

工欲善其事必先利其器,python爬蟲既然是和網(wǎng)絡(luò)打交道那charles是必不可少的,除此之外還有chrome。

我們倒著分析,打開charles,然后再瀏覽器中一步步點(diǎn)擊網(wǎng)頁,最終到達(dá)包含下載日志入口的設(shè)備詳情頁后,這里有個(gè)小技巧,我們看下載按鈕的名字叫“下載日志zip包”便可以在charles中(command + F)全局搜索這個(gè)關(guān)鍵字:

charles搜索下載按鈕.jpg

果然出現(xiàn)了一條結(jié)果,雙擊進(jìn)去。

下載按鈕網(wǎng)頁.jpg

我們看到這里包含了日志下載的鏈接,同時(shí)我們發(fā)現(xiàn)了這條post請(qǐng)求同時(shí)傳入了兩個(gè)參數(shù)adaptId和reportId。我們記住這個(gè)url。

再往回倒,我們是點(diǎn)擊詳情按鈕進(jìn)來這個(gè)頁面的,同樣的方法:


搜索詳情按鈕.jpg
詳情按鈕網(wǎng)頁.jpg

果然在這個(gè)列表這個(gè)頁面發(fā)現(xiàn)了列表中設(shè)備的adaptId和reportId,這個(gè)網(wǎng)頁的post請(qǐng)求包含了一系列篩選的參數(shù),在這里是adaptId和curPage從名字我們已經(jīng)能猜到參數(shù)的含義,我們記住這個(gè)url。

那么,現(xiàn)在只剩下adaptId了。

emmm..這時(shí)候在charles的全局搜索中搜索出的adaptId已經(jīng)包含了太多的結(jié)果,沒有參考價(jià)值。TestIn給我們發(fā)的郵件中有這個(gè):

在線報(bào)告鏈接.jpg

大膽猜測(cè)這個(gè)鏈接返回的結(jié)果中包含了adaptId,果然在charles的返回的結(jié)果中找到了這個(gè)參數(shù)。

尋找adaptId.jpg

注意看圖,這是一個(gè)302重定向的請(qǐng)求,第二條請(qǐng)求的結(jié)果才是最后的結(jié)果。

登錄

郵件中查看報(bào)告的鏈接是登錄賬號(hào)密碼后才能查看的?,F(xiàn)在的web服務(wù)器一般用cookie來標(biāo)識(shí)瀏覽器的請(qǐng)求,這里關(guān)于cookie的知識(shí)不過多介紹了,不了解的讀者自行g(shù)oogle。

這里博主陷入了一個(gè)坑,因?yàn)閏ookie的機(jī)制是在登錄的請(qǐng)求中,服務(wù)器校驗(yàn)賬號(hào)密碼后會(huì)在response的header中加入set-cookie的字段,把cookie放入其中,博主的思路是模擬登錄后,拿到這個(gè)set-cookie的字段然后再在后續(xù)的請(qǐng)求的request的header中加上cookie字段。然而理論是理論,實(shí)踐總是有點(diǎn)差異,這里這種方式死活行不通,我猜測(cè)這里web服務(wù)器的cookie的值設(shè)定有更多的邏輯,博主的方式一定是少設(shè)置了某個(gè)值。

被坑了半天后,博主發(fā)現(xiàn)其實(shí)python的requests庫已經(jīng)有接口幫忙做了cookie校驗(yàn)這個(gè)事情!類似下面這樣:

s = requests.session()
s.post(login_url, login_param)
# 后續(xù)用s(session)發(fā)起的請(qǐng)求自動(dòng)附帶cookie信息

這個(gè)教訓(xùn)告訴我們凡事先google,看有沒有現(xiàn)成的解決方案:)

登錄的接口該怎么找呢?? 這里有個(gè)技巧(別問博主第一次寫爬蟲為什么知道這么多技巧)

在chrome的登錄頁右鍵檢查,然后故意輸錯(cuò)賬號(hào)密碼,嘗試登錄。

登錄接口.jpg

如圖中紅框所示,點(diǎn)擊network,然后在底下就能找到登錄接口的url,同時(shí)能找到post請(qǐng)求的表單數(shù)據(jù)FormData。這樣子登錄的參數(shù)也出來了email和pwd。

所有的邏輯走通后下面這張圖就出來了:

爬蟲流程圖.jpg

解析HTML內(nèi)容&邏輯代碼

上面截圖的請(qǐng)求返回結(jié)果的HTML的紅圈中有我們需要的內(nèi)容,所以如何在html中解析內(nèi)容也是十分關(guān)鍵的,這里采用python自帶的Xpath庫,十分的好用,教程看這里。

下面兩行代碼通過//input[@id='adaptId']/@value這個(gè)字符串就取到了adaptId,可以說是十分簡(jiǎn)單粗暴了,這里不詳細(xì)展開Xpath的用法了。

# 2.獲取adaptid
report_res = s.get(report_url)
adaptId = etree.HTML(report_res.text).xpath("http://input[@id='adaptId']/@value")[0]

還剩下一下新建,刪除文件夾,解壓zip文件的操作,這里就不細(xì)說了,網(wǎng)絡(luò)庫用的是requests。這里把Testin的測(cè)試報(bào)告當(dāng)做參數(shù)傳入,這個(gè)鏈接每次測(cè)試都不一樣,其他的鏈接都要固定的。下面是最后的腳本代碼:

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# 需傳入一個(gè)參數(shù) >>>>>> 報(bào)告鏈接的url字符串
# 舉例 >>>>>> ./getTestinLog.py http://realauto.testin.cn/xxxxx

import os
import sys
import shutil
import urllib.request
import zipfile
import glob
import requests
from lxml import etree

# 一定要穿一個(gè)郵件中報(bào)告鏈接的參數(shù)url!
if len(sys.argv) < 2:
    print('請(qǐng)輸入郵件中報(bào)告的url作為參數(shù)!')
    sys.exit()

# 郵件中查看鏈接報(bào)告的url 用來獲取adaptId
report_url = sys.argv[1]

login_url = 'xxxxxx'

page_url = 'xxxxxx'

device_detail_url = 'xxxxxx'

login_param = {'email': 'xxx',
               'pwd': 'xxx'}

if os.path.isdir('./log_tmp'):
    shutil.rmtree('./log_tmp')
if os.path.isdir('./log_final'):
    shutil.rmtree('./log_final')
os.mkdir('./log_tmp')
os.mkdir('./log_final')

# 1.模擬登陸操作獲得cookie
s = requests.session()
s.post(login_url, login_param)

# 2.獲取adaptid
report_res = s.get(report_url)
adaptId = etree.HTML(report_res.text).xpath("http://input[@id='adaptId']/@value")[0]
print('>>>>>>adaptId ' + adaptId)

# 3.依次訪問5個(gè)有設(shè)備信息的列表
for page in range(5):
    page = page + 1
    print('>>>>>>this is page:', page)

    page_param = {'adaptId': adaptId, 'curPage': page}
    page_res = s.post(page_url, data=page_param)
    reportDetail = etree.HTML(page_res.text).xpath('//a[@style="background-color:#37bc9b;"]/@href')

    # sub_url是一個(gè)列表中每個(gè)設(shè)備條目的信息
    # 4. 從sub_url中獲取每個(gè)設(shè)備的下載鏈接
    for sub_url in reportDetail:
        reportId = sub_url.split('&')[2].split('=')[1]
        detail_param = {'adaptId': adaptId,
                        'reportId': reportId}
        detail_res = s.post(device_detail_url, data=detail_param)
        download_info = etree.HTML(detail_res.text).xpath("http://div[@class='right_btn']/a[2]/@href")[0]
        download_info_arr= download_info.split('\'')
        download_url = download_info_arr[1]
        download_name = download_info_arr[3]

        # 獲取到了下載的鏈接
        print('download:' + download_url + '    name:' + download_name)

        # 5.下載日志zip文件
        urllib.request.urlretrieve(download_url, download_name) 
        # 解壓
        with zipfile.ZipFile(download_name, "r") as zip_ref:
            zip_ref.extractall('./log_tmp')
        
        # 重命名
        for tmp_name in glob.glob('./log_tmp/*'):
            os.rename(tmp_name, './log_final/'+download_name.split('zip')[0]+'log')

        # 刪除zip文件
        os.remove(download_name)

# 刪除臨時(shí)文件夾
shutil.rmtree('./log_tmp')

運(yùn)行結(jié)果

直接上圖吧:


命令行截圖.jpg
日志結(jié)果.jpg

總結(jié)

博主認(rèn)為在python爬蟲中最重要的是想清楚你的目標(biāo)和達(dá)到這個(gè)目標(biāo)所需要的路徑,也就是用charles和chrome分析的過程,至與網(wǎng)絡(luò)操作和文件操作的接口現(xiàn)查python接口就好了,最重要的是分析的邏輯。

同時(shí)python現(xiàn)在的庫非常豐富,python用來處理數(shù)據(jù)也是很方便,善用python能自動(dòng)化很多東西。

雖然寫這個(gè)爬蟲腳本花了點(diǎn)時(shí)間,但是以后每次獲取日志就不需要手動(dòng)一個(gè)個(gè)去點(diǎn)擊下載了,博主認(rèn)為這是非常值得去做的一件事。日常的開發(fā)中如果有一些能自動(dòng)化的事情還是交給腳本去做,能大大的提高工作效率。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容