很早以前,我用uiautomator+java實踐過Android APP自動化測試,不過今天要提的不是uiautomator,而是uiautomator2。聽起來uiautomator2像是uiautomator的升級版,但是這兩款框架僅僅是名字上比較相似,實際上沒有任何關聯。
項目地址:https://github.com/openatx/uiautomator2
框架 | 支持語言 | 特點 |
uiautomator | java | 谷歌開源,僅支持Android |
xiaocong/uiautomator | python | 開源,僅支持Android |
uiautomator2 | python | 開源,僅支持Android |
pip install uiautomator2
pip install -U weditor # 安裝weditor
python -m uiautomator2 init
初始化成功會出現如下提示
當PC或Linux服務器連接了多臺adb device的情況下,“python -m uiautomator2 init”默認初始化的是所有設備,若指定設備初始化,則需使用“--serial”參數:
python -m uiautomator2 init --serial $SERIAL # $SERIAL為手機序列號,可通過adb devices查看
執行“python -m uiautomator2 init”命令,會自動往手機上安裝一堆東西:
更多信息詳見:https://github.com/openatx/uiautomator2/wiki/Manual-Init
uiautomator2提供了3種連接方式
import uiautomator2 as u2
d=u2.connect('10.0.0.1') # alias for u2.connect_wifi('10.0.0.1')
print(d.info)
import uiautomator2 as u2
d=u2.connect('123456f') # alias for u2.connect_usb('123456f')
print(d.info)
import uiautomator2 as u2
d=u2.connect_adb_wifi("10.0.0.1:5555")
# 等同于
# + Shell: adb connect 10.0.0.1:5555
# + Python: u2.connect_usb("10.0.0.1:5555")
uiautomator2 screenshot test.jpg
uiautomator2 current
uiautomator2 uninstall <package-name> # 卸載一個包
uiautomator2 uninstall <package-name-1> <package-name-2> # 卸載多個包
uiautomator2 uninstall --all # 全部卸載
$ uiautomator2 stop com.example.app # 停止一個app
$ uiautomator2 stop --all # 停止所有的app
ui2支持 android 中 UiSelector 類中的所有定位方式,詳細可以查看官網:https://developer.android.com/reference/android/support/test/uiautomator/UiSelector,以下僅列出幾種常見的定位方式:
定位方式 | 描述 |
text | 通過文本定位 |
textMatches | 通過文本正則匹配定位 |
className | 通過類名定位 |
classNameMatches | 通過類名正則匹配定位 |
description | 通過desc屬性定位 |
descriptionMatches | 通過desc屬性正則匹配定位 |
resourceId | 通過resourceId定位 |
resourceIdMatches | 通過resourceId正則匹配定位 |
#查找類名為android.widget.ListView下的Bluetooth元素
d(className="android.widget.ListView").child(text="Bluetooth")
# 下面這兩種方式定位有點不準確,不建議使用
d(className="android.widget.ListView")\
.child_by_text("Bluetooth",allow_scroll_search=True)
d(className="android.widget.ListView").child_by_description("Bluetooth")
#查找與google同一級別,類名為android.widget.ImageView的元素
d(text="Google").sibling(className="android.widget.ImageView")
d(className="android.widget.ListView", resourceId="android:id/list") \
.child_by_text("Wi?Fi", className="android.widget.LinearLayout") \
.child(className="android.widget.Switch") \
.click()
d(A).left(B),# 選擇A左邊的B
d(A).right(B),# 選擇A右邊的B
d(A).up(B), #選擇A上邊的B
d(A).down(B),# 選擇A下邊的B
#選擇 WIFI 右邊的開關按鈕
d(text='Wi?Fi').right(resourceId='android:id/widget_frame')
Java uiautoamtor中默認不支持xpath,這是屬于ui2的擴展功能,速度會相比其它定位方式慢一些。在xpath定位中,ui2中的description 定位需要替換為content-desc,resourceId 需要替換為resource-id
# 只會返回一個元素,如果找不到元素,則會報XPathElementNotFoundError錯誤
# 如果找到多個元素,默認會返回第0個
d.xpath('//*[@resource-id="com.android.launcher3:id/icon"]')
# 如果返回的元素有多個,需要使用all()方法返回列表
# 使用all方法,當未找到元素時,不會報錯,會返回一個空列表
d.xpath('//*[@resource-id="com.android.launcher3:id/icon"]').all()
方法 | 描述 | 返回值 | 備注 |
exists() | 判斷元素是否存在 | True,Flase | @property |
info() | 返回元素的所有信息 | 字典 | @property |
get_text() | 返回元素文本 | 字符串 | |
set_text(text) | 設置元素文本 | None | |
clear_text() | 清空元素文本 | None | |
center() | 返回元素的中心點位置 | (x,y) | 基于整個屏幕的點 |
send_keys() | 發送文本 |
用法示例:
d(test="Settings").exists
d.exists(text='Wi?Fi',timeout=5)
d(text='Settings').click() # 單擊
d.double_click(x, y)
d.double_click(x, y, 0.1) # 雙擊默認時間間隔0.1s
d(text='Settings').longclick() # 長按
# "left", "right", "up", "down"
d(text="Settings").swipe("up", steps=20) # 元素向上滑動,步長20
d(text="Settings").swipe("down", steps=20) # 元素向下滑動
d(text="Settings").swipe("left", steps=20) # 元素向左滑動
d(text="Settings").swipe("right", steps=20) # 元素向右滑動
d(text="Settings").drag_to(text="Clock", duration=0.25) # 拖動到某個元素,時長0.25秒
d(text="Settings").drag_to(877,733) # 拖動到屏幕某個坐標點,duration時長默認0.5秒
d(text="Settings").pinch_in() # 縮小
d(text="Settings").pinch_out() # 放大
d(text="Settings").wait(timeout=3.0) # 等待元素出現
d(text='Settings').wait_gone(timeout=20) # 等待元素消失,返回True False,timout默認為全局設置的等待時間
# 垂直滾動到頁面頂部/橫向滾動到最左側
d(scrollable=True).scroll.toBeginning()
d(scrollable=True).scroll.horiz.toBeginning()
# 垂直滾動到頁面最底部/橫向滾動到最右側
d(scrollable=True).scroll.toEnd()
d(scrollable=True).scroll.horiz.toEnd()
# 垂直向后滾動到指定位置/橫向向右滾動到指定位置
d(scrollable=True).scroll.to(description="指定位置")
d(scrollable=True).scroll.horiz.to(description="指定位置")
# 垂直向前滾動(橫向同理)
d(scrollable=True).scroll.forward()
# 垂直向前滾動到指定位置(橫向同理)
d(scrollable=True).scroll.forward.to(description="指定位置")
# 滾動直到System元素出現
d(scrollable=True).scroll.to(text="System")
d.send_keys("test")
d.clear_text() # 清空輸入框
# 獲取toast,當沒有找到toast消息時,返回default內容
d.toast.get_message(timout=5,default='no toast')
# 清空toast緩存
d.toast.reset()
# 移除ANR的監控
d.watcher.remove("ANR")
# 移除所有的監控
d.watcher.remove()
# 開始后臺監控
d.watcher.start()
d.watcher.start(2.0) # 默認監控間隔2.0s
# 強制運行所有監控
d.watcher.run()
# 停止監控
d.watcher.stop()
# 停止并移除所有的監控,常用于初始化
d.watcher.reset()
更多api詳見:https://github.com/openatx/uiautomator2
python -m weditor
默認端口17310,訪問地址:http://localhost:17310/,手機連接PC(確保已開啟USB調試模式),點擊Connect連接設備,當Connect圖標變為綠色表示連接成功。
weditor提供了所操作即所得式的元素定位方式,當雙擊屏幕上的圖標或按鈕,weditor界面右側的Coding框會同步展現元素操作的代碼,同時手機界面也會相應同步切換頁面。
部分內容參考以下:
https://www.cnblogs.com/fnng/p/8486863.html
https://testerhome.com/topics/11357
https://blog.csdn.net/Master724/article/details/107962349?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522163394586216780265448858%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=163394586216780265448858&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-2-107962349.pc_search_ecpm_flag&utm_term=uiautomator2&spm=1018.2226.3001.4187
歡迎關注天善智能,我們是專注于商業智能BI,大數據,數據分析領域的垂直社區,學習,問答、求職一站式搞定!
前言
這篇文章的下篇終于寫出來了,上篇(數據采集)在兩個月前寫出來的:
Python數據采集分析告訴你為何上海二手房你都買不起(一)
為何下篇現在才寫出來呢?
有兩個原因:
1.兩個月前對python數據分析掌握的很差,那時候天天學爬蟲,pandas,numpy了解的也不多
2.人嘛,總是有惰性,喜歡一拖再拖
注:python可視化才剛入門,最后的可視化以后還可以完善一篇
環境
Python3.X
編輯器:Jupyter notebook
導入鏈家網爬取的二手房數據
import numpy as npimport pandas as pd
df=pd.read_excel('house_lianjia.xlsx')df
。。。。。。
一共2871條數據,其實上次爬了2w條數據,然后找不到了,那就拿測試爬的2871條作分析了
數據初窺
查看數據信息(包括每個字段數據類型),數據條數,文件大小等
df.info()
查看數據前五行:
df.head()
查看數據后五行:
df.tail()
我們可以看到‘梯戶比例’一欄好像都是暫無數據
我們可以專門將這一欄數據拿出來查看:
df['梯戶比例']
。。。。。。
顯示的都是暫無數據
為了確定是否所有都是暫無數據,我們可以使用如下辦法:
df[df['梯戶比例']=='暫無數據']#把暫無數據的數據提取出來
。。。。。。
取出了2871行,說明所有行的這欄都是暫無數據
刪除無效的數據
那么,這一欄對我們數據分析沒有意義,可以刪去:
del df['梯戶比例']#移除一欄
我們再次看一下最新的數據情況:
df
獲得數據描述性統計
df.describe()
我們可以看一下數據的簡單統計信息,從圖中可以看出每個欄位數據的個數,不重復數據個數,出現最多的數據及其出現的次數
注意到最后一欄,進門朝向數據只有13個,這說明了數據缺失很嚴重,不考慮缺失值補齊,直接刪除該欄數據:
del df['進門朝向'] #只有13個數據
再看一下數據情況:
df
可以通過這種方式快速查看各欄位名稱:
#查看列(字段)名稱df.columns
專門查看各字段類型:
#查看字段類型df.dtypes
發現缺失值
如果想看每個單元的缺失值,可以使用:
df.isnull()#哪些包含了缺失值
Flase表示沒有缺失
當然,這樣看起來非常難受,而且不直觀
查看各字段是否有缺失值:
df.isnull().any()#是否有缺失值
這樣,我們就能看到除了'房屋朝向'字段有缺失值,其他字段都沒有缺失值。
我們想看看‘房屋朝向’字段有多少缺失值:
df.isnull().sum()#每個里有多少個缺失值
顯示有13個缺失值
如果你對這個個數不敏感,我們可以看看缺失值的比例:
df.isnull().sum() / df.count()#缺失值比例
‘房屋朝向’的缺失值只有0.45%左右
數據探索
我們想看看數據分布是怎樣的
例如看一下二手房所在區的情況:
df['所在區'].value_counts()
浦東的二手房在售的最多,可能是因為浦東新區地大
看一下二手房房屋朝向的情況:
df['房屋朝向'].value_counts()
南和南北朝向的有很多,陽光充足
看一下房屋戶型的情況:
df['房屋戶型'].value_counts()
可以看出在售的二手房多為2室1廳1衛
看一下房屋類型的情況:
df['房屋類型'].value_counts()
說明公寓類型最多
篩選房屋信息
如果我們想看一下我們感興趣的房屋,例如我想找3室1廳1衛的房屋,并且只看部分:
df[df['房屋戶型']=='3室1廳1衛'].head()
當然你也可以使用and(&)和or(|)挑選你感興趣的房屋信息,這里不再贅述。
舍棄有缺失值的行
之前看到有包含13個含缺失值的行,我們將其舍棄:
df=df.dropna()#舍棄含有任意缺失值的行,顯示的是剩下的df.head()
小插曲:在jupyter notebook中,如果想要看一個方法的具體使用方法,我們可以:
?df.dropna()
然后查看彈出的信息了解使用說明
此時,我們再看看是否還有缺失值:
df.isnull().any()#是否有缺失值
顯示已經沒有缺失值了
資料重整
我們發現:
房屋類型中,所有類型前面都顯示‘住宅-’,我們將其去除
df['房屋類型']=df['房屋類型'].map(lambda e: e.split(' - ')[1].split())
注:將該字段信息以‘-’為分隔符分成前后兩部分(兩部分都不包括-),然后索引取后面部分(如果是前面部分則是[0])
然后在pandas里用map加載匿名函數lambda,返回的e即為‘-’后面的部分信息
df
處理完之后:
正則表達式提取信息
由于建筑面積的信息不是數值型,而且建筑面積里含有‘平’這個字,不能直接用于計算
所以我們提取出其中的數字:
df['建筑面積'].str.extract('(\d+\.\d+)平', expand=False)
\d+:匹配0-9中一個及其以上數字
\.:轉義,匹配點
(\d+\.\d+)用括號括起來,提取括號中的信息
提取的結果如下:
我們將提取的結果反饋回dataframe表格:
df['建筑面積']=df['建筑面積'].str.extract('(\d+\.\d+)平', expand=False)
df.head(5)
、
看來已經完成這一步驟
同理,使用正則提取‘總價’欄位中的數字:
df['總價'].str.extract('(\d+)萬', expand=False)
df['總價']=df['總價'].str.extract('(\d+)萬', expand=False)df.head()
轉換數據類型
提取出來的數字是字符類型,我們需要將其轉化為數字類型,例如float浮點數,int整型等
df[['總價']]=df[['總價']].astype(int)
df[['建筑面積']]=df[['建筑面積']].astype(float)
df.info()
轉換完成后,可以看出‘建筑面積’和‘總價’已經是數值型的了
屬性構造
我們構造一個屬性叫做‘均價’
df['均價']=df['總價']/df['建筑面積']
df.head()
建立數據透視表
df2=df.pivot_table(index='位置',
columns='裝修情況',
values='均價',
fill_value='0')#默認均價,比較出各個地區價格df2.head()#位置作為索引,裝修情況作為欄位,均價作為值
df2=df.pivot_table(index='所在區',
columns='裝修情況',
values='均價',
fill_value='0')#默認均價,比較出各個地區價格df2#所在區作為索引,裝修情況作為欄位,均價作為值
這樣的數據透視表,可以讓我們一目了然的看出各區不同裝修情況在售的不同二手房價格(均價單位是萬元/每平方米,取的是平均值)
我們注意到,上面的數據透視表中有很多0,當然,這里的0不是說房價為0,而是fill_value='0',即缺失值用0填充了
無意義的值轉為缺失值
我們看到,裝修情況欄位中有著'暫無數據'的字樣情況:
我們將其變為缺失值:
df['裝修情況']=df['裝修情況'].replace('暫無數據',np.NaN)
然后刪去這些含缺失值的行:
df.dropna()
我們注意到行數確實減少了
我們再次制作數據透視表:
df2=df.pivot_table(index='所在區',
columns='裝修情況',
values='均價',
fill_value=np.NaN)#默認均價,比較出各個地區價格df2#位置作為索引,裝修情況作為欄位,均價作為值
NaN表示沒有信息:意思是沒有此類的房屋信息
數據可視化
做數據分析,數據展現的方法之一就是圖
#-*- coding: utf-8 -*-%matplotlib inline
以上是為了讓繪制的圖顯示出來
from matplotlib.font_manager import FontPropertiesimport matplotlib.pyplot as plt
font=FontProperties(fname='C:\Windows\Fonts\simsun.ttc')
以上是為了解決繪圖里中文字體顯示問題,可以參照如下鏈接設置顯示中文字體:
https://www.joinquant.com/post/441
繪制散點圖
df.plot(x='建筑面積', y='總價', kind='scatter')plt.xlabel('建筑面積', fontproperties=font)
可以看出,隨著建筑面積的增大,二手房的總價也在上升,并沒有出現特別異常的值
但是有離群值出現,例如靠近右上角的,我們可以看一下這些房屋的信息:
df[df['建筑面積']>250]
繪制柱狀圖
注:以下過程比較波折,在于我目前對可視化學習較少
為了統計不同裝修情況,我進行了一大堆步驟:
df1=pd.get_dummies(df['裝修情況'])#建立虛擬變量,是精裝則在精裝位置顯示1,以此類推df1.head()
dec=df1.sum()
type(dec)
我想將Series轉為df:
dec
df2=pd.DataFrame(dec)#series轉為dfdf2
發現索引不是我想要的,于是我刪除了索引:
df2=df2.reset_index(drop=True)df2
這繞了一圈差不多刪除完了,尷尬
再手工加回去:
df2['裝修情況']=['中裝','毛坯','簡裝','精裝','豪裝']df2
這欄位名不太對,改一下:
df2.columns=['個數', '裝修情況']df2
走到這部不容易,我一定好好學數據可視化。。。
df2.plot(x='裝修情況', y='個數', kind='bar')plt.xlabel('裝修情況', fontproperties=font)
繪圖,通過圖形更加一目了然看出不同裝修情況的個數比較
最后再看一下描述性統計:
df.describe()
因為數據缺失值處理了,數字轉為數值型了,所以此時的統計更有意義
包括數量、均指、標準差、最小值、上四分位數等等。
寫在文末
這是我寫的比較久的一篇文章,就像在做一個簡單的項目一樣,從上文的爬取數據到這次的數據分析可視化,終于完整的做了一次數據分析。
致謝
在這篇文章的數據分析過程中,感謝秦路老師給我在技術上的答疑解惑!
本文來源自天善智能社區,作者:王大偉,
博客:https://www.hellobi.com/u/wangdawei/articles
天善學院svip正火爆報名中!包含Excel BI、Python3爬蟲案例、Python機器學習、Python數據科學家、大數據體系、數據分析報告、數據分析師體系、深度學習、R語言案例共10套課程,其他課程只需五折即可,歡迎大家關注報名!