編號:32490230
模式:RGB
體積:16.659 MB
分辨率:72dpi
本文將分別使用 Python
,Golang
以及 GraphQuery
來解析某網(wǎng)站的 素材詳情頁面 ,這個(gè)頁面的特色是具有清晰的數(shù)據(jù)結(jié)構(gòu),但是DOM結(jié)構(gòu)不夠規(guī)范,無法通過單獨(dú)的選擇器定位頁面元素,對頁面的解析造成了一些曲折。通過這個(gè)頁面的解析過程,深入淺出的了解爬蟲的解析思想與這些語言之間的異同。
成都創(chuàng)新互聯(lián)公司是一家專注于成都網(wǎng)站建設(shè)、網(wǎng)站制作與策劃設(shè)計(jì),通遼網(wǎng)站建設(shè)哪家好?成都創(chuàng)新互聯(lián)公司做網(wǎng)站,專注于網(wǎng)站建設(shè)10多年,網(wǎng)設(shè)計(jì)領(lǐng)域的專業(yè)建站公司;建站業(yè)務(wù)涵蓋:通遼等地區(qū)。通遼做網(wǎng)站價(jià)格咨詢:13518219792
在前言中,為了防止在后面的章節(jié)產(chǎn)生不必要的困擾,我們將會(huì)首先了解一些基本的編程理念。
這里我們講的語義化的DOM結(jié)構(gòu),不僅僅包括 語義化的html標(biāo)簽,也包括了語義化的選擇器,在前端開發(fā)中應(yīng)該注意的是,所有的動(dòng)態(tài)文本都應(yīng)該有單獨(dú)的 html 標(biāo)簽包裹,并最好賦予其語義化的 class
屬性或 id
屬性,這在版本功能的迭代中,對前端和后端的開發(fā)都是大有裨益的,比如下面的HTML代碼:
編號:32490230
模式:RGB
體積:16.659 MB
分辨率:72dpi
這就是不夠語義化的前端代碼,32504070
,RGB
,16.659 MB
,72dpi
這些值都是動(dòng)態(tài)屬性, 會(huì)跟隨編號的改變而改變,在規(guī)范的開發(fā)中,應(yīng)該將這些 動(dòng)態(tài)變化的屬性
,分別用 <span>
這類行內(nèi)標(biāo)簽包裹起來,并賦予其一定的語義化選擇器,在上面的HTML結(jié)構(gòu)中大致可以推測出這是后端直接使用 foreach 渲染出的頁面,這是不符合前后端分離的思想的,如果有一天他們決定使用 jsonp
或 Ajax
渲染這些屬性, 由前端進(jìn)行渲染,工作量無疑會(huì)上一個(gè)層次。語義化的DOM結(jié)構(gòu)更傾向于下面這樣:
模式:RGB
也可以將 property-mode
直接作為 span
的 class
屬性,這樣這些屬性無論是后端渲染,還是前端動(dòng)態(tài)渲染都減輕了產(chǎn)品迭代產(chǎn)生的負(fù)擔(dān)。
在 語義化的DOM結(jié)構(gòu)
之后,我們來談?wù)劮€(wěn)定的解析代碼, 對于下面的DOM結(jié)構(gòu):
編號:32490230
模式:RGB
體積:16.659 MB
分辨率:72dpi
如果我們想要提取 模式
信息,當(dāng)然可以采取下面的步驟:
class
屬性中包含 main-right
的 div
div
中第二個(gè) p
元素,取出其包含的文本模式:
, 得到模式為 RGB
雖然成功獲取到了想要的結(jié)果,但是這樣的解析方法,我們認(rèn)為它是 不穩(wěn)定的
,這個(gè)不穩(wěn)定是指 在其祖先元素、兄弟元素等自身以外的元素節(jié)點(diǎn)發(fā)生一定程度的結(jié)構(gòu)改變時(shí),導(dǎo)致解析錯(cuò)誤或失敗的情況, 比如如果有一天在 模式
所在的節(jié)點(diǎn)之前增加了一個(gè) 尺寸
的屬性:
編號:32490230
尺寸:4724×6299像素
模式:RGB
體積:16.659 MB
分辨率:72dpi
那么我們之前的解析將會(huì)發(fā)生錯(cuò)誤(什么?你覺得不可能發(fā)生這樣的變動(dòng)?請對比 Page1 和 Page2)。
那我們應(yīng)該如何寫出更穩(wěn)定的解析代碼呢,對于上面的DOM結(jié)構(gòu),我們可以有下面幾種思路:
思路一: 遍歷 class
屬性為 main-rightStage
的 p
節(jié)點(diǎn),依次判斷節(jié)點(diǎn)的文本是否以 模式
開頭, 如果是, 取出其 :
后的內(nèi)容,缺點(diǎn)是邏輯太多,不易維護(hù)且降低了代碼可讀性。
思路二: 使用正則表達(dá)式 模式:([A-Z]+)
進(jìn)行匹配,缺點(diǎn)是使用不當(dāng)可能造成效率問題。
思路三: 使用 CSS選擇器中的 contains
方法,比如 .main-rightStage:contains(模式)
, 就可以選取文本中包含 模式
,且 class
屬性中包含 main-rightStage
的節(jié)點(diǎn)了。但缺點(diǎn)是不同語言和不同庫對這種語法的支持程度各有不同,缺乏兼容性。
使用哪種方法,仁者見仁智者見智,不同的解析思路帶來的解析的 穩(wěn)定性
、代碼的 復(fù)雜程度
、運(yùn)行效率
和 兼容性
都是不同的, 開發(fā)者需要從各種因素中進(jìn)行權(quán)衡, 來寫出最優(yōu)秀的解析代碼。
在進(jìn)行頁面數(shù)據(jù)的抽取之前,首先要做的是明確我們需要哪些數(shù)據(jù)、頁面上提供了哪些數(shù)據(jù),然后設(shè)計(jì)出我們需要的數(shù)據(jù)結(jié)構(gòu)。首先打開 待解析頁面, 由于其最上方的 瀏覽量
、收藏量
、下載量
等數(shù)據(jù)是動(dòng)態(tài)加載的, 在我們的演示中暫時(shí)不需要,而這個(gè)頁面右邊的 尺寸
、模式
等數(shù)據(jù),通過上面 Page1 和 Page2 的對比,可以得知這些屬性是不一定存在的,因此將它們一起歸到 metainfo
中。因此我們需要獲得的數(shù)據(jù)如下圖所示:
由此我們可以很快設(shè)計(jì)出我們的數(shù)據(jù)結(jié)構(gòu):
{
title
pictype
number
type
metadata {
size
volume
mode
resolution
}
author
images []
tags []
}
其中 size
、volume
、mode
、resolution
由于可能不存在,因此歸入到了 metadata
下, images
是一個(gè)圖片地址的數(shù)組,tags
是標(biāo)簽數(shù)組,在確定了要提取的數(shù)據(jù)結(jié)構(gòu),就可以開始進(jìn)行解析。
Python庫的數(shù)量非常龐大,有很多優(yōu)秀的庫可以幫助到我們,在使用Python進(jìn)行頁面的解析時(shí),我們通常用到下面這些庫:
正則表達(dá)式
支持的 re
庫CSS選擇器
支持的 pyquery
和 beautifulsoup4
Xpath
支持的 lxml
庫JSON PATH
支持的 jsonpath_rw
庫這些庫在 Python 3
下獲得支持的,可以通過 pip install
進(jìn)行安裝。
由于 CSS選擇器
的語法比 Xpath
語法要更加簡潔,而在方法的調(diào)用上,pyquery
比 beautifulsoup4
要更加方便,因此在 2 和 3 之間我們選擇了 pyquery
。
下面我們會(huì)以 title
和 type
屬性的獲取作為例子進(jìn)行講解, 其他節(jié)點(diǎn)的獲取是同理的。首先我們先使用 requests
庫下載這個(gè)頁面的源文件:
import requests
from pyquery import PyQuery as pq
response = requests.get("http://www.58pic.com/newpic/32504070.html")
document = pq(response.content.decode('gb2312'))
下面使用Python進(jìn)行的解析都將依次為前提進(jìn)行。
打開 待解析頁面,在標(biāo)題上右鍵, 點(diǎn)擊 查看元素
,可以看到它的DOM結(jié)構(gòu)如下:
這時(shí)我們注意到, 我們想要提取出的標(biāo)題文本 大俠海報(bào)金庸武俠水墨中國風(fēng)黑白
,并沒有被html標(biāo)簽包裹,這是不符合我們上面提到的 語義化的dom結(jié)構(gòu) 的。同時(shí),使用CSS選擇器,也是無法直接選取到這個(gè)文本節(jié)點(diǎn)的(可以使用Xpath直接選取到,本文略)。對于這樣的節(jié)點(diǎn),我們可以有下面兩種思路:思路一
: 先選取其父元素節(jié)點(diǎn), 獲取其 HTML 內(nèi)容,使用正則表達(dá)式, 匹配在 </div>
和 <p
之間的文本。思路二
: 先選取其父元素節(jié)點(diǎn),然后刪除文本節(jié)點(diǎn)之外的其他節(jié)點(diǎn),再直接通過獲取父元素節(jié)點(diǎn)的文本,得到想要的標(biāo)題文本。
我們采取思路二,寫出下面的Python代碼:
title_node = document.find(".detail-title")
title_node.find("div").remove()
title_node.find("p").remove()
print(title_node.text())
輸出結(jié)果與我們期望的相同, 為 大俠海報(bào)金庸武俠水墨中國風(fēng)黑白
。
在 尺寸
上右鍵查看元素,可以看到下圖所示的DOM結(jié)構(gòu):
我們發(fā)現(xiàn)這些節(jié)點(diǎn)不具有語義化的選擇器,并且這些屬性不一定都存在(詳見Page1 和 Page2 的對比)。在 穩(wěn)定的解析代碼 中我們也講到了對于這種結(jié)構(gòu)的文檔可以采取的幾種思路,這里我們采用正則解析的方法:
import re
context = document.find(".mainRight-file").text()
file_type_matches = re.compile("尺寸:(.*?像素)").findall(context)
filetype = ""
if len(file_type_matches) > 0:
filetype = file_type_matches[0]
print(filetype)
由于獲取 size
、volume
、mode
、resolution
這些屬性,都可以采取類似的方法,因此我們可以歸結(jié)出一個(gè)正則提取的函數(shù):
def regex_get(text, expr):
matches = re.compile(expr).findall(text)
if len(matches) == 0:
return ""
return matches[0]
因此,在獲取 size
節(jié)點(diǎn)時(shí),我們的代碼就可以精簡為:
size = regex_get(context, r"尺寸:(.*?像素)")
到這里,我們解析頁面可能遇到的問題就已經(jīng)解決了大半,整個(gè)Python代碼如下:
import requests
import re
from pyquery import PyQuery as pq
def regex_get(text, expr):
matches = re.compile(expr).findall(text)
if len(matches) == 0:
return ""
return matches[0]
conseq = {}
## 下載文檔
response = requests.get("http://www.58pic.com/newpic/32504070.html")
document = pq(response.text)
## 獲取文件標(biāo)題
title_node = document.find(".detail-title")
title_node.find("div").remove()
title_node.find("p").remove()
conseq["title"] = title_node.text()
## 獲取素材類型
conseq["pictype"] = document.find(".pic-type").text()
## 獲取文件格式
conseq["filetype"] = regex_get(document.find(".mainRight-file").text(), r"文件格式:([a-z]+)")
## 獲取元數(shù)據(jù)
context = document.find(".main-right p").text()
conseq['metainfo'] = {
"size": regex_get(context, r"尺寸:(.*?像素)"),
"volume": regex_get(context, r"體積:(.*? MB)"),
"mode": regex_get(context, r"模式:([A-Z]+)"),
"resolution": regex_get(context, r"分辨率:(\d+dpi)"),
}
## 獲取作者
conseq['author'] = document.find('.user-name').text()
## 獲取圖片
conseq['images'] = []
for node_image in document.find("#show-area-height img"):
conseq['images'].append(pq(node_image).attr("src"))
## 獲取tag
conseq['tags'] = []
for node_image in document.find(".mainRight-tagBox .fl"):
conseq['tags'].append(pq(node_image).text())
print(conseq)
在 Golang
中解析 html
和 xml
文檔, 常用到的庫有以下幾種:
正則表達(dá)式
支持的 regexp
庫CSS選擇器
支持的 github.com/PuerkitoBio/goquery
Xpath
支持的 gopkg.in/xmlpath.v2
庫JSON PATH
支持的 github.com/tidwall/gjson
庫這些庫,你都可以通過 go get -u
來獲取,由于在上面的Python解析中我們已經(jīng)整理出了解析邏輯,在Golang
中只需要復(fù)現(xiàn)即可,與 Python
不同的是,我們最好先為我們的數(shù)據(jù)結(jié)構(gòu)定義一個(gè) struct,像下面這樣:
type Reuslt struct {
Title string
Pictype string
Number string
Type string
Metadata struct {
Size string
Volume string
Mode string
Resolution string
}
Author string
Images []string
Tags []string
}
同時(shí),由于我們的 待解析頁面 是非主流的 gbk
編碼,所以在下載下來文檔之后,需要手動(dòng)將 utf-8
的編碼轉(zhuǎn)換為 gbk
的編碼,這個(gè)過程雖然不在解析的范疇之內(nèi),但是也是必須要做的步驟之一, 我們使用了 github.com/axgle/mahonia
這個(gè)庫進(jìn)行編碼的轉(zhuǎn)換,并整理出了編碼轉(zhuǎn)換的函數(shù) decoderConvert
:
func decoderConvert(name string, body string) string {
return mahonia.NewDecoder(name).ConvertString(body)
}
因此, 最終的 golang
代碼應(yīng)該是下面這樣的:
package main
import (
"encoding/json"
"log"
"regexp"
"strings"
"github.com/axgle/mahonia"
"github.com/parnurzeal/gorequest"
"github.com/PuerkitoBio/goquery"
)
type Reuslt struct {
Title string
Pictype string
Number string
Type string
Metadata struct {
Size string
Volume string
Mode string
Resolution string
}
Author string
Images []string
Tags []string
}
func RegexGet(text string, expr string) string {
regex, _ := regexp.Compile(expr)
return regex.FindString(text)
}
func decoderConvert(name string, body string) string {
return mahonia.NewDecoder(name).ConvertString(body)
}
func main() {
//下載文檔
request := gorequest.New()
_, body, _ := request.Get("http://www.58pic.com/newpic/32504070.html").End()
document, err := goquery.NewDocumentFromReader(strings.NewReader(decoderConvert("gbk", body)))
if err != nil {
panic(err)
}
conseq := &Reuslt{}
//獲取文件標(biāo)題
titleNode := document.Find(".detail-title")
titleNode.Find("div").Remove()
titleNode.Find("p").Remove()
conseq.Title = titleNode.Text()
// 獲取素材類型
conseq.Pictype = document.Find(".pic-type").Text()
// 獲取文件格式
conseq.Type = document.Find(".mainRight-file").Text()
// 獲取元數(shù)據(jù)
context := document.Find(".main-right p").Text()
conseq.Metadata.Mode = RegexGet(context, `尺寸:(.*?)像素`)
conseq.Metadata.Resolution = RegexGet(context, `體積:(.*? MB)`)
conseq.Metadata.Size = RegexGet(context, `模式:([A-Z]+)`)
conseq.Metadata.Volume = RegexGet(context, `分辨率:(\d+dpi)`)
// 獲取作者
conseq.Author = document.Find(".user-name").Text()
// 獲取圖片
document.Find("#show-area-height img").Each(func(i int, element *goquery.Selection) {
if attribute, exists := element.Attr("src"); exists && attribute != "" {
conseq.Images = append(conseq.Images, attribute)
}
})
// 獲取tag
document.Find(".mainRight-tagBox .fl").Each(func(i int, element *goquery.Selection) {
conseq.Tags = append(conseq.Tags, element.Text())
})
bytes, _ := json.Marshal(conseq)
log.Println(string(bytes))
}
解析邏輯完全相同,代碼量和復(fù)雜程度相較 python版 差不多,下面我們來看一下新出現(xiàn)的 GraphQuery
是如何做的。
已知我們想要得到的數(shù)據(jù)結(jié)構(gòu)如下:
{
title
pictype
number
type
metadata {
size
volume
mode
resolution
}
author
images []
tags []
}
GraphQuery
的代碼是下面這樣的:
{
title `xpath("/html/body/div[4]/div[1]/div/div/div[1]/text()")`
pictype `css(".pic-type")`
number `css(".detailBtn-down");attr("data-id")`
type `regex("文件格式:([a-z]+)")`
metadata `css(".main-right p")` {
size `regex("尺寸:(.*?)像素")`
volume `regex("體積:(.*? MB)")`
mode `regex("模式:([A-Z]+)")`
resolution `regex("分辨率:(\d+dpi)")`
}
author `css(".user-name")`
images `css("#show-area-height img")` [
src `attr("src")`
]
tags `css(".mainRight-tagBox .fl")` [
tag `text()`
]
}
通過對比可以看出, 它只是在我們設(shè)計(jì)的數(shù)據(jù)結(jié)構(gòu)之中添加了一些由反引號包裹起來的函數(shù)。驚艷的是,它能完全還原我們上面在 Python
和 Golang
中的解析邏輯,而且從它的語法結(jié)構(gòu)上,更能清晰的讀出返回的數(shù)據(jù)結(jié)構(gòu)。這段 GraphQuery
的執(zhí)行結(jié)果如下:
{
"data": {
"author": "Ice bear",
"images": [
"http://pic.qiantucdn.com/58pic/32/50/40/70d58PICZfkRTfbnM2UVe_PIC2018.jpg!/fw/1024/watermark/url/L2ltYWdlcy93YXRlcm1hcmsvZGF0dS5wbmc=/repeat/true/crop/0x1024a0a0",
"http://pic.qiantucdn.com/58pic/32/50/40/70d58PICZfkRTfbnM2UVe_PIC2018.jpg!/fw/1024/watermark/url/L2ltYWdlcy93YXRlcm1hcmsvZGF0dS5wbmc=/repeat/true/crop/0x1024a0a1024",
"http://pic.qiantucdn.com/58pic/32/50/40/70d58PICZfkRTfbnM2UVe_PIC2018.jpg!/fw/1024/watermark/url/L2ltYWdlcy93YXRlcm1hcmsvZGF0dS5wbmc=/repeat/true/crop/0x1024a0a2048",
"http://pic.qiantucdn.com/58pic/32/50/40/70d58PICZfkRTfbnM2UVe_PIC2018.jpg!/fw/1024/watermark/url/L2ltYWdlcy93YXRlcm1hcmsvZGF0dS5wbmc=/repeat/true/crop/0x1024a0a3072"
],
"metadata": {
"mode": "RGB",
"resolution": "200dpi",
"size": "4724×6299",
"volume": "196.886 MB"
},
"number": "32504070",
"pictype": "原創(chuàng)",
"tags": ["大俠", "海報(bào)", "黑白", "金庸", "水墨", "武俠", "中國風(fēng)"],
"title": "大俠海報(bào)金庸武俠水墨中國風(fēng)黑白",
"type": "psd"
},
"error": "",
"timecost": 10997800
}
GraphQuery
是一個(gè)文本查詢語言,它不依賴于任何后端語言,可以被任何后端語言調(diào)用,一段 GraphQuery
查詢語句,在任何語言中可以得到相同的解析結(jié)果。
它內(nèi)置了 xpath
選擇器,css
選擇器,jsonpath
選擇器和 正則表達(dá)式
,以及足量的文本處理函數(shù),結(jié)構(gòu)清晰易讀,能夠保證 數(shù)據(jù)結(jié)構(gòu)
、解析代碼
、返回結(jié)果
結(jié)構(gòu)的一致性。
項(xiàng)目地址: github.com/storyicon/graphquery
GraphQuery
的語法簡潔易懂, 即使你是第一次接觸它, 也能很快的上手, 它的語法設(shè)計(jì)理念之一就是 符合直覺
, 我們應(yīng)該如何執(zhí)行它呢:
在 golang
中,你只需要首先使用 go get -u github.com/storyicon/graphquery
獲得 GraphQuery
并在代碼中調(diào)用即可:
package main
import (
"log"
"github.com/axgle/mahonia"
"github.com/parnurzeal/gorequest"
"github.com/storyicon/graphquery"
)
func decoderConvert(name string, body string) string {
return mahonia.NewDecoder(name).ConvertString(body)
}
func main() {
request := gorequest.New()
_, body, _ := request.Get("http://www.58pic.com/newpic/32504070.html").End()
body = decoderConvert("gbk", body)
response := graphquery.ParseFromString(body, "{ title `xpath(\"/html/body/div[4]/div[1]/div/div/div[1]/text()\")` pictype `css(\".pic-type\")` number `css(\".detailBtn-down\");attr(\"data-id\")` type `regex(\"文件格式:([a-z]+)\")` metadata `css(\".main-right p\")` { size `regex(\"尺寸:(.*?)像素\")` volume `regex(\"體積:(.*? MB)\")` mode `regex(\"模式:([A-Z]+)\")` resolution `regex(\"分辨率:(\\d+dpi)\")` } author `css(\".user-name\")` images `css(\"#show-area-height img\")` [ src `attr(\"src\")` ] tags `css(\".mainRight-tagBox .fl\")` [ tag `text()` ] }")
log.Println(response)
}
我們的 GraphQuery
表達(dá)式以 單行
的形式, 作為函數(shù) graphquery.ParseFromString
的第二個(gè)參數(shù)傳入,得到的結(jié)果與預(yù)期完全相同。
在 Python
等其他后端語言中,調(diào)用 GraphQuery
需要首先啟動(dòng)其服務(wù),服務(wù)已經(jīng)為 windows
、mac
和 linux
編譯好,到 GraphQuery-http 中下載即可。
在解壓并啟動(dòng)服務(wù)后,我們就可以愉快的使用 GraphQuery
在任何后端語言中對任何文檔以圖形的方式進(jìn)行解析了。Python調(diào)用的示例代碼如下:
import requests
def GraphQuery(document, expr):
response = requests.post("http://127.0.0.1:8559", data={
"document": document,
"expression": expr,
})
return response.text
response = requests.get("http://www.58pic.com/newpic/32504070.html")
conseq = GraphQuery(response.text, r"""
{
title `xpath("/html/body/div[4]/div[1]/div/div/div[1]/text()")`
pictype `css(".pic-type")`
number `css(".detailBtn-down");attr("data-id")`
type `regex("文件格式:([a-z]+)")`
metadata `css(".main-right p")` {
size `regex("尺寸:(.*?)像素")`
volume `regex("體積:(.*? MB)")`
mode `regex("模式:([A-Z]+)")`
resolution `regex("分辨率:(\d+dpi)")`
}
author `css(".user-name")`
images `css("#show-area-height img")` [
src `attr("src")`
]
tags `css(".mainRight-tagBox .fl")` [
tag `text()`
]
}
""")
print(conseq)
輸出結(jié)果為:
{
"data": {
"author": "Ice bear",
"images": [
"http://pic.qiantucdn.com/58pic/32/50/40/70d58PICZfkRTfbnM2UVe_PIC2018.jpg!/fw/1024/watermark/url/L2ltYWdlcy93YXRlcm1hcmsvZGF0dS5wbmc=/repeat/true/crop/0x1024a0a0",
"http://pic.qiantucdn.com/58pic/32/50/40/70d58PICZfkRTfbnM2UVe_PIC2018.jpg!/fw/1024/watermark/url/L2ltYWdlcy93YXRlcm1hcmsvZGF0dS5wbmc=/repeat/true/crop/0x1024a0a1024",
"http://pic.qiantucdn.com/58pic/32/50/40/70d58PICZfkRTfbnM2UVe_PIC2018.jpg!/fw/1024/watermark/url/L2ltYWdlcy93YXRlcm1hcmsvZGF0dS5wbmc=/repeat/true/crop/0x1024a0a2048",
"http://pic.qiantucdn.com/58pic/32/50/40/70d58PICZfkRTfbnM2UVe_PIC2018.jpg!/fw/1024/watermark/url/L2ltYWdlcy93YXRlcm1hcmsvZGF0dS5wbmc=/repeat/true/crop/0x1024a0a3072"
],
"metadata": {
"mode": "RGB",
"resolution": "200dpi",
"size": "4724×6299",
"volume": "196.886 MB"
},
"number": "32504070",
"pictype": "原創(chuàng)",
"tags": ["大俠", "海報(bào)", "黑白", "金庸", "水墨", "武俠", "中國風(fēng)"],
"title": "大俠海報(bào)金庸武俠水墨中國風(fēng)黑白",
"type": "psd"
},
"error": "",
"timecost": 10997800
}
復(fù)雜的解析邏輯帶來的不僅僅是代碼可讀性的問題,在代碼的維護(hù)和移植上也會(huì)造成很大的困擾,不同的語言和不同的庫也為代碼的解析結(jié)果造成了差異,GraphQuery
是一個(gè)全新的開源項(xiàng)目,它的主旨就是讓開發(fā)者從這些重復(fù)繁瑣的解析邏輯中解脫出來,寫出高可讀性、高可移植性、高可維護(hù)性的代碼。歡迎實(shí)踐、持續(xù)關(guān)注與代碼貢獻(xiàn),一起見證 GraphQuery
與開源社區(qū)的發(fā)展!