The Puppeteer Language Experiment – انجمن DEV

اگر از Puppeteer برای اتوماسیون، تست یا خراش وب استفاده می کنید، احتمالاً با این سوال مواجه شده اید که چگونه زبان مرورگر را تنظیم کنید. کنترل صریح زبان بسیار مهم است زیرا ممکن است زبان سیستم محلی شما با زبان سیستم راه دور دیگری که میخواهید Puppeteer را اجرا کنید، مانند CI (مثلاً GitHub Actions) یا یک محیط بدون سرور (مانند AWS Lambda یا CloudFlare Workers) متفاوت باشد. .
در کمال تعجب، نحوه انجام این کار به خوبی مستند نشده است. به نظر می رسد یک سری گزینه وجود دارد، و شما باید بفهمید که چه چیزی برای شما بهتر است. پس از صرف زمان قابل توجهی برای تحقیق، فهرستی از گزینه ها را تهیه کرده ام. من هر گزینه را در مقابل BrowserLeaks تأیید کردهام، که تمام اطلاعات موجود در مرورگر شما و ویژگیهای پشتیبانیشده آن، از جمله زبان محلی و زبان پذیرفتهشده را به شما نشان میدهد، که در نهایت محتوایی را که قرار است ببینید تعیین میکند.
چه زبانی؟
دو راه برای تعیین زبان درخواست کننده وجود دارد: سمت مشتری و سمت سرور. در سمت کلاینت، با جاوا اسکریپت، شما این را دارید navigator.language
و navigator.languages
خواص، و Intl.DateTimeFormat().resolvedOptions()
روش. مقادیر لزوماً با یکدیگر مطابقت ندارند زیرا navigator.language
به نظر می رسد از زبان تنظیمات کروم استفاده می کند، در حالی که Intl.DateTimeFormat().resolvedOptions()
منعکس کننده زبان سیستم عامل است. در سمت سرور، شما باید Accept-Language
هدری که با هر درخواست HTTP از مرورگر ارسال می شود. مرورگر از navigator.languages
ویژگی برای پر کردن مقادیر هدر.
نکته جانبی: من از این اصطلاح استفاده می کنم زبان برای مراجعه به قسمت اول
de
از یک محل پسندیدنde-DE
.
لازم به ذکر است که سرور می تواند تصمیم بگیرد که محتوای یک وب سایت را به زبان منعکس کننده HTTP بازگرداند Accept-Language
مقادیر هدر، یا می تواند از جاوا اسکریپت برای شناسایی زبان و هدایت کاربر به یک URL محلی استفاده کند (به این فکر کنید example.com/de-DE/
، یا می تواند به صورت پویا محتوای واقعی را از طریق جاوا اسکریپت بر اساس ویژگی ها از سرور بارگیری کند.
عروسک گردان را راه اندازی کنید
من از puppeteer
بسته، که به طور خودکار نسخه اخیر Chrome را برای آزمایش دانلود می کند (فکر می کنم قبلاً Chromium بوده است). نیز وجود دارد puppeteer-core
اگر می خواهید نصب مرورگر را خودتان مدیریت کنید یا اگر محیط از قبل یک مرورگر ارائه می دهد.
برای راه اندازی مرورگر، تنها کاری که باید انجام دهیم این است که با آن تماس بگیرید launch
بدون نیاز به هیچ آرگومان دیگری عمل کنید. Puppeteer همه پیش فرض ها را فراهم می کند.
import puppeteer from "puppeteer";
const LANG = 'de-DE';
const browser = await puppeteer.launch();
const page = await browser.newPage();
من ثابت را تعریف کردم LANG
به عنوان زبان مقصد برای مرورگر. در چند مرحله بعدی، نحوه اعمال این تنظیمات را به شما نشان خواهم داد.
آرگومان خط فرمان --lang
کروم (و کرومیوم) تعداد زیادی آرگومان خط فرمان را ارائه می دهد. بحث و جدل --lang
می توان از آن برای تنظیم زبان هنگام راه اندازی استفاده کرد. به خاطر داشته باشید که باید با آرگ های پیش فرض Puppeteer ادغام شود.
console.log(`Using --lang=${LANG}`);
const args = [...puppeteer.defaultArgs(), `--lang=${LANG}`];
const browser = await puppeteer.launch({
args,
});
const page = await browser.newPage();
متغیر محیطی LANG
در حالی که متغیر محیطی به طور رسمی مستند نشده است، ارجاعاتی به آن در ردیاب اشکال Chromium و مخزن Puppeteer وجود دارد. با این حال، به نظر می رسد که این فقط در لینوکس کار می کند. حتی بدتر از آن، تنظیم این متغیر محیطی باعث می شود که مرورگر در سیستم عامل های خاصی راه اندازی نشود.
console.log(`Using env.LANG=${LANG}`);
const browser = await puppeteer.launch({
env: {
LANG,
}
});
const page = await browser.newPage();
هدر HTTP Accept-Language
هدرهای HTTP اضافی مانند Accept-Language
را می توان در هر درخواست صفحه تنظیم کرد. این روی زبان مرورگر تأثیری نمیگذارد، اما وبسایت درخواستی در صورت رعایت این سرصفحه ممکن است محتوا را به زبان درخواستی بازگرداند.
console.log(`Sending HTTP header Accept-Language: ${LANG}`);
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setExtraHTTPHeaders({
'Accept-Language': LANG,
});
لغو navigator.language
نادیده گرفتن navigator.language
ویژگی موجود در یک صفحه ممکن است مانند یک هک به نظر برسد، اما در واقع یک نمونه رسمی در اسناد Puppeteer است.
console.log(`Overriding navigator.language=${LANG}`);
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.evaluateOnNewDocument((lang) => {
Object.defineProperty(navigator, 'language', {
get() {
return lang;
},
});
Object.defineProperty(navigator, 'languages', {
get() {
return [lang];
},
});
}, LANG);
پروتکل Chrome DevTools Network.setUserAgentOverride
Puppeteer از پروتکل Chrome DevTools Protocol (CDP) برای برقراری ارتباط با Chrome استفاده می کند. را Network.setUserAgentOverride
روش اجازه تنظیم را می دهد acceptLanguage
به عنوان زبان مرورگر برای تقلید. توجه داشته باشید که userAgent
یک پارامتر ضروری است، اما مقدار پیش فرض را حفظ می کنیم.
console.log(`Using CDP Network.setUserAgentOverride(acceptLanguage: ${LANG})`);
const browser = await puppeteer.launch();
const page = await browser.newPage();
const cdpSession = await page.createCDPSession();
cdpSession.send('Network.setUserAgentOverride', {
userAgent: await browser.userAgent(),
acceptLanguage: LANG,
});
نتایج
همه گزینه ها را به صورت متوالی اجرا کردم و مقادیر آن را استخراج کردم navigator.language
، navigator.languages
، Intl.DateTimeFormat().resolvedOptions()
از BrowserLeaks JS و Accept-Language
هدر از IP BrowserLeaks. در اینجا نتایج برای هر گزینه آمده است:
System: darwin/23.1.0, arm64
Browser: Chrome/125.0.6422.60
Using --lang=de-DE
Internationalization Locale: en-GB
Navigator Language: en-GB
HTTP Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Using env.LANG=de-DE
Internationalization Locale: en-GB
Navigator Language: en-GB
HTTP Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Sending HTTP header Accept-Language: de-DE
Internationalization Locale: en-GB
Navigator Language: en-GB
HTTP Accept-Language: de-DE
Overriding navigator.language=de-DE
Internationalization Locale: en-GB
Navigator Language: de-DE
HTTP Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Using CDP Network.setUserAgentOverride(acceptLanguage: de-DE)
Internationalization Locale: en-GB
Navigator Language: de-DE
HTTP Accept-Language: de-DE
همانطور که می بینید، نتایج به طور قابل توجهی متفاوت است. هیچ یک از گزینهها روی منطقه بازگشتی از آن تأثیری نداشت Intl.DateTimeFormat().resolvedOptions()
. با کمال تعجب، حتی --lang
این گزینه بر زبان مرورگر تأثیری نمیگذارد navigator.language
. این غیرمنتظره است، زیرا معتقدم در نسخههای قدیمیتر Chrome/Chromium یا در سیستمعاملهای مختلف، این پرچم رعایت میشود. با استفاده از Network.setUserAgentOverride
به نظر می رسد دستور از CDP مطمئن ترین راه برای کنترل زبان مرورگر باشد، علیرغم اینکه حداقل گزینه مستند است.
مشارکت
من این پروژه را به عنوان یک مجموعه آزمایشی تکرارپذیر اجرا کردم که می توان آن را به صورت محلی با حداقل تنظیمات اجرا کرد. اگر علاقه مند هستید، لطفاً مخزن را بررسی کنید و آزمایش ها را روی دستگاه محلی خود اجرا کنید. من کنجکاو هستم که ببینم آیا نتایج در سیستم عامل های مختلف یا نسخه های مرورگر متفاوت است یا خیر.
این پروژه نحوه تغییر زبان مرورگر را با Puppeteer آزمایش می کند. چندین گزینه را برای تنظیم زبان کروم پیادهسازی میکند و هر گزینه را در مقابل BrowserLeaks بررسی میکند تا ببیند چگونه بر ویژگیهای جاوا اسکریپت و هدرهای HTTP موجود توسط مرورگر تأثیر میگذارد. برای اطلاعات بیشتر، به مقاله من The Puppeteer Language Experiment در DEV.to مراجعه کنید.
استفاده
این مخزن را در دستگاه محلی خود کلون کنید و وابستگی ها را با npm نصب کنید. را puppeteer
بسته به طور خودکار Chrome را در یک پوشه موقت دانلود می کند.
npm install
سپس تست را شروع کنید:
npm test
تست هر گزینه را اجرا می کند و نتیجه آن را چاپ می کند:
System: darwin/23.1.0, arm64
Language: en-US
Browser: Chrome/125.0.6422.60
Using --lang=de-DE
Internationalization Locale: en-GB
Navigator Language: en-GB
HTTP Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Using env.LANG=de-DE
Internationalization Locale: en-GB
Navigator Language: en-GB
HTTP Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Sending HTTP header Accept-Language: de-DE
Internationalization Locale: en-GB
Navigator Language: en-GB
HTTP Accept-Language: de-DE
Overriding navigator.language=de-DE
Internationalization Locale: en-GB
Navigator
…