我們在測試接口的過程中 對於接口返回的常用格式 有 json和html xml等; 常用的場景需要我們對返回的數據進行處理, 比如對返回數據的校驗斷言等操作.
插件一:json文件解析庫jsonpath
官方文檔: https://goessner.net/articles/JsonPath/
使用場景:
curl命令請求返回的json數據可以用 | jq 來格式化成json數據
結合 grep 查找關鍵字來查找元素值 | jq '.' | grep name
可以通過json樹來定位元素 | jq -r '.data.stocks[0].name'
適用於復雜的斷言:
列表中的元素是字典 字典中的值sybol==F006947時 name值是不是等於"華寶中短債債券A"
assert jsonpath.jsonpath(r.json(),
"$.data.stocks[?(@.symbol == 'F006947')].name")[0]=="華寶中短債債券A"
assert_that(jsonpath.jsonpath(r.json(),"$.data.stocks[?(@.symbol == 'F006947')].name")[0],
equal_to("華寶中短債債券B"), "比較上市代碼與名字")
{ "store": {
"book": [ { "category": "reference", "author": "Nigel Rees", "title": "Sayings of the Century", "price": 8.95 }, { "category": "fiction", "author": "Evelyn Waugh", "title": "Sword of Honour", "price": 12.99 }, { "category": "fiction", "author": "Herman Melville", "title": "Moby Dick", "isbn": "0-553-21311-3", "price": 8.99 }, { "category": "fiction", "author": "J. R. R. Tolkien", "title": "The Lord of the Rings", "isbn": "0-395-19395-8", "price": 22.99 } ], "bicycle": { "color": "red", "price": 19.95 } } }
XPath | JSONPath | Result |
/store/book/author |
$.store.book[*].author |
the authors of all books in the store |
//author |
$..author |
all authors |
/store/* |
$.store.* |
all things in store, which are some books and a red bicycle. |
/store//price |
$.store..price |
the price of everything in the store. |
//book[3] |
$..book[2] |
the third book |
//book[last()] |
$..book[(@.length-1)] $..book[-1:] |
the last book in order. |
//book[position()<3] |
$..book[0,1] $..book[:2] |
the first two books |
//book[isbn] |
$..book[?(@.isbn)] |
filter all books with isbn number |
//book[price<10] |
$..book[?(@.price<10)] |
filter all books cheapier than 10 |
//* |
$..* |
all Elements in XML document. All members of JSON structure. |
插件二:HTML文本解析庫BeautifulSoup 、 css selector + xpath
參考文獻:https://foofish.net/crawler-beautifulsoup.html
BeatifulSoup 是一個用於操作 HTML 文檔的 Python 庫,初始化 BeatifulSoup 時,需要指定 HTML 文檔字符串和具體的解析器。BeatifulSoup 有3類常用的數據類型,分別是 Tag、NavigableString、和 BeautifulSoup。查找 HTML元素有兩種方式,分別是遍歷文檔樹和搜索文檔樹,通常快速獲取數據需要二者結合。通過 BeautifulSoup 對象就可以定位到 HTML 中的任何一個標簽節點。
from bs4 import BeautifulSoup text = """ <html> <head> <title >hello, world</title> </head> <body> <h1>BeautifulSoup</h1> <p class="bold">如何使用BeautifulSoup</p> <p class="big" id="key1"> 第二個p標簽</p> <a href="http://foofish.net">python</a> </body> </html> """ soup = BeautifulSoup(text, "html.parser") # title 標簽 >>> soup.title <title>hello, world</title> # p 標簽 >>> soup.p <p class="bold">\u5982\u4f55\u4f7f\u7528BeautifulSoup</p> # p 標簽的內容 >>> soup.p.string u'\u5982\u4f55\u4f7f\u7528BeautifulSoup'
插件三:xml解析庫Xpath
插件四:text解析庫 regex
插件五:斷言庫hamcrest
官方文檔: https://github.com/hamcrest/PyHamcrest
from hamcrest import * import unittest class BiscuitTest(unittest.TestCase): def testEquals(self): theBiscuit = Biscuit('Ginger') myBiscuit = Biscuit('Ginger') assert_that(theBiscuit, equal_to(myBiscuit)) if __name__ == '__main__': unittest.main()
def test_hamcrest(self):
assert_that(0.1 * 0.1, close_to(0.01, 0.000000000000001))
#assert_that(0.1 * 0.1, close_to(0.01, 0.000000000000000001))
assert_that(
["a", "b", "c"],
all_of(
has_items("c", "d"),
has_items("c", "a")
)
)
插件六:結構體斷言 jsonschema
官方文檔: https://github.com/Julian/jsonschema
實用場景: 我們在做斷言時想把json的數據結構保存下來, 斷言時不根據特定值來判斷,而是根據整個返回的json數據結構來判斷
schema=json.load(open("list_schema.json")) #"list_schema.json"是利用schema在線生成器生成的
validate(instance=r.json(), schema=schema) #validata對比傳入的json數據與schema描述的數據結構是否一致
實例:
>> from jsonschema import validate >>> # A sample schema, like what we'd get from json.load() >>> schema = { ... "type" : "object", ... "properties" : { ... "price" : {"type" : "number"}, ... "name" : {"type" : "string"}, ... }, ... } >>> # If no exception is raised by validate(), the instance is valid. >>> validate(instance={"name" : "Eggs", "price" : 34.99}, schema=schema) >>> validate( ... instance={"name" : "Eggs", "price" : "Invalid"}, schema=schema, ... ) # doctest: +IGNORE_EXCEPTION_DETAIL Traceback (most recent call last): ... ValidationError: 'Invalid' is not of type 'number'
It can also be used from console:
$ jsonschema -i sample.json sample.schema
插件七:模版斷言 pystache
官方文檔: https://github.com/defunkt/pystache
實用場景:做接口測試的數據准備時,接口的請求參數往往非常龐大,但是我們通常只想把特定的參數動態更新,其余的參數不改動
實例:
>>> import pystache >>> print pystache.render('Hi {{person}}!', {'person': 'Mom'}) Hi Mom!
@classmethod
def parse(self, template_path, dict):
template = "".join(open(template_path).readlines())
#支持把temp中的{{值}} 替換成dict中給的值 temp中存放請求的json
return pystache.render(template, dict)
requests部分:
原文: https://foofish.net/http-requests.html
requests 的安裝可以直接使用 pip 方法:pip install requests
>>> import requests # GET 請求 >>> response = requests.get("https://foofish.net")
返回的時 Response 對象,Response 對象是 對 HTTP 協議中服務端返回給瀏覽器的響應數據的封裝,響應的中的主要元素包括:狀態碼、原因短語、響應首部、響應體等等,這些屬性都封裝在Response 對象中。
# 狀態碼 >>> response.status_code 200 # 原因短語 >>> response.reason 'OK' # 響應首部 >>> for name,value in response.headers.items(): ... print("%s:%s" % (name, value)) ... Content-Encoding:gzip Server:nginx/1.10.2 Date:Thu, 06 Apr 2017 16:28:01 GMT # 響應內容 >>> response.content '<html><body>此處省略一萬字...</body></html>
requests 除了支持 GET 請求外,還支持 HTTP 規范中的其它所有方法,包括 POST、PUT、DELTET、HEADT、OPTIONS方法。
>>> r = requests.post('http://httpbin.org/post', data = {'key':'value'}) >>> r = requests.put('http://httpbin.org/put', data = {'key':'value'}) >>> r = requests.delete('http://httpbin.org/delete') >>> r = requests.head('http://httpbin.org/get') >>> r = requests.options('http://httpbin.org/get')
構建請求查詢參數
很多URL都帶有很長一串參數,我們稱這些參數為URL的查詢參數,用"?"附加在URL鏈接后面,多個參數之間用"&"隔開,比如:http://fav.foofish.net/?p=4&s=20 ,現在你可以用字典來構建查詢參數:
>>> args = {"p": 4, "s": 20} >>> response = requests.get("http://fav.foofish.net", params = args) >>> response.url 'http://fav.foofish.net/?p=4&s=2'
構建請求首部 Headers
requests 可以很簡單地指定請求首部字段 Headers,比如有時要指定 User-Agent 偽裝成瀏覽器發送請求,以此來蒙騙服務器。直接傳遞一個字典對象給參數 headers 即可。
>>> r = requests.get(url, headers={'user-agent': 'Mozilla/5.0'})
構建 POST 請求數據
requests 可以非常靈活地構建 POST 請求需要的數據,如果服務器要求發送的數據是表單數據,則可以指定關鍵字參數 data,如果要求傳遞 json 格式字符串參數,則可以使用json關鍵字參數,參數的值都可以字典的形式傳過去。
作為表單數據傳輸給服務器
>>> payload = {'key1': 'value1', 'key2': 'value2'} >>> r = requests.post("http://httpbin.org/post", data=payload)
作為 json 格式的字符串格式傳輸給服務器
>>> import json >>> url = 'http://httpbin.org/post' >>> payload = {'some': 'data'} >>> r = requests.post(url, json=payload)
Response中的響應體
HTTP返回的響應消息中很重要的一部分內容是響應體,響應體在 requests 中處理非常靈活,與響應體相關的屬性有:content、text、json()。
content 是 byte 類型,適合直接將內容保存到文件系統或者傳輸到網絡中
>>> r = requests.get("https://pic1.zhimg.com/v2-2e92ebadb4a967829dcd7d05908ccab0_b.jpg") >>> type(r.content) <class 'bytes'> # 另存為 test.jpg >>> with open("test.jpg", "wb") as f: ... f.write(r.content)
text 是 str 類型,比如一個普通的 HTML 頁面,需要對文本進一步分析時,使用 text。
>>> r = requests.get("https://foofish.net/understand-http.html") >>> type(r.text) <class 'str'> >>> re.compile('xxx').findall(r.text)
如果使用第三方開放平台或者API接口爬取數據時,返回的內容是json格式的數據時,那么可以直接使用json()方法返回一個經過json.loads()處理后的對象。
>>> r = requests.get('https://www.v2ex.com/api/topics/hot.json') >>> r.json() [{'id': 352833, 'title': '在長沙,父母同住...
代理設置
當爬蟲頻繁地對服務器進行抓取內容時,很容易被服務器屏蔽掉,因此要想繼續順利的進行爬取數據,使用代理是明智的選擇。如果你想爬取牆外的數據,同樣設置代理可以解決問題,requests 完美支持代理。
import requests proxies = { 'http': 'http://10.10.1.10:3128', 'https': 'http://10.10.1.10:1080', } requests.get('http://example.org', proxies=proxies)
超時設置
requests 發送請求時,默認請求下線程一直阻塞,直到有響應返回才處理后面的邏輯。如果遇到服務器沒有響應的情況時,問題就變得很嚴重了,它將導致整個應用程序一直處於阻塞狀態而沒法處理其他請求。
>>> import requests >>> r = requests.get("http://www.google.coma") ...一直阻塞中
正確的方式的是給每個請求顯示地指定一個超時時間。
>>> r = requests.get("http://www.google.coma", timeout=5) 5秒后報錯 Traceback (most recent call last): socket.timeout: timed out
Session
HTTP協議是一中無狀態的協議,為了維持客戶端與服務器之間的通信狀態,使用 Cookie 技術使之保持雙方的通信狀態。
有些網頁是需要登錄才能進行爬蟲操作的,而登錄的原理就是瀏覽器首次通過用戶名密碼登錄之后,服務器給客戶端發送一個隨機的Cookie,下次瀏覽器請求其它頁面時,就把剛才的 cookie 隨着請求一起發送給服務器,這樣服務器就知道該用戶已經是登錄用戶。
import requests # 構建會話 session = requests.Session() # 登錄url session.post(login_url, data={username, password}) # 登錄后才能訪問的url r = session.get(home_url) session.close()
構建一個session會話之后,客戶端第一次發起請求登錄賬戶,服務器自動把cookie信息保存在session對象中,發起第二次請求時requests 自動把session中的cookie信息發送給服務器,使之保持通信狀態。
---恢復內容結束---