在這篇文章/教程中,我們將使用 NodeJS 創(chuàng)建一個(gè) API 代理服務(wù)器,這背后的主要原因是向您展示如何隱藏公共 API 密鑰,而不是像我過去那樣公開它們。我們所有人都創(chuàng)建了一個(gè)應(yīng)用程序,只是為了了解一個(gè)新的庫或框架,而實(shí)現(xiàn)這一點(diǎn)的最佳方法是使用開放 API(如TMDB)。
這些開放 API 中的大多數(shù)都需要在發(fā)出請求之前在請求 URL 或標(biāo)頭中添加 API 密鑰。無論哪種方式,API 密鑰都可能被實(shí)際上不擁有此密鑰的人竊取和使用。因此,您最終可能會被暫停帳戶,或者您的應(yīng)用程序無法按預(yù)期運(yùn)行。
請記住:API 密鑰屬于應(yīng)用程序的服務(wù)器端。
話不多說,讓我們創(chuàng)建這個(gè) API 代理服務(wù)器。為了這篇文章/教程的緣故,我們將使用Wea?ther API。
在開始編碼之前,我們需要具備以下條件:
創(chuàng)建應(yīng)用程序的項(xiàng)目文件夾
殼
cd ~/Documents/tutorials
mkdir api-proxy-server && cd api-proxy-server
npm init -y
安裝所需的 NPM 包
npm install -S express cors dotenv axios
npm install -D nodemon # for faster development
在 package.json 文件中準(zhǔn)備腳本
JSON
{
"scripts": {
"start": "node index.js",
"dev": "nodemon --config nodemon.json"
}
}
讓我們看一下項(xiàng)目的文件夾和文件,以便更清晰地了解結(jié)構(gòu)。
server/
├── src
│ ├── utils
│ │ └── env.js
│ └── routes
│ └── index.js
├── package.json
├── nodemon.json
├── index.js
└── .env
3 directories, 6 files
在項(xiàng)目的根目錄下創(chuàng)建一個(gè)index.js和一個(gè)文件。nodemon.json
JSON
// nodemon.json
{
"verbose": true,
"ignore": ["node_modules/"],
"watch": ["./**/*"]
}
在我們開始編輯 entry ( index.js) 文件之前,我們需要?jiǎng)?chuàng)建另外兩個(gè)有用的文件。第一個(gè)是.env我們所有環(huán)境變量所在的文件。它將有點(diǎn)可重用,以便能夠與除 OpenWeatherMap API 之外的其他公共 API 一起使用。
API_PORT = 1337
API_BASE_URL = "https://api.openweathermap.org/data/2.5/weather"
API_KEY = "1d084c18063128c282ee3b41e91b6740" # not actually api key; use a valid one
另一個(gè)文件是src/utils/env.js負(fù)責(zé)讀取和導(dǎo)出所有環(huán)境變量的文件。
JavaScript
require("dotenv").config();
module.exports = {
port: Number(process.env.API_PORT) || 3000,
baseURL: String(process.env.API_BASE_URL) || "",
apiKey: String(process.env.API_KEY) || "",
};
現(xiàn)在是時(shí)候編輯主文件并向這個(gè)代理服務(wù)器應(yīng)用程序添加一些邏輯了。
JavaScript
// imports
const express = require("express");
const cors = require("cors");
const env = require("./src/utils/env");
// Application
const app = express();
// Middlewares
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(cors());
// Routes
app.use("/api", require("./src/routes")); // Every request that starts with /api will be handled by this handler
// This route will handle all the requests that are not handled by any other route handler
app.all("*", (request, response, next) => {
return response.status(404).json({ message: "Endpoint not found!" });
});
// Bootstrap server
app.listen(env.port, () => {
console.log(`Server is up and running at http://localhost:${env.port}`);
});
這里的事情很簡單;它是一個(gè)基本的 ExpressJs 服務(wù)器應(yīng)用程序。在頂部,我們有所需的導(dǎo)入。然后我們有一些需要的中間件,例如JSON中間件,它負(fù)責(zé)解析帶有 JSON 有效負(fù)載的傳入請求,并且基于body-parser. 接下來,我們在此處管理端點(diǎn)及其處理程序的路由。最后,我們得到了服務(wù)器的引導(dǎo)。
讓我們看一下/api路由的處理程序。
JavaScript
// server/src/routes/index.js
const router = require("express").Router();
const { default: axios } = require("axios");
const env = require("../utils/env");
// [GET] Current weather data
router.get("/", async (request, response, next) => {
try {
const query = request.query || {};
const params = {
appid: env.apiKey, // required field
...query,
};
const { data } = await axios.get(env.baseURL, { params });
return response.status(200).json({
message: "Current weather data fetched!",
details: { ...data },
});
} catch (error) {
const {
response: { data },
} = error;
const statusCode = Number(data.cod) || 400;
return response
.status(statusCode)
.json({ message: "Bad Request", details: { ...data } });
}
});
module.exports = router;
請求處理函數(shù)將GET我們在調(diào)用它時(shí)提供的查詢字段作為輸入。
例如,如果我們沒有這個(gè) API 代理服務(wù)器,我們的請求看起來像這樣https://api.openweathermap.org/data/2.5/weather?q=Thessaloniki,Greece&appid={API key},但是現(xiàn)在我們已經(jīng)有了它,我們的請求看起來像這樣http://localhost/api?q=Thessaloniki,Greece。不需要添加必填字段appid,因?yàn)榇矸?wù)器自己將其添加為請求參數(shù)。當(dāng)我們在 OpenWeatherMap API 上發(fā)出請求時(shí),我們盡量不顯示 API 密鑰。
主要概念是將您的公共 API 密鑰隱藏在 API 代理服務(wù)器后面,以防止用戶竊取您的密鑰。為了實(shí)現(xiàn)這一點(diǎn),它是創(chuàng)建端點(diǎn)來包裝實(shí)際開放 API 的端點(diǎn),在需要此密鑰的每個(gè)請求中提供代理服務(wù)器內(nèi)的 API 密鑰作為環(huán)境變量。這種方法的明顯缺點(diǎn)是這是一個(gè)耗時(shí)的過程,在某些情況下甚至不需要。
您可以在此處獲取此 API 代理服務(wù)器的源代碼。
我正在考慮向這個(gè)代理服務(wù)器添加一些功能。其中之一是添加緩存功能,以便更快地提供最近請求的數(shù)據(jù)。另一種是添加速率限制器,以防止用戶過度使用 API 并導(dǎo)致服務(wù)器崩潰。
如果您想到了另一種隱藏 API 密鑰的解決方案,或者您有任何提示或技巧要告訴我,請發(fā)表評論。
效果圖:
2 html骨架:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--網(wǎng)頁標(biāo)題-->
<title>detection</title>
<!--內(nèi)部css部分-->
<style>
......
</style>
</head>
<body>
<!--div部分-->
<div class="contain">
......
</div>
<!--內(nèi)部js部分-->
<script>
......
</script>
</body>
</html>
3 css部分:
<!--內(nèi)部css部分-->
<style>
/*網(wǎng)頁和body整體設(shè)置*/
html,body{
margin:0;
padding:0;
/*網(wǎng)頁背景顏色設(shè)置*/
background-color: rgb(96, 94, 212);
}
/*class='contain'在css的前面有點(diǎn)*/
.contain{
width:200px;
height: 200px;
font-size:25px;
text-align: center;
position: absolute;
left:50%;
top:50%;
transform:translate(-50%,-50%);
z-index:30;
}
/*警示label和結(jié)果label*/
.alertInfo,.resultInfo{
color:rgb(12, 231, 213);
font-weight: bold;
/*可以寫在這里,注意數(shù)字和px緊緊相鄰*/
width: 350px;
height: 10px;
}
/*輸入框的設(shè)置*/
.text{
width:150px;
/*文本框默認(rèn)顯示,下面就是不顯示*/
/*outline:none;*/
text-align: center;
font-size:25px;
color:blue;
}
/*顯示框的設(shè)置*/
.show{
font-size:25px;
color:red;
}
</style>
4 body的div部分:
<!--div部分-->
<div class="contain">
<!--注意:style可以嵌套在里面,但不推薦,也可以單獨(dú)寫在上面的css內(nèi)-->
<!--p class="alertInfo" style="width: 350px; height: 10px">顯示倒5個(gè)字符串的輸入框:</!--p-->
<p class="alertInfo" >顯示倒5個(gè)字符串的輸入框:</p>
<!--placeholder是指輸入框默認(rèn)顯示文字-->
<input type="text" class="text" placeholder="請輸入內(nèi)容">
<!--p class="resultInfo" style="width: 350px; height: 10px">顯示倒4個(gè)字符串的顯示區(qū):</!--p-->
<p class="resultInfo" >顯示倒4個(gè)字符串的顯示區(qū):</p>
<p class="show"></p>
</div>
5 body的js=JavaScript=script部分:
<!--內(nèi)部js部分-->
<script>
// 被let聲明的變量不會作為全局對象window的屬性,而被var聲明的變量卻可以
//text和show均是class,所以前面有一個(gè)點(diǎn)
let input=document.querySelector(".text");
let show= document.querySelector(".show");
input.addEventListener('keyup',debounce(handle,100));
// 防抖處理
function debounce(func,wait){
let timeflag;
return function(){
clearTimeout(timeflag); //清除100ms之內(nèi)之前觸發(fā)的定時(shí)器。
let arg=arguments;
let timethis = this;
timeflag = setTimeout(func.bind(timethis,arg),wait);
}
}
//回調(diào)函數(shù)
function handle(){
//輸入框內(nèi)倒取5個(gè)字符串
input.value=input.value.slice(-5);
//輸入框內(nèi)倒取4個(gè)字符串
show.textContent=input.value.slice(-4);
}
</script>
6 html部分基礎(chǔ)學(xué)習(xí),自己整理并分享出來。
文將介紹驗(yàn)證碼的歷史與發(fā)展、驗(yàn)證碼破解的歷史與發(fā)展,驗(yàn)證碼破解全流程實(shí)戰(zhàn)。
驗(yàn)證碼,全稱為“Completely Automated Public Turing test to tell Computers and Humans Apart”,即全自動(dòng)區(qū)分計(jì)算機(jī)和人類的圖靈測試,Captcha。早在上個(gè)世紀(jì)90年代,為了防止惡意的網(wǎng)絡(luò)機(jī)器人行為,像郵件轟炸、暴力破解密碼等,驗(yàn)證碼應(yīng)運(yùn)而生。
最初的驗(yàn)證碼是簡單的文本字符,如用戶只需輸入一組扭曲的字母和數(shù)字。然后驗(yàn)證碼發(fā)展到圖像驗(yàn)證碼,例如,要求用戶識別哪些圖片中包含某個(gè)特定對象(比如貓、狗或汽車等)。隨著技術(shù)的發(fā)展,更為復(fù)雜的驗(yàn)證碼類型出現(xiàn)了,例如邏輯驗(yàn)證碼(例如,3+4=?),音頻驗(yàn)證碼(用戶必須聽音頻然后輸入聽到的字符)和3D驗(yàn)證碼(用戶需要解讀3D對象或者場景)。
此外,也有一些新的驗(yàn)證碼設(shè)計(jì),為了提高用戶體驗(yàn)同時(shí)維護(hù)網(wǎng)站安全,它們需要用戶進(jìn)行更為人性化的操作。例如,滑動(dòng)驗(yàn)證碼讓用戶通過滑動(dòng)解鎖,點(diǎn)擊驗(yàn)證碼讓用戶點(diǎn)擊特定的圖片或文字,旋轉(zhuǎn)驗(yàn)證碼則要求用戶調(diào)整圖片到正確的方向。
一些大公司也開發(fā)了自己的驗(yàn)證碼系統(tǒng)。例如,Google的reCAPTCHA v2引入了復(fù)雜的圖像識別任務(wù),需要用戶選擇包含特定物體(如汽車,交通燈)的圖片;而Google的reCAPTCHA v3則摒棄了用戶交互的方式,通過分析用戶的行為模式來確定是人類還是機(jī)器。同樣,第三方驗(yàn)證服務(wù)如GeeTest CAPTCHA和hCaptcha等,也為網(wǎng)站提供了驗(yàn)證服務(wù),使得他們可以更好地防止自動(dòng)化的惡意行為。
驗(yàn)證碼破解的歷史,與驗(yàn)證碼的發(fā)展緊密相連。早期的驗(yàn)證碼破解主要依賴于OCR(Optical Character Recognition,光學(xué)字符識別)技術(shù),這是一種將圖像中的文本轉(zhuǎn)換為機(jī)器可讀的字符的技術(shù),用于識別簡單的文本驗(yàn)證碼。
然而,隨著驗(yàn)證碼的復(fù)雜性的增加,驗(yàn)證碼破解也需要更為復(fù)雜的技術(shù)。例如,對于圖像驗(yàn)證碼,可能需要使用圖像處理技術(shù)來處理噪聲和扭曲。這可能包括灰度化(將圖像轉(zhuǎn)換為黑白),二值化(將圖像進(jìn)一步簡化為只有黑和白兩種顏色),邊緣檢測(識別圖像中的邊緣)等步驟。
對于更為復(fù)雜的驗(yàn)證碼,例如點(diǎn)擊驗(yàn)證碼和旋轉(zhuǎn)驗(yàn)證碼,可能需要使用更復(fù)雜的機(jī)器視覺技術(shù)。這可能涉及到特征提?。ㄗR別圖像中的重要特征),對象識別(識別特定的對象或形狀),甚至深度學(xué)習(xí)(訓(xùn)練模型來識別復(fù)雜的模式)。
近年來,隨著人工智能的發(fā)展,機(jī)器學(xué)習(xí)和深度學(xué)習(xí)等技術(shù)也被應(yīng)用于驗(yàn)證碼破解中。例如,卷積神經(jīng)網(wǎng)絡(luò)(CNN)已經(jīng)被用來識別復(fù)雜的圖像驗(yàn)證碼,而遞歸神經(jīng)網(wǎng)絡(luò)(RNN)可以用于識別音頻驗(yàn)證碼。這些模型通過在大量的數(shù)據(jù)上進(jìn)行訓(xùn)練,可以學(xué)習(xí)到識別驗(yàn)證碼的復(fù)雜模式,大大提高了驗(yàn)證碼破解的準(zhǔn)確性和效率。
人工驗(yàn)證碼識別服務(wù)是一種基于人工智能或人工勞動(dòng)力的驗(yàn)證碼識別解決方案。當(dāng)機(jī)器無法識別復(fù)雜的驗(yàn)證碼時(shí),這種服務(wù)能夠提供相對高效且準(zhǔn)確的解決方案。
2Captcha是一種基于人工勞動(dòng)力的驗(yàn)證碼識別服務(wù)。它提供了一個(gè)API接口,允許開發(fā)者將無法識別的驗(yàn)證碼發(fā)送到2Captcha服務(wù)。然后2Captcha的工人會手動(dòng)識別并返回結(jié)果。這種服務(wù)對處理圖像驗(yàn)證碼、文本驗(yàn)證碼、點(diǎn)擊類驗(yàn)證碼、GeeTest、reCAPTCHA、FunCaptcha等復(fù)雜驗(yàn)證碼有很高的準(zhǔn)確率,并且提供多種編程語言的接口文檔Python、PHP、Java、Go、Ruby、C++、C#。2Captcha的主要優(yōu)點(diǎn)是其優(yōu)異的精確性和靈活的API,使得開發(fā)者可以輕松集成并在不同環(huán)境中使用。
云碼基于圖像識別技術(shù)和人工輔助提供驗(yàn)證碼識別服務(wù),提供在線普通圖片、滑動(dòng)、點(diǎn)選、谷歌、HCaptcha、數(shù)字計(jì)算題驗(yàn)證碼識別服務(wù)。其對于圖像類的驗(yàn)證碼有比較好的效果,尤其是各種不同類型的圖像驗(yàn)證碼。但其對于復(fù)雜的驗(yàn)證碼存在準(zhǔn)確率下降和識別時(shí)間較長的情況、驗(yàn)證碼種類跟進(jìn)相較也會慢一些。
冰拓可識別各種常見圖片驗(yàn)證碼,AI識別 + 真人識別雙模式,可高效識別坐標(biāo)題、計(jì)算題、字符題、滑塊題、拼圖題等各種圖片。API支持Python、JAVA、PHP、JAVASCRIPT調(diào)用,支持按鍵精靈集成。對于多樣化的滑塊、拼圖、旋轉(zhuǎn)、坐標(biāo)有自己獨(dú)特的處理方法和提供定制服務(wù),不支持谷歌驗(yàn)證碼。
超級鷹是專業(yè)的人工打碼平臺,對圖片數(shù)據(jù)進(jìn)行精準(zhǔn)、快速分類處理,并實(shí)時(shí)返還分類結(jié)果。支持英文數(shù)字、中文漢字、坐標(biāo)選擇計(jì)算等多種類型圖片驗(yàn)證碼,并且提供定制化的驗(yàn)證碼識別服務(wù)。對于通用的驗(yàn)證碼、傳統(tǒng)驗(yàn)證碼有較好的識別效果,但對于復(fù)雜驗(yàn)證碼尚未提供更多服務(wù)。
安裝2captcha-python
pip3 install 2captcha-python
破解驗(yàn)證碼
# 導(dǎo)入BeautifulSoup、TwoCaptcha、requests庫
from bs4 import BeautifulSoup
from twocaptcha import TwoCaptcha
import requests
# TwoCaptcha服務(wù)的API秘鑰,你需要使用自己的
API_KEY = 'xxxxxxxxxxxxxx'
# 利用TwoCaptcha庫,使用提供的API秘鑰初始化一個(gè)solver對象,該對象可以解決ReCAPTCHA問題
solver = TwoCaptcha(API_KEY)
# 要抓取的網(wǎng)頁的URL
url = "https://www.scrapebay.com/spam"
# 這是ReCAPTCHA的site key,可以從網(wǎng)頁源碼中找到。
site_key='6LfGNEoeAAAAALUsU1OWRJnNsF1xUvoai0tV090n'
# 這個(gè)函數(shù)用來獲取CSRF token和cookies。它首先通過requests.get()獲取頁面內(nèi)容,然后通過BeautifulSoup找到CSRF token。最后返回CSRF token和cookies。
def get_csrf_cookie(url):
response = requests.get(url)
soup = BeautifulSoup(response.text, "lxml")
csrf_el = soup.select_one('[name=csrfmiddlewaretoken]')
csrf = csrf_el['value']
cokkies = response.cookies
return csrf, cokkies
# 這個(gè)函數(shù)用來解決ReCAPTCHA問題。它使用TwoCaptcha solver對象的recaptcha()方法,如果發(fā)生異常則打印錯(cuò)誤并退出。
def solve(url,sitekey):
try:
result = solver.recaptcha(sitekey=sitekey, url=url)
except Exception as e:
print(e)
exit()
return result
# 首先通過get_csrf_cookie(url)獲取CSRF token和cookies,然后通過solve(url,site_key)解決ReCAPTCHA問題,獲得ReCAPTCHA的驗(yàn)證碼結(jié)果
def main():
csrf,cokkies = get_csrf_cookie(url)
print("csrf:",csrf)
print("cokkies:",cokkies)
result = solve(url,site_key)
print("captcha:",result)
if __name__ == "__main__":
main()
運(yùn)行結(jié)果:
包含破解驗(yàn)證碼的全部代碼如下:
# 導(dǎo)入BeautifulSoup、TwoCaptcha、requests庫
from bs4 import BeautifulSoup
from twocaptcha import TwoCaptcha
import requests
# 2Captcha服務(wù)的API秘鑰,你需要使用自己的
API_KEY = 'xxxxxxxxxxxxxx'
# 利用TwoCaptcha庫,使用提供的API秘鑰初始化一個(gè)solver對象,該對象可以解決ReCAPTCHA問題
solver = TwoCaptcha(API_KEY)
# 要抓取的網(wǎng)頁的URL
url = "https://www.scrapebay.com/spam"
# 這是ReCAPTCHA的site key,可以從網(wǎng)頁源碼中找到。
site_key='6LfGNEoeAAAAALUsU1OWRJnNsF1xUvoai0tV090n'
# 這個(gè)函數(shù)用來獲取CSRF token和cookies。它首先通過requests.get()獲取頁面內(nèi)容,然后通過BeautifulSoup找到CSRF token。最后返回CSRF token和cookies。
def get_csrf_cookie(url):
response = requests.get(url)
soup = BeautifulSoup(response.text, "lxml")
csrf_el = soup.select_one('[name=csrfmiddlewaretoken]')
csrf = csrf_el['value']
cokkies = response.cookies
return csrf, cokkies
# 這個(gè)函數(shù)用來解決ReCAPTCHA問題。它使用TwoCaptcha solver對象的recaptcha()方法,如果發(fā)生異常則打印錯(cuò)誤并退出。
def solve(url,sitekey):
try:
result = solver.recaptcha(sitekey=sitekey, url=url)
except Exception as e:
print(e)
exit()
return result
# 這個(gè)函數(shù)用來提交解決ReCAPTCHA后的頁面。它首先構(gòu)建一個(gè)POST請求的payload,然后通過requests.post()方法發(fā)送請求。最后返回網(wǎng)頁的最后一列的文本。
def post_page(url, csrf, cookie, result):
payload = 'csrfmiddlewaretoken={}&g-recaptcha-response={}'
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Referer': 'https://www.scrapebay.com/spam'
}
response = requests.post(url,data=payload.format(csrf,result),headers=headers,cookies=cookie)
soup = BeautifulSoup(response.text, "lxml")
el = soup.select_one('td:last-child')
return el.get_text()
# 先通過get_csrf_cookie(url)獲取CSRF token和cookies,然后通過solve(url,site_key)解決ReCAPTCHA問題,最后通過post_page(url,csrf,cokkies,result)提交頁面并打印出結(jié)果。
def main():
csrf,cokkies = get_csrf_cookie(url)
print("csrf:",csrf)
print("cokkies:",cokkies)
result = solve(url,site_key)
print("captcha:",result)
data = post_page(url,csrf,cokkies,result)
print("result:",data)
if __name__ == "__main__":
main()
網(wǎng)站驗(yàn)證后的頁面:
運(yùn)行結(jié)果:
至此我們使用2Captcha服務(wù)破解了reCAPTCHA v2,并獲得了需要爬取的內(nèi)容。2Captcha服務(wù)包含多種驗(yàn)證碼格式,均可以使用上述的流程,修改其中不同驗(yàn)證碼的細(xì)節(jié)部分,攻克驗(yàn)證碼的識別難點(diǎn)。
*請認(rèn)真填寫需求信息,我們會在24小時(shí)內(nèi)與您取得聯(lián)系。