العودة إلى المدونة

إعداد البروكسي في تطبيقات Node.js: دليل كامل مع أمثلة على الكود

دليل مفصل لدمج البروكسي في Node.js: من الإعداد الأساسي إلى تقنيات متقدمة لتدوير IP ومعالجة الأخطاء.

📅٢٦ شعبان ١٤٤٧ هـ
```html

عند تطوير تطبيقات Node.js للزحف، أو الأتمتة، أو العمل مع واجهات برمجة التطبيقات (API)، غالبًا ما تكون هناك حاجة لاستخدام خوادم البروكسي. وهذا يسمح بتجاوز القيود الجغرافية، وتوزيع الحمل، وتجنب الحظر بناءً على IP. في هذا الدليل، سنستعرض جميع طرق إعداد البروكسي في Node.js - من الأساسيات إلى التقنيات المتقدمة مع تدوير ومعالجة الأخطاء.

المفاهيم الأساسية للعمل مع البروكسي في Node.js

يعمل خادم البروكسي في Node.js كوسيط بين تطبيقك والخادم المستهدف. عندما ترسل طلب HTTP عبر البروكسي، يتصل تطبيقك أولاً بخادم البروكسي، الذي يقوم بعد ذلك بإعادة توجيه الطلب إلى العنوان النهائي. وهذا يسمح بإخفاء عنوان IP الحقيقي لخادمك واستخدام عنوان IP الخاص بالبروكسي.

في Node.js، هناك عدة طرق أساسية للعمل مع البروكسي اعتمادًا على المكتبة المستخدمة لطلبات HTTP. الخيارات الأكثر شيوعًا هي:

  • الوحدات المدمجة http/https - وظيفة أساسية بدون تبعيات إضافية
  • Axios - مكتبة شائعة مع واجهة برمجة تطبيقات مريحة ودعم للوعود
  • Got - بديل حديث مع دعم TypeScript
  • node-fetch - تنفيذ Fetch API لـ Node.js
  • request - مكتبة قديمة ولكن لا تزال مستخدمة (لا يُوصى بها للمشاريع الجديدة)

تدعم خوادم البروكسي بروتوكولات مختلفة. للاستخدام مع Node.js، يتم استخدام:

البروتوكول الوصف الاستخدام
HTTP البروتوكول الأساسي للاتصالات غير المشفرة الزحف، العمل مع واجهات برمجة التطبيقات بدون SSL
HTTPS بروكسي يدعم تشفير SSL/TLS اتصالات آمنة، العمل مع واجهات برمجة التطبيقات المحمية
SOCKS5 بروتوكول عالمي لأي نوع من حركة المرور WebSocket، UDP، سيناريوهات معقدة

لمهام الزحف والأتمتة، غالبًا ما يستخدم المطورون بروكسي سكنية، حيث أن لديها عناوين IP حقيقية لمستخدمي المنازل وتتعرض للحظر أقل من المواقع المستهدفة.

إعداد البروكسي من خلال الوحدة المدمجة http/https

يوفر Node.js وحدات مدمجة http و https للعمل مع طلبات HTTP. يمكن استخدام مكتبة http-proxy-agent أو https-proxy-agent للاتصال بالبروكسي.

أولاً، قم بتثبيت الحزم اللازمة:

npm install http-proxy-agent https-proxy-agent

مثال على استخدام بروكسي HTTP مع الوحدة المدمجة:

const http = require('http');
const { HttpProxyAgent } = require('http-proxy-agent');

// إعدادات البروكسي
const proxyUrl = 'http://username:password@proxy-server.com:8080';
const agent = new HttpProxyAgent(proxyUrl);

// خيارات الطلب
const options = {
  hostname: 'api.example.com',
  path: '/endpoint',
  method: 'GET',
  agent: agent,
  headers: {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
  }
};

// تنفيذ الطلب
const req = http.request(options, (res) => {
  let data = '';
  
  res.on('data', (chunk) => {
    data += chunk;
  });
  
  res.on('end', () => {
    console.log('الاستجابة:', data);
  });
});

req.on('error', (error) => {
  console.error('فشل الطلب:', error.message);
});

req.end();

لاستخدام اتصالات HTTPS، استخدم https-proxy-agent:

const https = require('https');
const { HttpsProxyAgent } = require('https-proxy-agent');

const proxyUrl = 'http://username:password@proxy-server.com:8080';
const agent = new HttpsProxyAgent(proxyUrl);

const options = {
  hostname: 'api.example.com',
  path: '/secure-endpoint',
  method: 'GET',
  agent: agent
};

https.get(options, (res) => {
  let data = '';
  
  res.on('data', (chunk) => {
    data += chunk;
  });
  
  res.on('end', () => {
    console.log('الاستجابة الآمنة:', data);
  });
}).on('error', (error) => {
  console.error('فشل طلب HTTPS:', error.message);
});

يوفر هذا النهج تحكمًا أقصى في الطلبات، ولكنه يتطلب المزيد من الشيفرة لمعالجة الوعود والأخطاء. بالنسبة لمعظم المهام، من الأسهل استخدام المكتبات المتخصصة.

العمل مع البروكسي في مكتبة Axios

Axios - واحدة من أكثر المكتبات شعبية لطلبات HTTP في Node.js. إنها توفر واجهة برمجة تطبيقات مريحة لإعداد البروكسي وتعالج الوعود تلقائيًا.

تثبيت Axios:

npm install axios

إعداد البروكسي الأساسي في Axios يبدو كما يلي:

const axios = require('axios');

// الطريقة 1: إعداد البروكسي في تكوين الطلب
axios.get('https://api.example.com/data', {
  proxy: {
    protocol: 'http',
    host: 'proxy-server.com',
    port: 8080,
    auth: {
      username: 'your-username',
      password: 'your-password'
    }
  }
})
.then(response => {
  console.log('البيانات:', response.data);
})
.catch(error => {
  console.error('خطأ:', error.message);
});

للاستخدام المتكرر لنفس إعدادات البروكسي، من المناسب إنشاء مثيل Axios مع تكوين مسبق:

const axios = require('axios');

// إنشاء مثيل مع إعدادات البروكسي
const axiosWithProxy = axios.create({
  proxy: {
    protocol: 'http',
    host: 'proxy-server.com',
    port: 8080,
    auth: {
      username: 'your-username',
      password: 'your-password'
    }
  },
  timeout: 10000,
  headers: {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
  }
});

// الاستخدام
async function fetchData() {
  try {
    const response = await axiosWithProxy.get('https://api.example.com/data');
    console.log('الاستجابة:', response.data);
    return response.data;
  } catch (error) {
    console.error('فشل الطلب:', error.message);
    throw error;
  }
}

fetchData();

للعمل مع بروكسي HTTPS من خلال طريقة CONNECT، يمكنك استخدام وكلاء إضافيين:

const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');

const proxyUrl = 'http://username:password@proxy-server.com:8080';
const httpsAgent = new HttpsProxyAgent(proxyUrl);

const axiosInstance = axios.create({
  httpsAgent: httpsAgent,
  timeout: 15000
});

// الاستخدام
axiosInstance.get('https://api.example.com/secure-data')
  .then(response => console.log(response.data))
  .catch(error => console.error(error.message));

تقوم Axios بمعالجة إعادة التوجيه تلقائيًا وتدعم الاعتراضات لتسجيل الأخطاء ومعالجتها، مما يجعلها خيارًا ممتازًا للمشاريع التي تستخدم البروكسي بشكل نشط.

إعداد البروكسي في got و node-fetch

Got - بديل حديث لـ Axios مع دعم ممتاز لـ TypeScript وإمكانيات متقدمة لمعالجة الأخطاء. Node-fetch ينفذ واجهة Fetch API القياسية لـ Node.js.

العمل مع البروكسي في Got

تثبيت Got والوكلاء اللازمين:

npm install got https-proxy-agent

مثال على إعداد البروكسي في Got:

const got = require('got');
const { HttpsProxyAgent } = require('https-proxy-agent');

const proxyUrl = 'http://username:password@proxy-server.com:8080';
const agent = {
  https: new HttpsProxyAgent(proxyUrl)
};

// إنشاء مثيل Got مع البروكسي
const gotWithProxy = got.extend({
  agent: agent,
  timeout: {
    request: 10000
  },
  retry: {
    limit: 3,
    methods: ['GET', 'POST']
  }
});

// الاستخدام
async function fetchWithGot() {
  try {
    const response = await gotWithProxy('https://api.example.com/data');
    console.log('الاستجابة:', JSON.parse(response.body));
  } catch (error) {
    console.error('فشل طلب Got:', error.message);
  }
}

fetchWithGot();

العمل مع البروكسي في node-fetch

تتطلب Node-fetch إعدادًا صريحًا للوكيل للعمل مع البروكسي:

npm install node-fetch https-proxy-agent
const fetch = require('node-fetch');
const { HttpsProxyAgent } = require('https-proxy-agent');

const proxyUrl = 'http://username:password@proxy-server.com:8080';
const agent = new HttpsProxyAgent(proxyUrl);

async function fetchWithProxy() {
  try {
    const response = await fetch('https://api.example.com/data', {
      agent: agent,
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'
      }
    });
    
    if (!response.ok) {
      throw new Error(`خطأ HTTP! الحالة: ${response.status}`);
    }
    
    const data = await response.json();
    console.log('البيانات:', data);
    return data;
  } catch (error) {
    console.error('فشل الزحف:', error.message);
    throw error;
  }
}

fetchWithProxy();

يوفر Got وظائف أكثر ثراءً من الصندوق (إعادة المحاولات التلقائية، المهلات، معالجة الأخطاء)، بينما يكون node-fetch أقرب إلى واجهة Fetch API القياسية ومناسب للمطورين المألوفين بـ JavaScript في المتصفح.

الاتصال ببروكسي SOCKS5 من خلال socks-proxy-agent

SOCKS5 - بروتوكول بروكسي عالمي يعمل على مستوى أدنى من HTTP/HTTPS. يدعم أي نوع من حركة المرور، بما في ذلك UDP واتصالات WebSocket. للعمل مع SOCKS5 في Node.js، يتم استخدام مكتبة socks-proxy-agent.

تثبيت الحزم اللازمة:

npm install socks-proxy-agent

مثال على استخدام بروكسي SOCKS5 مع مكتبات مختلفة:

const { SocksProxyAgent } = require('socks-proxy-agent');
const https = require('https');

// إعداد بروكسي SOCKS5 مع المصادقة
const proxyUrl = 'socks5://username:password@proxy-server.com:1080';
const agent = new SocksProxyAgent(proxyUrl);

const options = {
  hostname: 'api.example.com',
  path: '/endpoint',
  method: 'GET',
  agent: agent
};

https.get(options, (res) => {
  let data = '';
  
  res.on('data', (chunk) => {
    data += chunk;
  });
  
  res.on('end', () => {
    console.log('استجابة SOCKS5:', data);
  });
}).on('error', (error) => {
  console.error('فشل طلب SOCKS5:', error.message);
});

استخدام SOCKS5 مع Axios:

const axios = require('axios');
const { SocksProxyAgent } = require('socks-proxy-agent');

const proxyUrl = 'socks5://username:password@proxy-server.com:1080';
const httpsAgent = new SocksProxyAgent(proxyUrl);
const httpAgent = new SocksProxyAgent(proxyUrl);

const axiosWithSocks = axios.create({
  httpAgent: httpAgent,
  httpsAgent: httpsAgent,
  timeout: 15000
});

async function fetchViaSocks5() {
  try {
    const response = await axiosWithSocks.get('https://api.example.com/data');
    console.log('البيانات عبر SOCKS5:', response.data);
    return response.data;
  } catch (error) {
    console.error('خطأ طلب SOCKS5:', error.message);
    throw error;
  }
}

fetchViaSocks5();

تعتبر بروكسي SOCKS5 مفيدة بشكل خاص للمهام التي تتطلب العمل مع بروتوكولات غير قياسية أو عندما تكون هناك حاجة إلى أقصى قدر من المرونة. تقدم العديد من مقدمي الخدمة بروكسي موبايل مع دعم SOCKS5، مما يسمح بمحاكاة حركة مرور الموبايل للعمل مع واجهات برمجة التطبيقات الخاصة بالموبايل.

تنفيذ تدوير البروكسي لتوزيع الحمل

عند الزحف إلى كميات كبيرة من البيانات أو العمل مع واجهات برمجة التطبيقات التي لديها حدود على عدد الطلبات من IP واحد، من الضروري استخدام تدوير البروكسي. وهذا يسمح بتوزيع الطلبات بين عدة خوادم بروكسي وتجنب الحظر.

مثال على تنفيذ تدوير البروكسي البسيط:

const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');

class ProxyRotator {
  constructor(proxyList) {
    this.proxyList = proxyList;
    this.currentIndex = 0;
  }
  
  // الحصول على البروكسي التالي بشكل دائري
  getNextProxy() {
    const proxy = this.proxyList[this.currentIndex];
    this.currentIndex = (this.currentIndex + 1) % this.proxyList.length;
    return proxy;
  }
  
  // الحصول على بروكسي عشوائي
  getRandomProxy() {
    const randomIndex = Math.floor(Math.random() * this.proxyList.length);
    return this.proxyList[randomIndex];
  }
  
  // إنشاء مثيل Axios مع البروكسي الحالي
  createAxiosInstance(useRandom = false) {
    const proxyUrl = useRandom ? this.getRandomProxy() : this.getNextProxy();
    const agent = new HttpsProxyAgent(proxyUrl);
    
    return axios.create({
      httpsAgent: agent,
      timeout: 10000,
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
      }
    });
  }
}

// قائمة البروكسي
const proxyList = [
  'http://user1:pass1@proxy1.example.com:8080',
  'http://user2:pass2@proxy2.example.com:8080',
  'http://user3:pass3@proxy3.example.com:8080',
  'http://user4:pass4@proxy4.example.com:8080'
];

const rotator = new ProxyRotator(proxyList);

// استخدام التدوير
async function fetchWithRotation(url) {
  const axiosInstance = rotator.createAxiosInstance();
  
  try {
    const response = await axiosInstance.get(url);
    console.log('نجاح مع البروكسي');
    return response.data;
  } catch (error) {
    console.error('فشل الطلب:', error.message);
    throw error;
  }
}

// مثال على الزحف الجماعي مع التدوير
async function massiveParsing(urls) {
  const results = [];
  
  for (const url of urls) {
    try {
      const data = await fetchWithRotation(url);
      results.push({ url, success: true, data });
      
      // تأخير بين الطلبات
      await new Promise(resolve => setTimeout(resolve, 1000));
    } catch (error) {
      results.push({ url, success: false, error: error.message });
    }
  }
  
  return results;
}

// بدء الزحف
const urlsToParse = [
  'https://api.example.com/data/1',
  'https://api.example.com/data/2',
  'https://api.example.com/data/3',
  'https://api.example.com/data/4',
  'https://api.example.com/data/5'
];

massiveParsing(urlsToParse)
  .then(results => console.log('نتائج الزحف:', results))
  .catch(error => console.error('خطأ الزحف:', error));

مثال أكثر تقدمًا مع تتبع حالة البروكسي واستبعاد التالفة تلقائيًا:

class AdvancedProxyRotator {
  constructor(proxyList, maxFailures = 3) {
    this.proxyList = proxyList.map(url => ({
      url,
      failures: 0,
      active: true,
      lastUsed: null
    }));
    this.maxFailures = maxFailures;
    this.currentIndex = 0;
  }
  
  // الحصول على البروكسي النشط
  getActiveProxy() {
    const activeProxies = this.proxyList.filter(p => p.active);
    
    if (activeProxies.length === 0) {
      throw new Error('لا توجد بروكسيات نشطة متاحة');
    }
    
    // البحث عن البروكسي الذي لم يستخدم لفترة أطول
    const proxy = activeProxies.reduce((oldest, current) => {
      if (!oldest.lastUsed) return oldest;
      if (!current.lastUsed) return current;
      return current.lastUsed < oldest.lastUsed ? current : oldest;
    });
    
    proxy.lastUsed = Date.now();
    return proxy;
  }
  
  // وضع علامة على البروكسي كناجح
  markSuccess(proxyUrl) {
    const proxy = this.proxyList.find(p => p.url === proxyUrl);
    if (proxy) {
      proxy.failures = 0;
    }
  }
  
  // وضع علامة على البروكسي كفاشل
  markFailure(proxyUrl) {
    const proxy = this.proxyList.find(p => p.url === proxyUrl);
    if (proxy) {
      proxy.failures++;
      if (proxy.failures >= this.maxFailures) {
        proxy.active = false;
        console.warn(`تم تعطيل البروكسي ${proxyUrl} بعد ${proxy.failures} فشل`);
      }
    }
  }
  
  // إنشاء مثيل Axios مع التدوير
  async createAxiosInstance() {
    const proxy = this.getActiveProxy();
    const agent = new HttpsProxyAgent(proxy.url);
    
    return {
      instance: axios.create({
        httpsAgent: agent,
        timeout: 10000
      }),
      proxyUrl: proxy.url
    };
  }
  
  // الحصول على إحصائيات البروكسي
  getStats() {
    return {
      total: this.proxyList.length,
      active: this.proxyList.filter(p => p.active).length,
      inactive: this.proxyList.filter(p => !p.active).length,
      proxies: this.proxyList.map(p => ({
        url: p.url.replace(/\/\/.*@/, '//***@'), // إخفاء بيانات الاعتماد
        active: p.active,
        failures: p.failures
      }))
    };
  }
}

// استخدام الروتات المتقدمة
const advancedRotator = new AdvancedProxyRotator(proxyList);

async function fetchWithAdvancedRotation(url) {
  const { instance, proxyUrl } = await advancedRotator.createAxiosInstance();
  
  try {
    const response = await instance.get(url);
    advancedRotator.markSuccess(proxyUrl);
    return response.data;
  } catch (error) {
    advancedRotator.markFailure(proxyUrl);
    throw error;
  }
}

// الإخراج الدوري للإحصائيات
setInterval(() => {
  console.log('إحصائيات البروكسي:', advancedRotator.getStats());
}, 30000);

يوفر هذا النظام استبعاد البروكسيات التالفة تلقائيًا من التدوير وتوزيع الحمل بالتساوي بين الخوادم النشطة. وهذا أمر بالغ الأهمية عند العمل مع كميات كبيرة من البيانات.

معالجة الأخطاء والتبديل التلقائي للبروكسي

عند العمل مع البروكسي، تحدث الأخطاء بشكل حتمي: المهلات، فشل الاتصال، الحظر من الخادم المستهدف. تعزز معالجة الأخطاء الصحيحة والتبديل التلقائي إلى بروكسي آخر موثوقية التطبيق.

مثال على تنفيذ نظام مع إعادة المحاولات التلقائية وتبديل البروكسي:

const axios = require('axios');
const { HttpsProxyAgent } = require('https-proxy-agent');

class ResilientProxyClient {
  constructor(proxyList, maxRetries = 3) {
    this.proxyList = proxyList;
    this.maxRetries = maxRetries;
    this.currentProxyIndex = 0;
  }
  
  // الحصول على البروكسي التالي
  getNextProxy() {
    const proxy = this.proxyList[this.currentProxyIndex];
    this.currentProxyIndex = (this.currentProxyIndex + 1) % this.proxyList.length;
    return proxy;
  }
  
  // تحديد نوع الخطأ
  isRetryableError(error) {
    // الأخطاء التي يجب إعادة المحاولة
    const retryableCodes = ['ECONNRESET', 'ETIMEDOUT', 'ENOTFOUND', 'ECONNREFUSED'];
    const retryableStatuses = [408, 429, 500, 502, 503, 504];
    
    if (error.code && retryableCodes.includes(error.code)) {
      return true;
    }
    
    if (error.response && retryableStatuses.includes(error.response.status)) {
      return true;
    }
    
    return false;
  }
  
  // تنفيذ الطلب مع إعادة المحاولات التلقائية
  async request(url, options = {}, attempt = 1) {
    const proxyUrl = this.getNextProxy();
    const agent = new HttpsProxyAgent(proxyUrl);
    
    const config = {
      ...options,
      httpsAgent: agent,
      timeout: 10000,
      headers: {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
        ...options.headers
      }
    };
    
    try {
      console.log(`المحاولة ${attempt}/${this.maxRetries} مع البروكسي: ${proxyUrl.replace(/\/\/.*@/, '//***@')}`);
      const response = await axios.get(url, config);
      console.log(`نجاح في المحاولة ${attempt}`);
      return response.data;
    } catch (error) {
      console.error(`فشلت المحاولة ${attempt}: ${error.message}`);
      
      // إذا تم الوصول إلى حد إعادة المحاولات
      if (attempt >= this.maxRetries) {
        console.error(`تم الوصول إلى الحد الأقصى من إعادة المحاولات (${this.maxRetries}) لـ ${url}`);
        throw error;
      }
      
      // إذا كان الخطأ غير قابل لإعادة المحاولة
      if (!this.isRetryableError(error)) {
        console.error(`خطأ غير قابل لإعادة المحاولة: ${error.message}`);
        throw error;
      }
      
      // تأخير أسي قبل إعادة المحاولة
      const delay = Math.min(1000 * Math.pow(2, attempt - 1), 10000);
      console.log(`انتظار ${delay} مللي ثانية قبل إعادة المحاولة...`);
      await new Promise(resolve => setTimeout(resolve, delay));
      
      // إعادة المحاولة بشكل متكرر مع البروكسي التالي
      return this.request(url, options, attempt + 1);
    }
  }
  
  // معالجة دفعة من URL مع معالجة الأخطاء
  async batchRequest(urls, concurrency = 3) {
    const results = [];
    const queue = [...urls];
    const active = [];
    
    while (queue.length > 0 || active.length > 0) {
      // بدء مهام جديدة حتى الوصول إلى حد التوازي
      while (active.length < concurrency && queue.length > 0) {
        const url = queue.shift();
        const promise = this.request(url)
          .then(data => ({ url, success: true, data }))
          .catch(error => ({ url, success: false, error: error.message }))
          .finally(() => {
            const index = active.indexOf(promise);
            if (index > -1) active.splice(index, 1);
          });
        
        active.push(promise);
      }
      
      // الانتظار حتى تكتمل واحدة على الأقل من المهام
      if (active.length > 0) {
        const result = await Promise.race(active);
        results.push(result);
      }
    }
    
    return results;
  }
}

// الاستخدام
const proxyList = [
  'http://user1:pass1@proxy1.example.com:8080',
  'http://user2:pass2@proxy2.example.com:8080',
  'http://user3:pass3@proxy3.example.com:8080'
];

const client = new ResilientProxyClient(proxyList, 3);

// طلب فردي
client.request('https://api.example.com/data')
  .then(data => console.log('البيانات:', data))
  .catch(error => console.error('خطأ نهائي:', error.message));

// معالجة دفعة
const urls = [
  'https://api.example.com/data/1',
  'https://api.example.com/data/2',
  'https://api.example.com/data/3',
  'https://api.example.com/data/4',
  'https://api.example.com/data/5'
];

client.batchRequest(urls, 3)
  .then(results => {
    const successful = results.filter(r => r.success).length;
    const failed = results.filter(r => !r.success).length;
    console.log(`اكتمل: ${successful} ناجح، ${failed} فاشل`);
    console.log('النتائج:', results);
  });

تتضمن هذه التنفيذ:

  • التبديل التلقائي إلى البروكسي التالي عند حدوث خطأ
  • تأخير أسي بين إعادة المحاولات (1 ثانية، 2 ثانية، 4 ثوانٍ، 8 ثوانٍ، 10 ثوانٍ كحد أقصى)
  • تحديد نوع الخطأ لاتخاذ قرار بشأن إعادة المحاولة
  • التحكم في التوازي عند معالجة الدفعات
  • تسجيل مفصل للتصحيح

أفضل الممارسات وتحسين الأداء

عند العمل مع البروكسي في تطبيقات Node.js، يجب اتباع التوصيات التالية لضمان الاستقرار والأداء:

1. إدارة الاتصالات

أعد استخدام وكلاء HTTP بدلاً من إنشاء وكلاء جدد لكل طلب. هذا يقلل من الحمل الزائد على إنشاء الاتصالات:

const { HttpsProxyAgent } = require('https-proxy-agent');

// سيء: إنشاء وكيل لكل طلب
async function badExample(url) {
  const agent = new HttpsProxyAgent(proxyUrl);
  return axios.get(url, { httpsAgent: agent });
}

// جيد: إعادة استخدام الوكيل
const agent = new HttpsProxyAgent(proxyUrl);
const axiosInstance = axios.create({
  httpsAgent: agent,
  httpAgent: agent
});

async function goodExample(url) {
  return axiosInstance.get(url);
}

2. إعداد المهلات

دائمًا قم بتعيين مهلات معقولة لمنع تجميد التطبيق:

const axiosInstance = axios.create({
  httpsAgent: agent,
  timeout: 10000, // المهلة العامة
  // إعداد تفصيلي للمهلات لـ Got
  // timeout: {
  //   lookup: 1000,    // بحث DNS
  //   connect: 2000,   // الاتصال بالبروكسي
  //   secureConnect: 2000, // مصافحة SSL
  //   socket: 5000,    // عدم النشاط في المقبس
  //   response: 3000,  // انتظار أول بايت من الاستجابة
  //   send: 10000,     // إرسال الطلب
  //   request: 15000   // الطلب بالكامل
  // }
});

3. التحكم في التوازي

قم بتقييد عدد الطلبات المتزامنة لتجنب التحميل الزائد:

const pLimit = require('p-limit');

// تقييد إلى 5 طلبات متزامنة
const limit = pLimit(5);

async function processUrls(urls) {
  const promises = urls.map(url => 
    limit(() => axiosInstance.get(url))
  );
  
  return Promise.allSettled(promises);
}

4. تدوير User-Agent

اجمع بين تدوير البروكسي وتدوير User-Agent لتحقيق مزيد من الخصوصية:

const userAgents = [
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
  'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
  'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0'
];

function getRandomUserAgent() {
  return userAgents[Math.floor(Math.random() * userAgents.length)];
}

async function fetchWithRandomUA(url) {
  return axiosInstance.get(url, {
    headers: {
      'User-Agent': getRandomUserAgent()
    }
  });
}

5. المراقبة والتسجيل

نفذ نظام مراقبة لتتبع أداء البروكسي:

class ProxyMonitor {
  constructor() {
    this.stats = new Map();
  }
  
  recordRequest(proxyUrl, success, responseTime) {
    if (!this.stats.has(proxyUrl)) {
      this.stats.set(proxyUrl, {
        total: 0,
        success: 0,
        failed: 0,
        totalResponseTime: 0,
        avgResponseTime: 0
      });
    }
    
    const stat = this.stats.get(proxyUrl);
    stat.total++;
    
    if (success) {
      stat.success++;
      stat.totalResponseTime += responseTime;
      stat.avgResponseTime = stat.totalResponseTime / stat.success;
    } else {
      stat.failed++;
    }
  }
  
  getReport() {
    const report = [];
    
    this.stats.forEach((stat, proxyUrl) => {
      report.push({
        proxy: proxyUrl.replace(/\/\/.*@/, '//***@'),
        successRate: ((stat.success / stat.total) * 100).toFixed(2) + '%',
        avgResponseTime: stat.avgResponseTime.toFixed(0) + 'مللي ثانية',
        total: stat.total,
        success: stat.success,
        failed: stat.failed
      });
    });
    
    return report;
  }
}

const monitor = new ProxyMonitor();

// التفاف للطلبات مع المراقبة
async function monitoredRequest(url, proxyUrl) {
  const startTime = Date.now();
  
  try {
    const response = await axiosInstance.get(url);
    const responseTime = Date.now() - startTime;
    monitor.recordRequest(proxyUrl, true, responseTime);
    return response.data;
  } catch (error) {
    const responseTime = Date.now() - startTime;
    monitor.recordRequest(proxyUrl, false, responseTime);
    throw error;
  }
}

// تقرير دوري
setInterval(() => {
  console.table(monitor.getReport());
}, 60000);

6. اختيار نوع البروكسي

اختر نوع البروكسي بناءً على المهمة:

المهمة النوع الموصى به السبب
الزحف الجماعي للبيانات العامة بروكسي مركز البيانات سرعة عالية، سعر منخفض
العمل مع الشبكات الاجتماعية والمنصات المحمية بروكسي سكنية عناوين IP حقيقية، خطر منخفض للحظر
محاكاة حركة مرور الموبايل بروكسي موبايل عناوين IP لمشغلي الموبايل
العمل مع بروتوكولات غير قياسية بروكسي SOCKS5 دعم أي حركة مرور

بالنسبة للمهام التي تتطلب مستوى عالٍ من الخصوصية وأقل خطر للحظر، يُوصى باستخدام بروكسي سكنية. للزحف السريع إلى كميات كبيرة من البيانات، يمكن استخدام بروكسي مركز البيانات.

7. أمان بيانات الاعتماد

لا تخزن أبدًا بيانات اعتماد البروكسي في الشيفرة. استخدم متغيرات البيئة:

// ملف .env
PROXY_HOST=proxy-server.com
PROXY_PORT=8080
PROXY_USERNAME=your-username
PROXY_PASSWORD=your-password

// في الشيفرة
require('dotenv').config();

const proxyUrl = `http://${process.env.PROXY_USERNAME}:${process.env.PROXY_PASSWORD}@${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`;

// أو لقائمة البروكسي
const proxyList = process.env.PROXY_LIST.split(',').map(proxy => proxy.trim());

الخاتمة

إعداد البروكسي في تطبيقات Node.js هو مهارة مهمة للمطورين الذين يعملون مع الزحف، والأتمتة، والتكامل مع واجهات برمجة التطبيقات الخارجية. في هذا الدليل، استعرضنا جميع الطرق الأساسية للعمل مع البروكسي: من الإعداد الأساسي عبر الوحدات المدمجة إلى التقنيات المتقدمة مع التدوير، ومعالجة الأخطاء، والمراقبة.

النقاط الرئيسية التي يجب تذكرها:

  • اختر مكتبة لطلبات HTTP بناءً على المهمة: Axios للمرونة، Got لمشاريع TypeScript، node-fetch للتوافق مع واجهة برمجة التطبيقات في المتصفح
  • استخدم بروكسي SOCKS5 للعمل مع بروتوكولات غير قياسية وأقصى مرونة
  • نفذ تدوير البروكسي لتوزيع الحمل وتجنب الحظر
  • قم بتنفيذ نظام معالجة الأخطاء مع إعادة المحاولات التلقائية والتبديل للبروكسي
  • راقب أداء البروكسي واستبعد التالفة تلقائيًا
  • أعد استخدام وكلاء HTTP واضبط مهلات معقولة
  • احفظ بيانات الاعتماد في متغيرات البيئة، وليس في الشيفرة

عند اختيار البروكسي لتطبيقات Node.js الخاصة بك، ضع في اعتبارك خصوصية المهمة. للزحف إلى منصات محمية والعمل مع واجهات برمجة التطبيقات الحساسة لسمعة IP، نوصي باستخدام بروكسي سكنية - فهي توفر مستوى عالٍ من الخصوصية وأقل خطر للحظر بفضل استخدام عناوين IP حقيقية لمستخدمي المنازل.

```