Utiliser la bibliothèque curl / jq avec Go

introduction

Il s'agit de la construction de l'environnement à partir de Go pour obtenir json avec la bibliothèque curl et analyser json avec la bibliothèque jq. Il est possible de le faire uniquement avec la bibliothèque standard [^ lib], mais en utilisant une bibliothèque de commandes typiques, je voudrais réduire la nouvelle mémorisation et réduire autant que possible la différence d'implémentation entre les langages.

Comme chaque bibliothèque est fournie en langage C, il est bon de l'utiliser directement lors de son utilisation dans Go, mais pour plus de commodité, nous utiliserons la bibliothèque wrapper.

[^ lib]: Récupère json avec "net / http" et analyse json avec "encoding / json"

Environnement

L'environnement est le suivant.

Les bibliothèques de wrapper sont les suivantes, les deux sont des wrappers légers et ne font rien de plus.

Construction de l'environnement MSYS2

Installez les bibliothèques curl et jq. Je travaille sur le terminal de MSYS2.

Installation de la bibliothèque curl et de la bibliothèque jq

Utilisez Alexpux / MINGW-packages pour l'installation, et créez chaque bibliothèque avec la commande makepkg (téléchargement source de chaque bibliothèque, correctif pour MSYS2, build). Vous pouvez facilement l'installer avec (il fera tout automatiquement).

[^ libcurl]: Avec libcurl installé par pacman, curl_global_init et curl_easy_init n'ont pas pu être exécutés en raison d'une erreur lors de l'appel de la fonction realloc.

Installation


#Mettre à jour le package
$ pacman -Syuu
#* Si l'avertissement suivant apparaît, redémarrez le terminal et réessayez.
#avertissement: terminate MSYS2 without returning to shell and check for updates again
#avertissement: for example close your terminal window instead of calling exit
$ pacman -Syuu

#Installer les packages liés à la construction utilisés par makepkg
$ pacman -S git base-devel mingw-w64-x86_64-toolchain

# MINGW-paquets git clone
$ cd /tmp
$ git clone --depth=1 https://github.com/Alexpux/MINGW-packages.git

#construire jq&Installation
$ cd /tmp/MINGW-packages/
$ cd `ls | grep jq`
# -Installez les packages dépendants manquants avec l'option s
$ makepkg -s
#Installation de packages locaux
$ pacman -U ./mingw-w64-x86_64-jq-1.5-3-any.pkg.tar.xz
#Cliquez ici pour une installation directe
#$ cp -r pkg/mingw-w64-x86_64-jq/mingw64/* /mingw64/

#construire curl&Installation
$ cd /tmp/MINGW-packages/
$ cd `ls | grep curl`
$ makepkg -s
#Si vous recevez un message indiquant que la clé PGP ne peut pas être vérifiée, importez-la car il n'y a pas de clé publique.
# ==>Valider la signature du fichier source avec gpg...
#     curl-7.52.1.tar.bz2 ...Échec(Clé publique inconnue 5CC908FDB71E12C2) <-* Copiez ceci
# ==>Erreur:La clé PGP n'a pas pu être vérifiée!
$ gpg --recv-keys 5CC908FDB71E12C2
#Ré-exécuter
$ makepkg -s
#Installation de packages locaux
$ pacman -U ./mingw-w64-x86_64-curl-7.52.1-1-any.pkg.tar.xz
#Cliquez ici pour une installation directe
#$ cp -r pkg/mingw-w64-x86_64-curl/mingw64/* /mingw64/

Contrôle de fonctionnement (C)

Compilez et exécutez la source suivante avec gcc, et si "ok" est affiché, la construction de l'environnement curl est réussie. J'omettrai jq car il sera utilisé dans Go après cela.

test_curl.c


//Vérifiez si l'authentification SSL de curl fonctionne
#include <stdio.h>
#include <curl/curl.h>

size_t noop_function(char *buffer, size_t size, size_t nitems, void *instream) {
	return 0;
}

int main() {
	curl_global_init(CURL_GLOBAL_DEFAULT); // NOTE : libcurl-Si vous utilisez devel, vous pouvez aller ici

	CURL *curl = curl_easy_init();
	if (!curl) return 1;

	// qiita.Connexion SSL à com
	curl_easy_setopt(curl, CURLOPT_URL, "https://qiita.com");
	curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0);
	//Par défaut, la réponse est sortie sur la sortie standard, ce qui est ennuyeux, alors serrez-la.
	curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION , noop_function);
	curl_easy_perform(curl);

	//Obtenir une réponse HTTP
	long http_code = 0;
	curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
	if (http_code == 200) printf("ok");

	curl_easy_cleanup(curl);
	return 0;
}

compiler&Courir


$ gcc test_curl.c -I/mingw64/include -L/mingw64/lib -lcurl
$ ./a
ok

Aller construction d'environnement

Cela se fait à partir d'une invite de commande qui a un chemin vers gcc (C: \ chemin \ vers \ msys64 \ mingw \ bin \ gcc) sur Go et MSYS2. ** * Veuillez faire correspondre le chemin vers MSYS2 en fonction de votre environnement. ** **

Installer la bibliothèque d'encapsuleurs


> @rem curl, rendre la bibliothèque jq visible depuis Go
> set CGO_CFLAGS=-I C:\path\to\msys64\mingw64\include
> set CGO_LDFLAGS=-L C:\path\to\msys64\mingw64\lib
> @Installer rem curl, bibliothèque de wrapper jq
> go get -u github.com/andelf/go-curl
> go get -u github.com/mgood/go-jq
> @rem go get -u github.com/wordijp/go-jq

C'est la fin de la construction de l'environnement.

Essayez d'utiliser la bibliothèque curl / jq dans Go

** (2017/2/11) Ajout de la commodité de jq **

Obtenez la liste des publications de qiita avec curl, et affinez la liste des ID et des titres des affiches avec jq et affichez-la. En passant, vous pouvez consulter la liste des messages de Tous les messages sur Qiita Home.

すべての投稿.png

Le JSON de la liste d'articles pouvant être obtenue avec l'API Qiita v2 a le format suivant.

Qiita_API_V2_items.json


[
  {
    "rendered_body": "Texte(Omis parce que c'est long)",
    "coediting": false,
    "created_at": "2017-02-09T20:29:56+09:00",
    "group": null,
    "id": "acb70ae51c334aa1ee52",
    "private": false,
    "tags": [
      {
        "name": "C",
        "versions": []
      },
      {
        "name": "Go",
        "versions": []
      },
      {
        "name": "curl",
        "versions": []
      },
      {
        "name": "jq",
        "versions": []
      },
      {
        "name": "msys2",
        "versions": []
      }
    ],
    "title": "Curl en Go/Utiliser la bibliothèque jq",
    "updated_at": "2017-02-09T21:24:05+09:00",
    "url": "http://qiita.com/wordijp/items/acb70ae51c334aa1ee52",
    "user": {
      "description": "C++J'aime(C++Je ne dis pas que je publierai un article)",
      "facebook_id": "",
      "followees_count": 7,
      "followers_count": 9,
      "github_login_name": "wordijp",
      "id": "wordijp",
      "items_count": 33,
      "linkedin_id": "",
      "location": "",
      "name": "",
      "organization": "",
      "permanent_id": 51728,
      "profile_image_url": "https://qiita-image-store.s3.amazonaws.com/0/51728/profile-images/1473692528",
      "twitter_screen_name": "wordijp",
      "website_url": ""
    }
  },
  {
2e article
  }
]

La chaîne jq qui analyse d'ici à l'identifiant de l'affiche et au groupe de titres est la suivante.

Chaîne de caractères pour jq(Identifiant de l'affiche et groupe de titres)


'.[] | {user_id: .user.id, title: .title}'

Après l'analyse, ce sera comme suit.

Identifiant de l'affiche et groupe de titres.json


{
  "user_id": "wordijp",
  "title": "Curl en Go/Utiliser la bibliothèque jq"
}
{
2e article
}

Au fait, lors de l'analyse syntaxique tout en conservant la séquence, ce sera comme suit, Je ne l'utilise pas au moment de la mise en œuvre, mais je ne le présenterai que parce que c'est accessoire.

Chaîne de caractères pour jq(ID de l'affiche et groupe de titres, maintenance du tableau Ver)


'. | [{user_id: .[].user.id, title: .[].title}]'

Après l'analyse, ce sera comme suit.

ID de l'affiche et groupe de titres, maintenance du tableau Ver.json


[
  {
    "user_id": "wordijp",
    "title": "Curl en Go/Utiliser la bibliothèque jq"
  },
  {
2e article
  }
]

Cliquez ici pour le code source de Go

test_curl_json.go


//Appelez l'API Qiita v2 et indiquez l'ID de l'affiche et le titre de l'article publié
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	curl "github.com/andelf/go-curl"
	"sync/atomic"
	jq "github.com/mgood/go-jq" // <del>Correction d'un bug PR</del>fixé!
	//jq "github.com/wordijp/go-jq" //Le bogue d'origine a été corrigé, il n'est donc plus nécessaire
)

type wrdata struct {
	ch     chan []byte //Pour la propagation des données acquises
	remain int32       //Nombre restant de données acquises
	//Pour la synchronisation du traitement parallèle
	perform  chan int
	complete chan int
}

//Exécuter l'acquisition de données cURL et le traitement des valeurs de retour en parallèle
// NOTE :Un peu plus rapide que séquentiel
func easyParallelWrite(easy *curl.CURL, fWrite func([]byte)) {

	// write function
	easy.Setopt(curl.OPT_WRITEFUNCTION, func(ptr []byte, userdata interface{}) bool {
		//println("ptr size:", len(ptr))
		wd, ok := userdata.(*wrdata)
		if !ok {
			println("ERROR!")
			return false
		}

		atomic.AddInt32(&wd.remain, 1)
		wd.ch <- ptr
		return true // ok
	})

	// write data
	wd := &wrdata{
		ch:       make(chan []byte, 100),
		remain:   0,
		perform:  make(chan int),
		complete: make(chan int),
	}
	var buf bytes.Buffer
	go func(wd *wrdata) {
		performed := false
	loop:
		for {
			if !performed {
				select {
				case <-wd.perform:
					performed = true
					//Les données ont déjà été acquises
					if atomic.LoadInt32(&wd.remain) <= 0 {
						break loop
					}
				default:
					// no-op
				}
			}

			data := <-wd.ch
			atomic.AddInt32(&wd.remain, -1)
			//println("Got data size=", len(data))
			buf.Write(data)
			// complete after performed
			if performed && atomic.LoadInt32(&wd.remain) <= 0 {
				break
			}
		}

		//println("recv finished!")
		wd.complete <- 1
	}(wd)
	easy.Setopt(curl.OPT_WRITEDATA, wd)

	easy.Perform()

	//Attendez la fin de l'acquisition des données
	wd.perform <- 1
	<-wd.complete

	//Renvoie le résultat
	fWrite(buf.Bytes())
}

//Fonction d'assistance
//Le processus de curl est résumé ici
func curlExec(url string, fWrite func([]byte)) {
	curl.GlobalInit(curl.GLOBAL_DEFAULT)
	defer curl.GlobalCleanup()

	easy := curl.EasyInit()
	defer easy.Cleanup()

	easy.Setopt(curl.OPT_URL, url)
	//easy.Setopt(curl.OPT_SSL_VERIFYPEER, 0) //Activé pendant SSL
	easyParallelWrite(easy, fWrite)
}

type Item struct {
	UserID string `json:"user_id"`
	Title  string `json:"title"`
}

func main() {
	curlExec("http://qiita.com/api/v2/items", func(buf []byte) {
		j, _ := jq.NewJQ(".[] | {user_id: .user.id, title: .title}")

		j.HandleJson(string(buf))
		for j.Next() {
			item := &Item{}
			valuejson := j.ValueJson()
			json.Unmarshal(([]byte)(valuejson), &item)

			fmt.Println(valuejson)                        // JSON
			fmt.Println(item.UserID + " : " + item.Title) //Structure
			fmt.Println()
		}
	})
}

Résultat d'exécution


> go run test_curl_json.go
{"user_id":"kazuma1989","title":"Notez la configuration minimale pour exécuter Jetty avec Docker pour le moment"}
kazuma1989 :Notez la configuration minimale pour exécuter Jetty avec Docker pour le moment

{"user_id":"ironsand","title":"foo_Notez que si vous camélisez BarHoge, il deviendra FooBarhoge."}
ironsand : foo_Notez que si vous camélisez BarHoge, il deviendra FooBarhoge.

{"user_id":"Teach","title":"Flash jump avec ThirdPersonController"}
Teach :Flash jump avec ThirdPersonController
Abréviation

J'utilise jq pour extraire les données que je veux du JSON acquis, jq a une fonction de filtrage si puissante, je pense qu'il serait difficile de l'implémenter sans utiliser jq, jq est pratique ..

De plus, la fonction easyParallelWrite est parallélisée par goroutine, il est donc un peu compliqué de traiter la variable partagée (reste) de manière thread-safe, mais elle est utilisée à des fins de performance.

Recommended Posts

Utiliser la bibliothèque curl / jq avec Go
Utiliser le chiffrement de la bibliothèque de chiffrement avec l'image Python de Docker
Python avec Go
Utilisez mecab-ipadic-neologd avec igo-python
Utilisez RTX 3090 avec PyTorch
Utiliser ansible avec cygwin
Appelez votre propre bibliothèque de langage C avec Go en utilisant cgo
Utiliser pipdeptree avec virtualenv
[Python] Utiliser JSON avec Python
Utilisez Mock avec pytest
Utiliser l'indicateur avec pd.merge
Utiliser Gentelella avec Django
Utiliser mecab avec Python 3
Utiliser tensorboard avec Chainer
Utiliser DynamoDB avec Python
Utiliser pip avec MSYS2
Utilisez Python 3.8 avec Anaconda
Utiliser les droits d'auteur avec Spacemacs
Utiliser TypeScript avec django-compresseur
Utiliser MySQL avec Django
Utiliser Enum avec SQLAlchemy
Utiliser tensorboard avec NNabla
Utiliser le GPS avec Edison
Utilisez nim avec Jupyter
Utiliser la mémoire partagée avec une bibliothèque partagée
Utiliser des balises personnalisées avec PyYAML
Faire fonctionner le conteneur Db2 avec Go
Utiliser des graphiques directionnels avec networkx
Utiliser TensorFlow avec Intellij IDEA
Premiers pas avec Go Assembly
Utiliser l'API Twitter avec Python
Utiliser pip avec Jupyter Notebook
Utiliser DATE_FORMAT avec le filtre SQLAlchemy
Recherche de bits complète avec Go
Utiliser TUN / TAP avec Python
Utilisez sqlite3 avec NAO (Pepper)
Connectez-vous à Postgresql avec GO
Utilisez les load_extensions de sqlite avec Pyramid
Utiliser les polices Windows 10 avec WSL
Utilisation du chainer avec Jetson TK1
Utiliser SSL avec Celery + Redis
Utiliser Cython avec Jupyter Notebook
[Aller] Comment utiliser "... (3 périodes)"
Utilisez Maxout + CNN avec Pylearn2
Essayez d'implémenter le parfum avec Go
Utilisez WDC-433SU2M2 avec Manjaro Linux
Utilisez OpenBLAS avec numpy, scipy
Utiliser l'API subsonique avec python3
Utilisation de Sonicwall NetExtener avec Systemd
Utilisez prefetch_related commodément avec Django
Utiliser l'interpréteur AWS avec Pycharm
Utilisation de Bokeh avec IPython Notebook
Utiliser une plage de type Python avec Rust
Je souhaite utiliser une bibliothèque externe avec IBM Cloud Functions