새로운 Twitter API를 사용하여 원격 개발자 직위를 발표하는 것에 대해 알 수 있는 다섯 가지
78525 단어 reactcareertutorialtwitterapi
이 문서에서는 원격 개발자의 작업 목록을 포함하는 트윗을 트위터 API로 실시간으로 가져오는 법을 배웠습니다.
이 후속 강좌에서는 지난 7일 동안 트위터에 게시된 원격 개발자 일자리의 공백에 대한 다섯 가지 필수 지식을 대답하기 위해 프로그램을 구축하는 방법을 배울 것입니다.
설치 프로그램
다음과 같이 시작해야 합니다.
터미널 창을 열고 create React app by usingnpx을 사용하여 React 응용 프로그램을 부트합니다.
npx create-react-app remote-dev-jobs-analytics
create react app가 실행된 후 새로 만든 원격 개발자 작업 분석 디렉터리로 전환하고 소프트웨어 패키지의 스크립트 블록을 대체합니다.가방에 다음 스크립트 블록을 포함하는 json입니다.json.이 줄들은 개발 또는 생산에서 클라이언트와 서버 백엔드 코드를 동시에 실행하는 명령 단축 방식을 제공할 것이다.cd remote-dev-jobs-analytics
소포.json "scripts": {
"start": "npm run development",
"development": "NODE_ENV=development concurrently --kill-others \"npm run client\" \"npm run server\"",
"client": "react-scripts start",
"server": "nodemon server/server.js",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
[스크립트] 섹션을 업데이트하면 패키지가 삭제됩니다.제이슨은 지금 아래와 같아야 한다.다음에 src/하위 디렉터리의 모든 파일을 삭제합니다.
rm src/*
그리고 src/하위 디렉터리에 index라는 새 파일을 만듭니다.js.이 파일의 코드는 다음과 같다.import React from "react";
import ReactDOM from "react-dom";
import App from "./components/App";
ReactDOM.render(<App />, document.querySelector("#root"));
자격증가장 가까운 검색 노드에 연결하려면 프로그램의 불러오는 영패를 사용하여 인증해야 합니다.승재 영패를 사용하려면 다음과 같은 환경 변수를 설정해야 한다.만약 bash를 셸으로 사용한다면 터미널 창에서 다음 명령을 내릴 수 있습니다.왼쪽 괄호와 오른쪽 괄호를 포함하여 수표 소유자의 지폐로 바꾸십시오.
export TWITTER_BEARER_TOKEN=<YOUR BEARER TOKEN HERE>
서버측 코드먼저 Twitter API에 실제 요청을 보내는 노드 서버를 시작해야 합니다.이 노드 서버는 브라우저 기반 React 클라이언트와 Twitter API 사이의 에이전트로 사용됩니다.노드 서버에서 가장 가까운 검색 엔드포인트에 연결된 API 엔드포인트를 생성해야 합니다.반대로 React 클라이언트의 요청은 로컬 노드 서버로 프록시됩니다.
더 나아가기 전에 cd를 프로젝트 루트 디렉터리로 이동하고 다음 의존항을 설치하십시오
npm install concurrently express body-parser util request http path http-proxy-middleware axios react-router-dom react-twitter-embed react-chartjs-2
다음에 프로젝트 루트 디렉터리에 '서버' 라는 새 하위 디렉터리를 만들고, 이 하위 디렉터리에 '서버.js' 라는 새 파일을 만듭니다.mkdir server
touch server/server.js
이 소스 코드 파일에는 가장 가까운 검색 엔드포인트에 연결되어 트윗을 받는 모든 백엔드 로직이 포함됩니다.서버의 컨텐트입니다.js 파일은 다음과 같습니다.서버.js
const axios = require("axios");
const express = require("express");
const bodyParser = require("body-parser");
const moment = require("moment");
const app = express();
let port = process.env.PORT || 3000;
const BEARER_TOKEN = process.env.TWITTER_BEARER_TOKEN;
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
const searchURL = "https://api.twitter.com/2/tweets/search/recent";
const query =
'(developer OR software) remote (context:66.961961812492148736 OR context:66.850073441055133696) -is:retweet -"business developer"';
const maxResults = 100;
const requestConfig = {
headers: {
Authorization: `Bearer ${BEARER_TOKEN}`,
},
params: {
max_results: maxResults,
query: query,
"tweet.fields": "context_annotations,created_at,public_metrics",
},
};
const authMessage = {
title: "Could not authenticate",
detail: `Please make sure your bearer token is correct.
If using Glitch, remix this app and add it to the .env file`,
type: "https://developer.twitter.com/en/docs/authentication",
};
app.get("/api/search/recent", async (req, res) => {
if (!BEARER_TOKEN) {
res.status(401).send(authMessage);
}
try {
const response = await getSearchResults();
res.send(response);
} catch (e) {
console.log(e);
}
});
const getSearchResults = async (config = requestConfig) => {
try {
const response = await axios.get(searchURL, config);
return response.data;
} catch (e) {
console.log(e);
}
};
const getAllTweets = async () => {
let response = await getSearchResults();
let tweets = [];
while (response.meta.next_token) {
let config = {
...requestConfig,
params: {
...requestConfig.params,
next_token: response.meta.next_token,
},
};
response = await getSearchResults(config);
tweets = tweets.concat(response.data);
}
return tweets;
};
const getCount = async () => {
let response = await getSearchResults();
let resultCount = response.meta.result_count;
while (response.meta.next_token) {
let config = {
...requestConfig,
params: {
...requestConfig.params,
next_token: response.meta.next_token,
},
};
response = await getSearchResults(config);
resultCount = resultCount + response.meta.result_count;
}
return resultCount;
};
const countsByDay = async () => {
let tweets = await getAllTweets();
return tweets.reduce(
(counts, tweet) => ({
...counts,
[moment(tweet.created_at).format("ddd - MM/DD")]:
(counts[moment(tweet.created_at).format("ddd - MM/DD")] || 0) + 1,
}),
{}
);
};
const countsByLanguage = async () => {
let counts = {};
const languages = [
"javascript",
"JavaScript",
"android",
"frontend",
"ios",
"backend",
"node",
"nodejs",
"python",
"react",
"scala",
"c#",
"rails",
"ruby",
"php",
"java",
"blockchain",
".net",
"sql",
"java",
"php",
"golang",
"go",
"wordpress",
];
const tweets = await getAllTweets();
for (tweet of tweets) {
for (language of languages) {
if (
tweet.text.includes(language) ||
tweet.text.includes(language.toUpperCase())
) {
counts[language] = (counts[language] || 0) + 1;
}
}
}
if (counts["JavaScript"]) {
counts["javascript"] += counts["JavaScript"];
delete counts.JavaScript;
}
if (counts["node"]) {
counts["nodejs"] += counts["node"];
delete counts.node;
}
if (counts["golang"]) {
counts["go"] += counts["golang"];
delete counts.node;
}
return counts;
};
const sortCounts = (counts, keyName = "name") => {
let sortedCounts = Object.keys(counts).map((language) => ({
[keyName]: language,
total: counts[language],
}));
sortedCounts.sort((a, b) => {
return b.total - a.total;
});
return sortedCounts;
};
app.get("/api/search/recent/top", async (req, res) => {
if (!BEARER_TOKEN) {
res.status(401).send(authMessage);
}
const tweets = await getAllTweets();
let tweetsByEngagement = {};
for (tweet of tweets) {
const total_engagement = Object.values(tweet.public_metrics).reduce(
(total_engagement, public_metric) => total_engagement + public_metric
);
tweetsByEngagement[tweet.id] = total_engagement;
}
res.send({ result: sortCounts(tweetsByEngagement, "id")[0] });
});
app.get("/api/search/recent/count", async (req, res) => {
if (!BEARER_TOKEN) {
res.status(401).send(authMessage);
}
const results =
req.query.group === "day" ? await countsByDay() : await getCount();
res.send({ count: results });
});
app.get("/api/search/recent/language", async (req, res) => {
if (!BEARER_TOKEN) {
res.status(401).send(authMessage);
}
try {
let results = await countsByLanguage();
results = sortCounts(results);
res.send({ count: results.slice(0, 10) });
} catch (e) {
console.log(e);
}
});
if (process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, "../build")));
app.get("*", (request, res) => {
res.sendFile(path.join(__dirname, ".../build", "index.html"));
});
} else {
port = 3001;
}
app.listen(port, () => console.log(`Listening on port ${port}`));
서버 쪽 코드에서 다음 단점을 구축하고 있습니다group=day
지난 7일 동안 일별 작업 수group=language
는 지난 7일(developer OR software) remote (context:66.961961812492148736 OR context:66.850073441055133696) -is:retweet -"business developer”
이 검색 질의는 키워드 "개발자"또는 "소프트웨어"가 포함된 트윗에서 가장 가까운 검색 엔드포인트가 트윗 텍스트의 키워드 "원격"과 일치하는 것을 나타냅니다.또한 이 검색 질의는 특정 도메인 이름과 엔티티 이름을 포함하는 트윗과 일치하는 컨텍스트 연산자를 사용합니다. "context_annotations": [
{
"domain": {
"id": "65",
"name": "Interests and Hobbies Vertical",
"description": "Top level interests and hobbies groupings, like Food or Travel"
},
"entity": {
"id": "847544972781826048",
"name": "Careers",
"description": "Careers"
}
},
{
"domain": {
"id": "66",
"name": "Interests and Hobbies Category",
"description": "A grouping of interests and hobbies entities, like Novelty Food or Destinations"
},
"entity": {
"id": "961961812492148736",
"name": "Recruitment",
"description": "Recruitment"
}
}
컨텍스트 연산자는 다음 형식의 컨텍스트를 따릅니다.위의 예시 유효 하중에서 보듯이 역 id 65와 66은'취미와 취미 유형'을 대표한다.실체 ID 961961812492148736은'채용'실체를, 실체 ID 847544972781826048은'직업'실체를 대표한다.도메인의 전체 목록에 대해 Tweet Annotations 문서에는 가 포함되어 있습니다.마지막으로 운영자'-is:retweet'과'-비즈니스 개발자'는 검색 결과에서 전송을 배제하고'비즈니스 개발자'를 포함하는 모든 트윗을 배제하는 데 사용된다.리트윗은 검색 결과에서 중복되지 않도록 제외됐고,'비즈니스 개발자'가 포함된 트윗은 이와 무관하기 때문에 제외됐다.
클라이언트 코드
다음 단계에서는 다음 React 어셈블리를 사용하여 이러한 정보를 표시합니다.
응용 프로그램.js - 부모 구성 요소, 모든 다른 구성 요소를 순서대로 보여 줍니다.
트위터js- 모집 정보를 포함하는 트윗 보이기
하루.js- 지난 7일 동안 매일 발표된 트윗 수량을 표시하는 스트라이프 맵
꼭대기js-지난 7일 동안 참여도가 가장 높은 트윗
트위터js-자리 표시자 구성 요소, 발표된 10가지 프로그래밍 언어, 10가지 언어를 표시합니다.js 구성 요소, 이날.js 구성 요소입니다. 여러 개의 추문을 보여 줍니다.js 구성 요소
회전기.js - 마운트된 API 호출에 대한 로드 표시기 보이기
이제 React 구성 요소 생성을 시작해야 합니다./src 하위 디렉터리에 "components"라는 디렉터리를 만듭니다.위의 원본 코드 파일은 이 새 디렉터리에 저장될 것입니다.우선, 응용 프로그램의 부모most 구성 요소를 만듭니다.이 구성 요소는 모든 다른 구성 요소를 보여 줍니다.
응용 프로그램.js
import React from "react";
import { BrowserRouter, Route } from "react-router-dom";
import Tweets from "./Tweets";
const App = () => {
return (
<div className="ui container">
<div className="introduction"></div>
<h1 className="ui header">
<img
className="ui image"
src="/Twitter_Logo_Blue.png"
alt="Twitter Logo"
/>
<div className="content">
Remote Developer Job Analytics
<div className="sub header">Powered by Twitter data</div>
</div>
</h1>
<div className="ui grid">
<BrowserRouter>
<Route exact path="/" component={Tweets} />
</BrowserRouter>
</div>
</div>
);
};
export default App;
다음은 직위 공고를 포함하는 트윗 예시를 보여주는 부모 구성 요소를 만듭니다.트위터js
import React, { useEffect, useState } from "react";
import axios from "axios";
import Tweet from "./Tweet";
import Top from "./Top";
import Day from "./Day";
import Spinner from "./Spinner";
const initialState = {
tweets: [],
};
const Tweets = () => {
const [tweets, setTweets] = useState([]);
const [tweetCount, setTweetCount] = useState(0);
const [topTweetId, setTopTweetId] = useState(null);
const [error, setError] = useState(null);
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
getTweets();
getTweetCount();
getTopTweetId();
}, []);
const getTweets = async () => {
try {
setIsLoading(true);
const response = await axios.get("/api/search/recent");
setTweets(response.data.data);
setIsLoading(false);
} catch (e) {
setError(e.response.data);
setIsLoading(false);
}
};
const getTweetCount = async () => {
try {
const response = await axios.get("/api/search/recent/count");
console.log(response);
setTweetCount(response.data.count);
} catch (e) {
setError(e.response.data);
setIsLoading(false);
}
};
const getTopTweetId = async () => {
const response = await axios.get("/api/search/recent/top");
setTopTweetId(response.data.result.id);
};
const errors = () => {
if (error) {
return (
<div className="sixteen wide column">
<div className="ui message negative">
<div className="header">{error.title}</div>
<p key={error.detail}>{error.detail}</p>
<em>
See
<a href={error.type} target="_blank" rel="noopener noreferrer">
{" "}
Twitter documentation{" "}
</a>
for further details.
</em>
</div>
</div>
);
}
};
const dashboard = () => {
if (!isLoading) {
if (!error) {
return (
<React.Fragment>
<div className="sixteen wide column">
<div className="ui segment">
<div className="ui header center aligned ">
Total number of Tweets
</div>
<div className="ui header center aligned ">{tweetCount}</div>
</div>
</div>
<div className="eight wide column">
<div className="ui segment">
<Top />
</div>
</div>
<div className="eight wide column">
<div className="ui segment">
<Day />
</div>
</div>
<div className="eight wide column">
<div className="ui header">Top Tweet</div>
<Tweet key={topTweetId} id={topTweetId} />
</div>
<div className="eight wide column">
<div className="ui basic segment">
<div className="ui header">Recent Tweets</div>
{tweets.map((tweet) => (
<Tweet key={tweet.id} id={tweet.id} />
))}
</div>
</div>
</React.Fragment>
);
}
} else {
return <Spinner />;
}
};
return (
<React.Fragment>
{errors()}
{dashboard()}
</React.Fragment>
);
};
export default Tweets;
다음으로 참여도가 가장 높은 트윗을 보여주는 구성 요소를 만듭니다.꼭대기js
import React, { useEffect, useState } from "react";
import axios from "axios";
import Spinner from "./Spinner";
const Top = () => {
const [countByLanguage, setCountByLanguage] = useState([]);
const [isLoading, setIsLoading] = useState(false);
const style = {
fontSize: "17px",
};
useEffect(() => {
getTopLanguages();
}, []);
const getTopLanguages = async () => {
setIsLoading(true);
const response = await axios.get("/api/search/recent/language");
setCountByLanguage(response.data.count);
setIsLoading(false);
};
const capitalize = (word) => {
const first_letter = word.slice(0, 1).toUpperCase();
return first_letter + word.slice(1);
};
const displayTopLanuguages = () => {
{
if (!isLoading) {
return countByLanguage.map((count, i) => (
<div style={style} className="item">
{i + 1}. {capitalize(count.name)}
</div>
));
} else {
return <Spinner />;
}
}
};
return (
<React.Fragment>
<div className="ui header">Top Programming Languages</div>
<ul className="ui relaxed list"> {displayTopLanuguages()}</ul>
</React.Fragment>
);
};
export default Top;
다음에 하나의 트윗을 보여주는 구성 요소를 만듭니다.트위터js
import React from "react";
import { TwitterTweetEmbed } from "react-twitter-embed";
const Tweet = ({ id }) => {
const options = {
cards: "hidden",
align: "left",
width: "550",
conversation: "none",
};
return <TwitterTweetEmbed options={options} tweetId={id} />;
};
export default Tweet;
마지막으로 마운트된 API 호출 시 로드 표시기를 표시할 구성 요소를 생성합니다.import React from "react";
const Spinner = () => {
return (
<div>
<div className="ui active centered large inline loader">
<img
className="ui image"
src="/Twitter_Logo_Blue.png"
alt="Twitter Logo"
/>
</div>
</div>
);
};
export default Spinner;
프록시 설정마지막 단계는 클라이언트의 요청 에이전트를 백엔드 서버에 연결하는 것입니다.이를 위해 src/디렉터리에 'setupProxy.js' 라는 새 파일을 만들고 다음 코드를 추가합니다.
에이전트를 설정합니다.js
const { createProxyMiddleware } = require("http-proxy-middleware");
// This proxy redirects requests to /api endpoints to
// the Express server running on port 3001.
module.exports = function (app) {
app.use(
["/api"],
createProxyMiddleware({
target: "http://localhost:3001",
})
);
};
이제 프로젝트 루트 디렉토리로 이동하여 다음 명령을 입력하여 서버와 클라이언트를 시작할 수 있습니다.npm start
이 명령이 완료되면 기본 웹 브라우저가 자동으로 시작되고 http://localhost:3000 로 이동합니다. 지난 7일간의 채용 공고와 소개에 제출한 모든 질문에 대한 답변 예시를 볼 수 있습니다.결론
최근 검색 노드를 사용하여 지난 7일 동안 원격 개발자 직위 공고에 대한 질문에 대답할 프로그램을 만들었습니다.이러한 질문에 대한 답은 포부를 가진 개발자나 이미 일자리가 있고 다음 일자리를 어떻게 찾는지에 대해 더욱 전략적인 개발자에게 매우 유용하다.
너는 이 앱을 확장할 재미있는 방법을 찾았니?트위터로 알려줘.이 자습서는 Twitter API 이외의 몇 개의 라이브러리를 사용해서 만들었지만, 서로 다른 수요와 요구가 있을 수 있습니다. 이 도구들이 당신에게 적합한지 평가해야 합니다.
Reference
이 문제에 관하여(새로운 Twitter API를 사용하여 원격 개발자 직위를 발표하는 것에 대해 알 수 있는 다섯 가지), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/twitterdev/top-5-things-you-can-learn-about-remote-developer-job-postings-with-the-new-twitter-api-8p5텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)