현재 진행중인 프로젝트에서 웹페이지 url을 입력받아 Open Graph 정보를 표시하는 작업을 하려고 합니다.
카카오톡이나 소셜 sns를 사용할 때 url만 입력해도 이미지와 제목이 뜨는걸 보셨을꺼에요.
일반적으로 웹 페이지를 만들때 약속된 meta tag라고 할 수 있는데요, 이런걸 Open Graph 프로토콜이라고 합니다.
https://ogp.me/
Open Graph protocol
The Open Graph protocol enables any web page to become a rich object in a social graph.
ogp.me
# og meta tag 확인
쿠팡을 기준으로 작업을 해보겠습니다.
쿠팡 사이트에 들어가서 맥북을 검색해 본 뒤 크롬 기준으로 [도구 더보기 -> 개발자 도구] 항목을 클릭합니다.
펼쳐진 개발자 도구에서 'Elements' 탭을 선택하면 HTML요소들어 보여집니다.
head tag부분에서 아래와같이 link, meta, script 태그들이 보이네요.
저는 여기서 og: 라고 시작되는 태그들만을 원합니다.
보통 다른 쇼핑몰 같은 경우에도 image, title, url, description 항목은 필수적으로 사용하고 있는데요.
이 정보들이 소셜 sns나 채팅에 공유될때 나오는 정보들입니다.
image - 상품의 이미지
title - 상품 제목
url - 해당 상품 url
description - 간략한 설명
# 개발 준비
Flutter 프레임워크에서는 특정 패키지가 필요합니다.
http 패키지로 사이트의 html문자열을 가져온 다음, html 패키지로 문자열을 html로 파싱파여 원하는 정보를 가져올겁니다.
https://pub.dev/packages/http
http | Dart Package
A composable, multi-platform, Future-based API for HTTP requests.
pub.dev
https://pub.dev/packages/html/install
html | Dart Package
APIs for parsing and manipulating HTML content outside the browser.
pub.dev
# 페이지 정보 읽기
아래처럼 패키지를 import 해줍니다.
import 'package:http/http.dart' as http;
import 'package:html/dom.dart' as dom;
import 'package:html/parser.dart' as parse;
http.get 함수로 웹페이지 주소를 넣어 호출합니다.
아래처럼만 작성해도 일반적인 사이트에서는 문제 없이 html 문자열을 응답받습니다.
final String uri = "웹 페이지 주소";
final response = await http.get(Uri.parse(uri));
하지만 쿠팡같은 경우에는 pending이 걸려버리고 response가 오지 않는것을 알 수 있습니다.
각종 블로그 에서도 해당 문제에 대해서 다루지만 user-agent를 넣는 방법으로는 동작하지 않습니다.
해결을 위해 쿠팡 사이트에 들어와 다시 [도구 더보기 -> 개발자 도구] 로 진입해서 웹 페이지 요청 항목을 선택한 뒤 [Network -> Headers] 탭으로 들어가보겠습니다.
Request Headers 부분에서 아래와 같이 accept 정보를 넣어주어 압축된 정보를 주고받는걸 알 수 있는데요.
저도 이와 같이 header정보를 추가 해주겠습니다.
그리고 만약 동작하지 않을 수 있다는 부분을 위해 timeout 시간을 2초로 주었습니다.
아래처럼 작성하시면 쿠팡 사이트의 정보까지 문제없이 html문자열을 응답받을 수 있습니다
final response = await http.get(Uri.parse(uri), headers: {
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
}).timeout(const Duration(seconds: 2));
# og tag 추출
이제 html문자열에서 og tag만 추출하면 되겠네요.
위에서 import한 alias를 확인해주시고 코드를 봐주시면 됩니다.
response.body에서 html 문자열만을 추출해 html로 변환하면 Document라는 객체로 변환됩니다.
이 Document 객체에서 어떻게 정보를 꺼내냐면 meta tag 속성을 살펴보면 됩니다.
<meta property="og:title" content="Apple 2020 맥북 에어 13">
[head -> meta 에서 property가 'og:title' 인것의 content라는 속성을 꺼내줘] 라고 한다면 html 패키지에서는 아래처럼 코드를 작성하면 됩니다.
정말 간단하게 원하는 tag 정보를 가져올 수 있습니다.
dom.Document document = parse.parse(response.body);
String? title = document.head
?.querySelector("meta[property='og:title']")
?.attributes['content'];
String? description = document.head
?.querySelector("meta[property='og:description']")
?.attributes['content'];
String? image = document.head
?.querySelector("meta[property='og:image']")
?.attributes['content'];
String? url = document.head
?.querySelector("meta[property='og:url']")
?.attributes['content'];
전체 코드 입니다.
return type은 원하는 model을 생성해서 응답해주면 딱이겠군요!
import 'package:http/http.dart' as http;
import 'package:html/dom.dart' as dom;
import 'package:html/parser.dart' as parse;
static void urlToMeta(String uri) async {
final response = await http.get(Uri.parse(uri), headers: {
'accept-encoding': 'gzip, deflate, br',
'accept-language': 'ko-KR,ko;q=0.9,en-US;q=0.8,en;q=0.7',
}).timeout(const Duration(seconds: 2));
dom.Document document = parse.parse(response.body);
String? title = document.head
?.querySelector("meta[property='og:title']")
?.attributes['content'];
String? description = document.head
?.querySelector("meta[property='og:description']")
?.attributes['content'];
String? image = document.head
?.querySelector("meta[property='og:image']")
?.attributes['content'];
String? url = document.head
?.querySelector("meta[property='og:url']")
?.attributes['content'];
}
# 마무리
Flutter 뿐만 아니라 다른 언어에서도 방법만 안다면 문제없어 원하는 정보를 Crawling 할 수 있습니다.