वापस ब्लॉग पर

PHP वेब स्क्रैपिंग: एक व्यावहारिक मार्गदर्शिका

Ava Wilson
Ava Wilson

Expert in Web Scraping Technologies

11-Aug-2026

TL;DR:

  • PHP हर बुनियादी स्क्रैपर की जरूरतों को पूरा करता है। ext-curl डेटा लाता है, DOMDocument और DOMXPath पार्स करता है, और कोई Composer पैकेज शामिल नहीं है। एक कामकाजी एक्सट्रैक्शन लगभग पंद्रह लाइनों का होता है।
  • एक डिफ़ॉल्ट php-cli इंस्टॉलेशन काफी नहीं है। एक साफ Ubuntu बॉक्स पर php-cli ने केवल json और libxml को उजागर किया; curl, dom और mbstring को प्रत्येक को अलग से स्थापित करना पड़ा। कोड लिखने से पहले जांचें, बाद में नहीं।
  • DOMDocument::loadHTML() आपके आउटपुट को चेतावनियों से भर देगा। असली पृष्ठ HTML5 हैं और पार्सर HTML4 की अपेक्षा करता है। लोड को libxml_use_internal_errors(true) में लपेटें, नहीं तो शोर एक विफलता जैसा लगेगा जब कुछ भी विफल नहीं हुआ।
  • Scrapeless यूनिवर्सल स्क्रैपिंग API एक JSON लिफाफा लौटाता है। मार्कअप data में होता है, इसलिए आप पहले डिकोड करें और फिर पार्स करें।
  • chrome-php एक रिमोट ब्राउज़र को संचालित करता है, लेकिन इसका डिफ़ॉल्ट टाइमआउट एक स्थानीय के लिए आकार में है। क्लाउड CDP अंत बिंदु के खिलाफ मापे जाने पर, पुस्तकालय का 5-सेकंड डिफ़ॉल्ट 6 में से 2 प्रयासों को पूरा करता है; sendSyncDefaultTimeout को बढ़ाने से यह 6 में से 5 हो गया।
  • मुफ्त शुरू करें: Scrapeless डैशबोर्ड एक कुंजी जारी करता है जो नीचे दिए गए हर उदाहरण के साथ काम करती है।

आपको क्या चाहिए

यहां हर उदाहरण PHP 8.3.6 (cli) पर चलाया गया था quotes.toscrape.com, एक साइट जो विशेष रूप से स्क्रैपिंग अभ्यास के लिए प्रकाशित की गई थी।

एक साफ मशीन पर पहली आश्चर्य यह है कि बेस PHP इंस्टॉलेशन में कितना कम शामिल है। एक ताजा इंस्टॉल किया गया php-cli केवल json और libxml की रिपोर्ट करता है जो यहां मायने रखते हैं। तीन जो आपको वास्तव में चाहिए अलग-अलग आते हैं:

bash Copy
# Ubuntu/Debian — php-cli alone is not enough
apt-get install -y php-cli php-curl php-xml php-mbstring

php -m | grep -E '^(curl|dom|libxml|mbstring)$'
# curl
# dom
# libxml
# mbstring

php-xml वह पैकेज है जो DOMDocument प्रदान करता है; एक्सटेंशन का नाम dom है, यही कारण है कि xml के लिए grep करने पर कुछ नहीं मिलता और लोग गोल-गोल घूमते हैं।

ext-curl के साथ एक पृष्ठ लाना

file_get_contents() तुच्छ मामलों के लिए काम करता है, लेकिन यह आपको कोई स्थिति कोड, कोई हेडर नियंत्रण और कोई मूल्यवान टाइमआउट नहीं देता। ext-curl किसी भी वास्तविकता के लिए मूलभूत है:

php Copy
<?php
$ch = curl_init('https://quotes.toscrape.com/');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_FOLLOWLOCATION => true,
    CURLOPT_TIMEOUT        => 30,
    CURLOPT_USERAGENT      => 'Mozilla/5.0 (compatible; php-guide/1.0)',
]);
$html   = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

echo "http={$status} bytes=" . strlen($html) . "\n";

यह http=200 bytes=11064 प्रिंट करता है। दो विकल्प अधिक वजन रखते हैं जितना वे दिखते हैं: CURLOPT_RETURNTRANSFER वह है जो curl_exec() को बॉडी वापस करने के लिए मजबूर करता है बजाय इसके कि उसे प्रिंट करे, और CURLOPT_USERAGENT के बिना कई साइटें बिना पते वाले PHP क्लाइंट का जवाब अलग तरीके से या बिल्कुल नहीं देती हैं।

DOMDocument और DOMXPath के साथ पार्सिंग

PHP का DOM एक्सटेंशन W3C DOM मानक का पूरा कार्यान्वयन है, और DOMXPath आपको किसी भी समर्पित स्क्रैपिंग लाइब्रेरी की तरह समान क्वेरी शक्ति देता है। जाल लोडर में है।

php Copy
<?php
$doc = new DOMDocument();
libxml_use_internal_errors(true);   // without this, every HTML5 tag warns
$doc->loadHTML($html);
libxml_clear_errors();

$xpath  = new DOMXPath($doc);
$quotes = $xpath->query("//div[@class='quote']");
echo "quotes=" . $quotes->length . "\n";

$first  = $quotes->item(0);
$text   = trim($xpath->query(".//span[@class='text']", $first)->item(0)->textContent);
$author = trim($xpath->query(".//small[@class='author']", $first)->item(0)->textContent);

echo "first_author={$author}\n";
echo "first_text=" . mb_substr($text, 0, 40) . "\n";

आउटपुट:

text Copy
quotes=10
first_author=Albert Einstein
first_text=“The world as we have created it is a pr

तीन चीजें बाहर खींचने लायक हैं।

libxml_use_internal_errors(true) व्यवहार में वैकल्पिक नहीं है। DOMDocument HTML4 पार्सिंग लागू करता है, इसलिए हर HTML5 तत्व और आधुनिक पृष्ठ पर बिना उद्धृत गुण एक चेतावनी उठाते हैं। कॉल छोड़ दें और एक सफल स्क्रैप अपने स्वयं के आउटपुट को पार्सर शोर के नीचे दफना देता है — PHP मैनुअल इस स्विच को नियंत्रित करने के लिए समर्थित तरीका बताता है

DOMXPath::query() के लिए दूसरा तर्क प्रश्न को एक नोड तक सीमित करता है। इसके बिना, .//span[@class='text'] पूरे दस्तावेज़ की खोज करता है और आपको हर पंक्ति के लिए पहले उद्धरण का पाठ मिलता है। वह एक तर्क प्रति-पंक्ति एक्सट्रैक्शन और एक सूक्ष्म रूप से गलत डेटा सेट के बीच का अंतर है।

mb_substr() के बजाय substr() मायने रखता है क्योंकि पृष्ठ कर्ली उद्धरण चिह्नों का उपयोग करता है। substr() बाइट्स पर कटता है और एक मल्टी-बाइट वर्ण को अमान्य UTF-8 में विभाजित कर देगा, जो कि ठीक वही भ्रष्टाचार है जो तीन चरणों के बाद एक डेटाबेस में प्रकट होता है।

जहाँ साधारण PHP खत्म होता है

ऊपर का स्क्रिप्ट काम करता है क्योंकि लक्षित सामग्री को सर्वर-साइड पर रेंडर करता है और इसकी परवाह नहीं करता कि कौन पूछ रहा है। दो चीजें इसे समाप्त करती हैं: सामग्री जो केवल JavaScript निष्पादित होने के बाद ही मौजूद है, और साइटें जो तय करती हैं कि एक बिना पते वाला HTTP क्लाइंट एक ब्राउज़र नहीं है। न तो PHP की समस्या है — कोई HTTP क्लाइंट किसी भी भाषा में इनमें से कोई भी समाधान नहीं करता है।

एक प्रॉक्सी पूल के माध्यम से अनुरोधों को रूट करना दूसरे मामले का एक हिस्सा संभालता है, और यदि आप एक फ्रेमवर्क के भीतर काम कर रहे हैं तो Laravel प्रॉक्सी इंटीग्रेशन गाइड इस लेख की तुलना में उस कॉन्फ़िगरेशन को अधिक गहराई में कवर करता है।

उस बिंदु पर दो वृद्धि हैं, और वे एक-दूसरे के बेहतर संस्करणों के बजाय अलग उपकरण हैं। जहां यह काम करता है वहां साधारण HTTP पथ रखें; यह एक बड़े अंतर से सबसे तेज और सबसे सस्ता विकल्प बना रहता है।

वृद्धि एक: यूनिवर्सल स्क्रैपिंग API

पहली वृद्धि आपके कोड को एक HTTP क्लाइंट के समान आकार में रखती है और कठिन भाग को सर्वर-साइड पर ले जाती है। अनुरोध साधारण ext-curl है; प्रतिक्रिया वह है जहाँ दृश्यता दिखती है।

php Copy
<?php
$key     = getenv('SCRAPELESS_API_KEY');
$payload = json_encode([
    'actor' => 'unlocker.webunlocker',
    'input' => ['url' => 'https://quotes.toscrape.com/', 'js_render' => false],
]);

$ch = curl_init('https://api.scrapeless.com/api/v2/unlocker/request');
curl_setopt_array($ch, [
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_POST           => true,
    CURLOPT_POSTFIELDS     => $payload,
    CURLOPT_TIMEOUT        => 90,
    CURLOPT_HTTPHEADER     => ['Content-Type: application/json', "x-api-token: {$key}"],
]);
$raw    = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);

$body = json_decode($raw, true);
echo "http={$status} envelope_keys=" . implode(',', array_keys($body)) . "\n";

$apiHtml = $body['data'];              // the markup lives here, not in $raw
echo "bytes=" . strlen($apiHtml) . "\n";
text Copy
http=200 envelope_keys=code,data
bytes=11064

लिफाफा वह चीज है जिसे आंतरिक बनाना है। $raw JSON है, और इसे DOMDocument पर पास करने से एक दस्तावेज़ उत्पन्न होता है जिसमें कोई मेल खाने वाले नोड्स और कोई त्रुटि नहीं होती है - चयनकर्ता बस शून्य पंक्तियाँ लौटाते हैं। डिकोड करें, data लें, फिर पार्स करें। उस अनव्रैप को एक सहायक फ़ंक्शन में रखें बजाय इसके कि json_decode(...)['data'] को कॉल साइट्स पर दोहराएँ।

बाइट काउंट को छोड़कर, पार्सिंग कोड अपरिवर्तित है। उसी DOMXPath ब्लॉक में $apiHtml को फीड करने से वही दस उद्धरण लौटते हैं, जो कि मुद्दा है: केवल फ़ेच बदलता है।

वृद्धि दो: PHP से एक वास्तविक ब्राउज़र

जब सामग्री वास्तव में JavaScript की आवश्यकता होती है, तो आपको एक ब्राउज़र की आवश्यकता होती है। chrome-php/chrome Chrome DevTools प्रोटोकॉल से बात करता है और स्थानीय Chrome को लॉन्च कर सकता है या WebSocket के माध्यम से एक दूरस्थ ब्राउज़र से जुड़ सकता है।

bash Copy
composer require chrome-php/chrome
# Using version ^1.16 for chrome-php/chrome  → v1.16.1
php Copy
<?php
require __DIR__ . '/vendor/autoload.php';

use HeadlessChromium\BrowserFactory;

$key = getenv('SCRAPELESS_API_KEY');
$uri = "wss://browser.scrapeless.com/api/v2/browser?token={$key}";

$browser = BrowserFactory::connectToBrowser($uri, [
    'sendSyncDefaultTimeout' => 60000,   // see below — the default is 5000
]);

$page = $browser->createPage();
$page->navigate('https://quotes.toscrape.com/')->waitForNavigation();

echo "title: "          . $page->evaluate('document.title')->getReturnValue() . "\n";
echo "quotes on page: " . $page->evaluate('document.querySelectorAll(".quote").length')->getReturnValue() . "\n";

$browser->close();

Output:

text Copy
title: Quotes to Scrape
quotes on page: 10

डिफ़ॉल्ट टाइमआउट एक स्थानीय ब्राउज़र के लिए आकार में है

वह sendSyncDefaultTimeout पंक्ति लेख का वह हिस्सा है जिसका महत्व है। chrome-php इसे 5000 मिलीसेकंड पर डिफ़ॉल्ट करता है, जो कि उसी मशीन पर चल रहे Chrome के लिए उदार और TLS कनेक्शन के पार के लिए सीमित है। छह रन के दो मेल खाने वाले सेट, एक ही स्क्रिप्ट, एक ही लक्ष्य, केवल उस विकल्प को बदलते हुए:

कॉन्फ़िगरेशन परिणाम विफलता मोड
पुस्तकालय डिफ़ॉल्ट (5000 ms) 2 सफल, 4 विफल हर विफलता OperationTimedOut: Operation timed out after 5s
sendSyncDefaultTimeout => 60000 5 सफल, 1 विफल एकल विफलता एक अलग त्रुटि है, कनेक्ट समय पर

इसमें दो निष्कर्ष निकलते हैं, और दूसरा वह है जिसे लोग छोड़ देते हैं।

टाइमआउट बढ़ाना आवश्यक है। डिफ़ॉल्ट पर छोड़ दिया गया, प्रयासों की अधिकांश संख्या उसी संदेश पर मर गई, और यह एक संदेश है जो भ्रामक है - यह एक टाइमआउट का नाम देता है, इसलिए यह पढ़ता है जैसे पृष्ठ या नेटवर्क धीमा है, जबकि जो वास्तव में समाप्त हुआ वह प्रोटोकॉल राउंड ट्रिप पर पुस्तकालय का अपना इंतजार था।

बढ़ाना भी पर्याप्त नहीं है। जो एक विफलता बची थी वह Cannot connect to the browser, make sure it was not closed थी, जो लगभग पांच सेकंड में उठी, जबकि कनेक्शन अभी भी स्थापित किया जा रहा था न कि किसी आदेश के दौरान। एक बड़े आदेश का टाइमआउट इस पर कोई प्रभाव नहीं डालता है। ब्राउज़र प्राप्त करने को नौकरी के डिज़ाइन में उसकी खुद की त्रुटिपूर्ण चरण के रूप में मानिए, जिसे एक बार जब आप एक पकड़ लेते हैं तो आप करते हैं, और $browser->close() को एक finally में रखें ताकि मध्य-नौकरी की विफलता कभी भी एक सत्र को stranded न करे।

एक निदान जो गलत था

उन टाइमआउट के लिए पहला सिद्धांतिक विचार था कि URI का क्वेरी स्ट्रिंग WebSocket हैंडशेक तक नहीं पहुंचा, ?token= को अपने साथ ले गया। इसके लिए वास्तविक证据 हैं: Protocol::validateSocketUri() केवल [$scheme, $host, $port] लौटाता है और पथ और क्वेरी को पूरी तरह से हटा देता है।

यह अभी भी गलत है। हैंडशेक कहीं और निर्मित होता है, Protocol::getRequestHandshake() द्वारा, जो एक अलग validateUri() को कॉल करता है जो पांच तत्व लौटाता है और अनुरोध पंक्ति से पहले स्पष्ट रूप से क्वेरी को फिर से जोड़ता है। टोकन आता है। विफलता विलंबता थी, और एक ही URI को विभिन्न गहराइयों में पार्स करने वाले दो कार्यों का एक संयोग है जो एक बग जैसा दिखता है।

यह एक उपयोगी अनुस्मारक है कि एक पुस्तकालय में जिसमें एक से अधिक URI पार्सर हैं, एक ऐसा खोजने का अर्थ यह नहीं है कि यह उस पथ पर है जो आपको महत्वपूर्ण है।

तीनों में से चुनना

दृष्टिकोण उपयोग कब करें लागत
ext-curl + DOMXPath सर्वर-रेन्डर किया गया HTML, उदार लक्ष्य सबसे कम; कोई आश्रितता नहीं
यूनिवर्सल स्क्रैपिंग एपीआई अवरुद्ध या चुनौती दी गई, कोई JS की आवश्यकता नहीं एक HTTP कॉल, अनव्रैप करने के लिए लिफाफा
chrome-php + स्क्रैपिंग ब्राउज़र सामग्री को JavaScript निष्पादन की आवश्यकता है सबसे अधिक; एक ब्राउज़र सत्र प्रति नौकरी

उस सूची को नीचे की ओर काम करें, ऊपर की ओर नहीं। अधिकांश पृष्ठ जो दिखाई देते हैं जैसे उन्हें एक ब्राउज़र की आवश्यकता है, वे वास्तव में <script> टैग में अपने डेटा को एम्बेड करते हैं, और एक DOMXPath क्वेरी के साथ json_decode() ब्राउज़र सत्र को हर धुरी पर मात देता है।

सारांश

PHP एक पूरी तरह से उचित स्क्रैपिंग भाषा है, और जिन भागों की लोग उम्मीद करते हैं कि वे गायब हैं वे मानक पुस्तकालय में हैं। ext-curl और DOMXPath सर्वर-रेन्डर किए गए पृष्ठों को पूरी तरह से कवर करते हैं, जबकि libxml_use_internal_errors(true) और query() को एक स्कोप्ड दूसरे तर्क के साथ काम कर रहे कोड को चुपचाप गलत कोड से अलग करने वाले दो विवरण हैं।

जब कोई लक्ष्य सहयोग करना बंद कर दे, तो जानबूझकर बढ़ोतरी करें। API पथ आपके कोड को एक HTTP क्लाइंट बनाता है और केवल यही मांग करता है कि आप एक लिफाफा अनव्रैप करें। ब्राउज़र पथ एक सत्र की लागत लगाता है और, यदि वह ब्राउज़र दूरस्थ है, तो एक विशिष्ट कॉन्फ़िगरेशन का एक टुकड़ा: chrome-php का पांच सेकंड का डिफ़ॉल्ट एक स्थानीय-Crom चूक है, और इसे बनाए रखने से यहाँ छह कनेक्शनों में से चार की कीमत बढ़ गई।

PHP से स्क्रैप करने के लिए तैयार?

एक कुंजी Scrapeless डैशबोर्ड पर बनाएँ और ext-curl उदाहरण को एक पृष्ठ पर चलाएँ जिसे आप पहले से एकत्र कर चुके हैं। यदि यह वह वापस करता है जिसकी आप उम्मीद करते हैं, तो आप समाप्त हैं - कोई निर्भरता नहीं, कोई ब्राउज़र नहीं। यूनिवर्सल स्क्रैपिंग एपीआई वहाँ है उन पृष्ठों के लिए जहाँ यह कार्य नहीं करता, और वर्तमान दरें मूल्य निर्धारण पृष्ठ पर हैं।

FAQ

प्र: क्या मुझे PHP के साथ स्क्रैप करने के लिए Composer की आवश्यकता है?

नहीं। ext-curl के साथ फेचिंग और DOMDocument और DOMXPath के साथ पार्सिंग के लिए एक मानक इंस्टॉलेशन में एक्सटेंशन से अधिक कुछ नहीं चाहिए। Composer केवल एक ब्राउज़र ड्राइवर जैसे chrome-php/chrome के लिए तस्वीर में आता है।

प्र: हर पृष्ठ पर DOMDocument चेतावनियाँ क्यों प्रिंट करता है?

क्योंकि यह HTML4 पार्सिंग को लागू करता है और आधुनिक पृष्ठ HTML5 हैं। चेतावनियाँ विफलताओं के बजाय सूचनाात्मक होती हैं। libxml_use_internal_errors(true) को loadHTML() से पहले कॉल करें और libxml_clear_errors() के बाद, और वास्तव में उन्हें देखने के लिए libxml_get_errors() की जांच करें।

प्र: जब एपीआई अनुरोध सफल होता है तो मेरी पार्सिंग कुछ क्यों नहीं लौटाती?

आप लगभग निश्चित रूप से लिफाफे को पार्स कर रहे हैं। यूनिवर्सल स्क्रैपिंग एपीआई JSON को मार्कअप के साथ data के अंदर लौटाता है, इसलिए DOMDocument JSON स्ट्रिंग प्राप्त करता है और बिना किसी त्रुटि उठाए मेल खाने वाले नोड नहीं पाता। प्रतिक्रिया को डिकोड करें और data पार्स करें।

प्र: मुझे क्लास एट्रिब्यूट के लिए कौन सा XPath उपयोग करना चाहिए?

//div[@class='quote'] केवल एक सटीक एट्रिब्यूट वैल्यू से मेल खाता है। एक तत्व जो कई कक्षाएँ लिए हुए है, उसके लिए //div[contains(concat(' ', normalize-space(@class), ' '), ' quote ')] का उपयोग करें, जो quote-footer के साथ मेल खाने से बचता है जैसे बिना किसी contains() का।

प्र: क्या मुझे बड़े स्क्रैपिंग प्रोजेक्ट के लिए PHP का उपयोग करना चाहिए?

फेच-एंड-पार्स कार्यों के लिए बड़े पैमाने पर, हाँ - curl_multi_* आपको वास्तविक समवर्ती प्रदान करता है और DOM एक्सटेंशन तेज है। जहाँ PHP कमजोर है वह लंबे समय तक चलने वाले ब्राउज़र ऑटोमेशन में है, जहाँ Playwright और Puppeteer के आसपास का उपकरण अधिक परिपक्व है। एक सामान्य विभाजन HTTP पथ के लिए PHP और उन पृष्ठों के लिए एक ब्राउज़र सेवा है जिन्हें वास्तव में एक की आवश्यकता है।

स्क्रैपलेस में, हम केवल सार्वजनिक रूप से उपलब्ध डेटा का उपयोग करते हैं, जबकि लागू कानूनों, विनियमों और वेबसाइट गोपनीयता नीतियों का सख्ती से अनुपालन करते हैं। इस ब्लॉग में सामग्री केवल प्रदर्शन उद्देश्यों के लिए है और इसमें कोई अवैध या उल्लंघन करने वाली गतिविधियों को शामिल नहीं किया गया है। हम इस ब्लॉग या तृतीय-पक्ष लिंक से जानकारी के उपयोग के लिए सभी देयता को कोई गारंटी नहीं देते हैं और सभी देयता का खुलासा करते हैं। किसी भी स्क्रैपिंग गतिविधियों में संलग्न होने से पहले, अपने कानूनी सलाहकार से परामर्श करें और लक्ष्य वेबसाइट की सेवा की शर्तों की समीक्षा करें या आवश्यक अनुमतियाँ प्राप्त करें।

सबसे लोकप्रिय लेख

सूची