현재 이 블로그 플랫폼인 velog(https://velog.io)는 각 게시물의 조회수는 공개하고 있지만(게시물-통계-전체에서 확인 가능) 총 조회수는 공개하고 있지 않다.
하지만 최근 총 조회수를 확인해야 할 일이 생겨 velog의 총 조회수를 구해보기로 했다.
이를 위해 세가지 방법을 썼고, 마지막 방법에서 성공했다.
인터넷에 공개된 velog 총 조회수 툴 이용하기
selenium으로 크롤링하기
HTTP Request, Response
이 포스팅에서는 성공한 세번째 방법을 살펴보려고 한다.
지금까지 내가 쓴 총 99개 게시물들의 조회수를 구할 수 있었고, 실제 조회수와 일치하는 것도 확인했다.
그리고 이들의 합을 구하여 총 조회수(45360번)를 구할 수 있었다🥳
먼저 조회수 데이터를 받아오는 API를 찾아야 한다. 이곳에 Request를 보내 받은 Response로 조회수를 확인할 것이기 때문이다.
이 graphql이 뭔지 확인해보자. graphql 클릭 후 Headers에 가보면 Request URL에 https://v2cdn.velog.io/graphql 가 보인다. 이곳에 Request를 보내서 데이터를 받아오는 것임을 알 수 있다.
Response를 클릭해봤다. 이곳에 조회수가 있는 것을 확인했다.
Payload를 클릭해보자. 조회수를 알기 위해 Request를 보낼 때 post_id가 필요하다는 것을 알게 되었다. 즉, 모든 게시물의 post_id를 알아야 한다.
만약 graphql을 클릭했는데 위의 사진과 같지 않다면(조회수 데이터가 안보인다면) 다른 request을 클릭한 것이다. Response에 조회수 데이터가 있는 graphql request을 찾아야 한다.
이제 https://v2cdn.velog.io/graphql에 Request를 보내야 한다. 이때 Header에 Cookie의 refresh_token, access_token이 필요하다. 이는 개발자도구-Application-Cookies에서 확인할 수 있다.
하지만, 이를 직접 Request Header에 넣어서 수작업으로 Header를 작성하지 않아도 되는 방법이 있다.
아까 통계 페이지에서 찾은 graphql request 오른쪽 마우스 클릭 - Copy - Copy as fetch를 하면 쉽고 빠르게 fetch 함수를 작성할 수 있다.
복사한 fetch 함수는 아래와 같다.
fetch("https://v2cdn.velog.io/graphql", {
"headers": {
"accept": "*/*",
"accept-language": "en-US,en;q=0.9,ko;q=0.8",
"content-type": "application/json",
"sec-ch-ua": "\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site"
},
"referrer": "https://velog.io/",
"referrerPolicy": "strict-origin-when-cross-origin",
"body": "{\"operationName\":\"GetStats\",\"variables\":{\"post_id\":\"2408eec9-f895-4ab5-a9...생략\"},\"query\":\"query GetStats($post_id: ID!) {\\n getStats(post_id: $post_id) {\\n total\\n count_by_day {\\n count\\n day\\n __typename\\n }\\n __typename\\n }\\n}\\n\"}",
"method": "POST",
"mode": "cors",
"credentials": "include"
});
보면 access_token이나 refresh_token에 대한 정보가 없다. 이를 가능하게 해주는 것은 "credentials": "include"
속성인데, 이에 대한 설명은 mdn 문서나 모던 자바스크립트 튜토리얼에서 확인할 수 있다.
여기에 받아온 값을 콘솔로 찍어보기 위해 마지막에 .then(response => response.json()).then(json => console.log(json))
을 추가한다. 이를 콘솔에 찍어보면 아래와 같이 해당 post_id를 가진 게시물의 조회수를 얻을 수 있다!!
구글 기본 페이지에서 콘솔을 찍어서일 수도 있다. 이유는 정확히 모르겠지만 지금까지 특별한 이유 없는 콘솔 오류가 발생했을 때 구글 기본 페이지가 아닌 다른 곳에서 보면 해결된 경우가 있었다.
자신의 블로그 메인 페이지(들어가자마자 보이는 페이지)에서 개발자도구-Network를 클릭한 후 새로고침하면 아까와 같은 graphql을 확인할 수 있는데, 그걸 클릭한 후 Response 탭을 보면 id를 포함한 모든 포스팅 정보가 들어있는 것을 볼 수 있다.
모든 포스팅의 id를 추출하기 위해, 이 Response를 복붙해서 postData라는 변수에 넣어주고 map 함수로 id만을 추출했다.
const postData = {
"data": {
"posts": [
{
"id": "2408eec9-f895-4ab5-376d...",
"title": "[퀴즈앱] 컴퓨터 상식 퀴즈앱 만들기",
"__typename": "Post"
},
{
"id": "15b28641-c8cc-47a0-d304...",
"title": "패턴 바꾸기 & 5월 회고",
"__typename": "Post"
},
...
]
}
};
const postIdArray = postData.data.posts.map(post => post.id)
console.log(postIdArray)
post_id를 차례대로 바꿔가서 요청을 보내고, 조회수를 받아온다. 그리고 그걸 더해서 totalSum을 구한다.
const postIdArray = ["2408eec9-f895-4ab5-a9ea-376...", "b43d47b8-a96b-4c91-9fe1-7e5...", ...]
const fetchPromises = postIdArray.map(postId => {
const requestBody = {
operationName: "GetStats",
variables: {
post_id: postId
},
query: "query GetStats($post_id: ID!) {\n getStats(post_id: $post_id) {\n total\n count_by_day {\n count\n day\n __typename\n }\n __typename\n }\n}\n"
};
return fetch("https://v2cdn.velog.io/graphql", {
headers: {
accept: "*/*",
"accept-language": "en-US,en;q=0.9,ko;q=0.8",
"content-type": "application/json",
"sec-ch-ua": "\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\"",
"sec-ch-ua-mobile": "?0",
"sec-ch-ua-platform": "\"Windows\"",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-site"
},
referrer: "https://velog.io/",
referrerPolicy: "strict-origin-when-cross-origin",
body: JSON.stringify(requestBody),
method: "POST",
mode: "cors",
credentials: "include"
})
.then(response => response.json())
.then(json => json.data.getStats.total);
});
Promise.all(fetchPromises)
.then(results => {
console.log(results)
const totalSum = results.reduce((acc, val) => acc + val, 0);
console.log("Total sum:", totalSum);
});
잘 받아와졌다!🥳🥳
총 조회수를 구하는 방법으로 크롤링밖에 생각 못했어서 이 방법으로 문제를 해결할 거라고는 생각도 못했다(처음에 Request를 보내는 것에 대해 하나도 알지 못했음)
하지만 이렇게 해결하게 되어서 신기하다! HTTP Request/Response에 대해 조금 알게된 것 같아 기쁘다. 앞으로 관련 지식을 더 쌓고 싶다.
참고로 이번 포스팅이 velog 100번째 포스팅이다! 아끼는 글이 100개가 되었다니 뿌듯하고 행복하다😆
피드에서 와 같이 Post get all을 해주는 graphql 사용하시면 한 번에 100개까지 가져올 수 있어서 id 추출하기 더 편하실 것 같아요!
혹시 아래 게시글 처럼, 조회수 분석 관련 코드 공유드릴까요!
https://velog.io/@qlgks1/%EB%A6%AC%EB%B7%B0-velog-%EA%B8%B0%EC%88%A0-%EB%B8%94%EB%A1%9C%EA%B7%B8-2%EB%85%84-%ED%9B%84%EA%B8%B0-self-celebration
곧 repo로 하나 파려고하는데,, 아직 정리 하는 중이라 :')...